【问题标题】:angular + redux/ngrx: state update vs. formsangular + redux/ngrx:状态更新与表单
【发布时间】:2017-08-30 05:38:26
【问题描述】:

首先是一些上下文,我正在使用:

现在类似于react real world example,我有一个专用于实体的状态切片,这实际上是 normalizr 的 denormalize() 方法工作的要求,因为文章可以有一个 authormedia 可以都是实体本身。

这意味着,例如,当我从我的状态中选择一个 user 时,我正在抓取这个全局实体切片,我的选择器看起来像这样

export const getOne = createSelector(
  getAllEntities,
  getDetailId,
  (entities, id) => denormalize(id, schema, entities)
);

然后从ngrx/store中“选择”

state.map(getOne).distinctUntilChanged()

现在要填写我的表格,我这样做(半伪代码)

class Cmp {
  form = new FormGroup({ /* whatever */});
  user$ = this._store.map(getOne).distinctUntilChanged();

  constructor(private _store: Store<AppState>) {
    this.user$.subscribe((data) => {
      this.form.patchValue(data);
    });
  }
}

结合 reselect 和 map/distincUntilChanged 我几乎可以在任何时候任何实体更改时获得新的更新,因此请考虑这种情况..

  • 您访问了 id 5 的文章,其中填写了对由全局实体 slice 组成的 store slice 的订阅
  • 你开始编辑“textFormControl
  • 其他人更改了 id 为 3 的用户,而您通过 websockets 收到更新,导致subscribe 触发(因为全局实体切片已更改)并覆盖您在编辑时更改的任何值,当前值位于存储(旧值)

..这个 websocket 有很多不同的情况会导致更新更新。

redux 世界中是否有任何模式可以解决这个问题,或者 ngrx 世界中是否有人必须处理这个问题?我想到的唯一明智的事情是在您键入时将任何价值保存到商店,这将需要挂钩角度反应形式来存储,这是一个巨大的痛苦,而且ngrx/forms 不会很快到来。但我敢肯定,一定有人已经想出了一些简单的解决方案。

谢谢!

(你可以阅读gitter的后续讨论)

【问题讨论】:

  • denormalize 不是直接访问实体,而不是通过reselect-created 选择器访问实体的问题吗?如果您要使用选择器以更手动的方式进行非规范化,那会解决问题吗?虽然,您显然会失去基于模式的细节。
  • 可行的方法是为表单所需的值创建一个额外的选择器(通常是没有关系的原语,即文本),但在某些情况下这仍然存在问题, IE。 (下一条评论)
  • 想象你有一个用户的名字和年龄,你至少可以有两种情况 - 你正在编辑名字,没有触及年龄,其他人更新同一个用户并改变年龄..因为你没有'实际上并没有改变它,你希望它更新,否则一旦你在更改名称后按保存,更新的年龄将被更改回更新前的任何值 - 但如果你同时更改了年龄和名称(并且你仍在编辑名称),您不希望将年龄更改为其他人将其更改为的任何值,因为您故意进行了更改并且您希望它是最新值
  • 我认为你有两个问题:非规范化问题;和并发编辑问题。使用选择器而不是非规范化可能会解决第一个问题。并且关闭焦点上的输入/属性更新(并在保存后重新打开)可能会解决第二个问题。您可能可以使用filter 来打开/关闭每个输入/属性更新。需要考虑的事情。
  • 我认为焦点不够,您可以将多个输入分组在一个保存按钮下.. 但我在想的是实现一个自定义 patchValue 函数,只能更新pristine 表单控件,假设FormControl 上的pristine 执行我认为的操作。

标签: angular redux rxjs angular2-forms ngrx


【解决方案1】:

考虑将表单的状态与规范化实体分开存储。例如,您的状态可能包含诸如 articleEditForm 或类似的属性。基于从您的路由器状态订阅以获取文章的 id,combineLatest 与商店的当前状态一起调度带有文章有效负载的“EDIT_ARTICLE”类型的操作。然后,reducer 将使用文章状态有效负载在您的商店状态上设置 articleEditForm 属性。原始规范化实体仅在成功提交表单后才会更新 - 可能使用来自 articleEditForm 的值或表单提交到的服务的响应。这种方法的优点是在你的 redux 存储中拥有表单的状态,用于更复杂的显示和验证场景。这些值可以在每次击键、输入更改、表单提交或任何满足您的用例需求的任何情况下在 redux 中更新。

这对于 80% 的简单表单来说是多余的 - 可以选择直接从存储中的规范化实体填充表单(可能使用 .take(1) 或使用路由器状态和 combineLatest 与存储来管理并发问题) .然而,即使只是使用普通表单,状态仍然与商店中的规范化实体分开管理 - 这是 IMO 问题的准确模型。

【讨论】:

    【解决方案2】:

    到目前为止,我们正在通过在输入模糊时更新存储来解决这个问题(因此不是在每次击键时)。

    class Cmp {
        form = new FormGroup({ /* whatever */});
    
        constructor(private _store: Store<AppState>) {
            this._store.select(getOne)
                       .take(1) // we don't listen to changes
                       .subscribe((data) => {
                           this.form.patchValue(data);
                       });
        }
    
        onInputBlur() {
            this._store.dispatch({ type: 'UPDATE_USER', payload: this.form.value });
        }
    }
    

    然后我们将 onBlur 事件添加到我们的输入中

    <input (blur)="onInputBlur()" formControlName="firstname">
    

    【讨论】:

    • 您应该在这里使用change 而不是blurchange 仅在值发生变化时才会在输入模糊时触发
    • 是的,但是change 在每次击键时都会触发,这不是我想要的。
    • 不是,是input。除非我记错了,changeblur 非常相似,但 change 只有在值发生变化时才会被触发。
    猜你喜欢
    • 2022-06-18
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 2018-02-11
    相关资源
    最近更新 更多