【问题标题】:valueChanges observable seems to be subscribing to data before subscriptionvalueChanges observable 似乎在订阅前订阅数据
【发布时间】:2018-01-16 00:47:25
【问题描述】:

我正在创建一个动态创建的响应式表单。我以可观察的形式(来自主题)从服务获取数据,并希望在创建表单后订阅表单的valueChanges。当我第一次设置它时,我订阅了每个服务的 observables 和 valueChanges,但是我看到了表单创建的每一步的数据,这是有道理的,因为这两个服务 observables 可以在之后发出。

所以我重构了设置以使用Observable.zip,据我所知,这使代码线性化,所以应该在我订阅valueChanges之前设置表单,但我仍然看到每一步正在创建表单。

this.filterForm = this.formBuilder.group({
    'search': '',
    'types': this.formBuilder.group({}),
    'dates': this.formBuilder.array([]),
});

let formData = Observable.zip(
    this.eventService.getTypes(),
    this.eventService.getDates()
);
formData.forEach(data => {
    let types = data[0],
        dates = data[1];
    types.forEach(type => {
        this.types.push(type);
        (<FormGroup>(this.filterForm.get('types'))).addControl(type, new FormControl())
    });
    dates.forEach(date => {
        this.dates.push(date.format('dddd'));
        (<FormArray>(this.filterForm.get('dates'))).push(new FormControl(false))
    });
    this.filterForm.valueChanges.subscribe(data => console.log(data));
});

当我说我看到表单正在构建的值时,我的意思是:

{search: "", types: {…}, dates: Array(0)}
{search: "", types: {…}, dates: Array(1)}
{search: "", types: {…}, dates: Array(2)}
{search: "", types: {…}, dates: Array(3)}
{search: "", types: {…}, dates: Array(4)}
{search: "", types: {…}, dates: Array(5)}

随着类型字段的构建,它前面有十几个条目。

这应该是这样工作的吗?我的目标是在表单构建后订阅,所以我只在用户更改表单时获取值。

【问题讨论】:

  • 您可以尝试在afterViewInit 生命周期内订阅
  • 为什么是afterViewInit?这与视图没有任何关系;在我订阅之前,订阅不应该给我价值吗?
  • 您在与formBuilder 相同的级别上运行订阅。这意味着您在 filterForm 初始化时订阅了它,并且可能在它完成构建之前订阅(因此订阅的值在开始时将为空)。由于正在构建的表单是构建视图的一部分,因此在视图初始化后订阅应该是安全的。希望这会有所帮助!
  • 我认为这意味着当我点击addControl 之类的方法时,不会立即构建表单?因为唯一有意义的方法是表单构建异步,允许订阅在表单初始化之前进行。我将移动代码以使其正常工作,但我仍然不明白它为什么会这样:/
  • 正确,都是异步的

标签: angular rxjs angular-reactive-forms


【解决方案1】:

我有一点不同的建议。在 RxJS 中,除了订阅链的 subscribe() 方法之外,还有内部订阅的 toPromise()forEach()。见https://github.com/ReactiveX/rxjs/blob/master/src/internal/Observable.ts#L223

我认为这就是发生在你身上的事情。 forEach 方法订阅 formData Observable 并为每个发出的值调用其回调。但是您还在回调中使用了this.filterForm.valueChanges.subscribe,这意味着您每次formData 发出一个值时都会创建一个新订阅。

我不知道您的代码应该做什么,但我认为您可以将其重组为如下所示:

Observable.zip(
    this.eventService.getTypes(),
    this.eventService.getDates()
  )
  .do(data => {
    let types = data[0],
        dates = data[1];
    types.forEach(type => {
        this.types.push(type);
        (<FormGroup>(this.filterForm.get('types'))).addControl(type, new FormControl())
    });
    dates.forEach(date => {
        this.dates.push(date.format('dddd'));
        (<FormArray>(this.filterForm.get('dates'))).push(new FormControl(false))
    });
  })
  .switchMap(() => this.filterForm.valueChanges))
  .subscribe(data => console.log(data));

【讨论】:

  • 我真的很喜欢这个想法,但是根据 VSCode,do 在从 zip 返回时不存在?仅限dowhile
  • 在 RxJS 5.4 中你需要使用 import 'rxjs/add/operator/do' 导入它,或者在 RxJS 5.5 中它被重命名为 tap
  • tap 被重命名为 lettable 版本,但 do 的流畅版本仍然存在。 rxjs/src/add/operator/do.ts
  • 奇怪的是,它没有让我按照您的建议导入,但通过水龙头的管道似乎已经解决了这个问题。担心zipforEach 引起了问题,如答案中所述,我尝试了forkJoin 的解决方案,并遇到了同样的问题。仍然不确定我是否理解正在发生的事情,这让我很困扰,但我猜它现在可以工作了:/
【解决方案2】:

不要订阅 OnInit 生命周期挂钩。创建一个处理订阅的方法,并在 OnInit 中声明表单模型后调用该方法。

更一般地说,尽管您可以使用 skip() 运算符跳过特定数量的发射,或者使用过滤器/映射仅在满足特定条件/结构后处理发射。

【讨论】:

  • 即使我将订阅移动到不同的方法,这确实有意义,它与上面的代码没有相同的效果吗?如果它是同步的,它仍然会在那个时候触发。我想我很困惑,为什么在表单构建之后的订阅向我展示了表单的构建阶段。
  • 当你说“正在构建的表单的阶段”时具体发出了什么
  • 我在问题中添加了控制台输出。
猜你喜欢
  • 2017-01-01
  • 2019-05-05
  • 2021-07-22
  • 2019-12-29
  • 1970-01-01
  • 2023-01-04
  • 1970-01-01
  • 2016-12-30
  • 2018-03-13
相关资源
最近更新 更多