首页 > 解决方案 > @ngrx/data - 撤消乐观删除,UNDO_ONE 操作是否应该恢复 changeState?

问题描述

堆栈闪电战

在@ngrx/data 中进行乐观删除(我们的英雄“蚁人”)会导致 changeState 被更新,如下所示:

{
  "entityCache": {
    "Hero": {
      "ids": [1, 2, 3, 5, 6],
      "entities": {
        "1": {
          "id": 1,
          "name": "Spiderman",
          "power": 1
        },
        "2": {
          "id": 2,
          "name": "Thor",
          "power": 5
        },
        "3": {
          "id": 3,
          "name": "Hulk",
          "power": 6
        },
        "5": {
          "id": 5,
          "name": "Iron Man",
          "power": 9
        },
        "6": {
          "id": 6,
          "name": "Thanos",
          "power": 10
        }
      },
      "entityName": "Hero",
      "filter": "",
      "loaded": true,
      "loading": true,
      "changeState": {
        "4": {
          "changeType": 2,
          "originalValue": {
            "id": 4,
            "name": "Ant-Man",
            "power": 7
          }
        }
      }
    }
  }
}

使用下面的效果,当删除由于 http 请求错误而失败时,我触发了 UNDO_ONE:

  deleteError$ = createEffect(() => {
    return this.actions$.pipe(
      ofEntityType("Hero"),
      ofEntityOp([EntityOp.SAVE_DELETE_ONE_ERROR]),
      map(action => {
        const id = action.payload.data.originalAction.payload.data;
        const options: EntityActionOptions = {
            // tried various values
        }
        return new EntityActionFactory().create( <-----------------------dispatch UNDO_ONE action-----------
          "Hero",
          EntityOp.UNDO_ONE,
          id,
          options
        );
      })
    );
  });

问题:调度 UNDO_ONE 操作是否应该恢复changeState
即删除由删除操作引起的实体状态这部分的更改?
如果是这样,您如何正确调度 UNDO_ONE 以及需要哪些参数?
我探索了EntityActionFactory.create() 方法的数据和选项的不同值:

EntityActionFactory.create<P = any>(entityName: string, entityOp: EntityOp, data?: P, options?: EntityActionOptions): EntityAction<P>

在这里,我正在执行一个乐观删除,并在 SAVE_DELETE_ONE_ERROR 上通过效果调度 UNDO_ONE 操作。

当我将 UNDO_ONE 换成 UNDO_ALL 时,changeState确实会恢复到{}这让我有理由认为changeState应该恢复到{}因为我们正在取消删除。

标签: javascriptangularngrxngrx-effectsangular-ngrx-data

解决方案


根据此处的文档,它应该:

撤消操作根据 changeState 映射中的信息替换集合中的实体,将它们恢复为最后一个已知的服务器端状态,并将它们从 changeState 映射中删除。这些实体变得“不变”。

为了克服这个问题,您可以创建一个 metaReducer,它在撤消操作后删除 changeState 中剩余的相关修改。这是我的 entity-metadata.ts 的内容以及相关的 metareduce。

import { EntityMetadataMap, EntityDataModuleConfig, EntityCache } from '@ngrx/data';
import { MetaReducer, ActionReducer, Action } from '@ngrx/store';

const entityMetadata: EntityMetadataMap = {};

const pluralNames = {};

const objectWithoutProperties = (obj, keys) => {
  const target = {};
  for (const i in obj) {
    if (keys.indexOf(i) >= 0) { continue; }
    if (!Object.prototype.hasOwnProperty.call(obj, i)) { continue; }
    target[i] = obj[i];
  }
  return target;
};

function revertStateChanges(reducer: ActionReducer<any>): ActionReducer<any> {
  return (state, action: any) => {
    if (action.type.includes('@ngrx/data/undo-one')) {
      
      //  Note that you need to execute the reducer first if you have an effect to add back a failed removal
      state = reducer(state, action);

      const updatedChangeState = objectWithoutProperties(state[action.payload.entityName].changeState, [action.payload.data.toString()]);
      const updatedState = {
        ...state,
        [action.payload.entityName]: {
          ...state[action.payload.entityName],
          changeState: updatedChangeState
        }
      };

      return reducer(updatedState, action);
    }

    return reducer(state, action);
  };
}

const entityCacheMetaReducers: MetaReducer<EntityCache, Action>[] = [revertStateChanges];

export const entityConfig: EntityDataModuleConfig = {
  entityMetadata,
  pluralNames,
  entityCacheMetaReducers
};

编写此代码可能有更好的方法(特别是我处理覆盖 changeState 属性的方式),但对于我的情况,它证明是有效的。

此外,它可能需要一些更新以处理不同的撤消情况,因为当我编写它时,我只需要使其适用于有关删除操作的撤消,其中 action.payload.data 是实体 ID。


推荐阅读