valueChanges可观察对象似乎在订阅之前就订阅了数据。

3
我正在创建一个动态生成的响应式表单。我从一个Observable(来自Subject)的服务中获取数据,并想要在创建后订阅表单的valueChanges。当我第一次设置它时,我订阅了每个服务的可观察对象和valueChanges,但我看到表单创建的每个步骤的数据,这是有道理的,因为两个服务可观察对象可能会发出信号。

所以我重构了设置,使用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)}

在types字段构建之前有十几个条目。

这是正常的工作方式吗?我的目标是在表单构建之后订阅,以便只在用户更改表单时获取值。


你可以尝试在 afterViewInit 生命周期期间进行订阅。 - Z. Bagley
为什么要使用 afterViewInit?这与视图还没有任何关系;在订阅之前,订阅不应该给我任何值吗? - Rohit
1
你正在与 formBuilder 相同的级别上运行订阅。这意味着你正在订阅 filterForm,而此时它正在初始化过程中,很可能在构建完成之前(因此订阅的值将为空)。由于正在构建的表单是视图构建的一部分,因此在视图初始化后订阅应该是安全的。希望这可以帮助你! - Z. Bagley
1
正确,这都是异步的。 - Z. Bagley
这对你有用吗? - Hacker
显示剩余2条评论
2个回答

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 - Rohit
在RxJS 5.4中,你需要使用import 'rxjs/add/operator/do'导入它。而在RxJS 5.5中,它被重命名为tap - martin
tap 已经被重命名为可赋值版本,但 do 的流畅版本仍然存在。rxjs/src/add/operator/do.ts - Richard Matsen
奇怪的是,它不让我像你建议的那样导入“do”,但通过tap进行管道传输似乎解决了问题。由于答案中提到了“zip”和“forEach”可能会引起问题,我尝试了使用“forkJoin”的解决方案,并遇到了同样的问题。仍然不确定我理解发生了什么,这让我感到困扰,但现在它可以工作了 :/ - Rohit

0
不要在OnInit生命周期钩子中订阅。创建一个处理订阅的方法,并在OnInit中声明表单模型后调用该方法。
更一般地说,您可以使用skip()运算符跳过特定数量的发射,或使用filter / mapping仅在满足特定条件/结构之后处理发射。

即使我将订阅移动到另一个方法,这是有意义的,它是否具有与上面代码相同的效果?如果它是同步的,它仍然会在那个时间触发。我想我困惑的是为什么订阅在表单构建之后,却向我展示了表单构建的阶段。 - Rohit
当你说“表单构建的阶段”时,具体发出了什么? - Nikola Jankovic
我已经添加了控制台输出到问题中。 - Rohit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接