【问题标题】:Correct way of subscribing to valueChanges in Angular 2在 Angular 2 中订阅 valueChanges 的正确方法
【发布时间】:2017-01-16 12:04:26
【问题描述】:

我一直在 Angular 2 中摆弄响应式表单和 valueChanges 订阅。我不太明白为什么某些订阅形式似乎不允许。

this.form.get('name').valueChanges /* <- doesn't work */
  .do(changes => {
    console.log('name has changed:', changes)
    });
  .subscribe();

this.form.get('title').valueChanges.subscribe( /* <- does work */
  changes => console.log('title has changed:', changes)
);

This plunker 重现问题(打开 DevTools 控制台查看错误):

ZoneAwareError {stack:“错误:未捕获(承诺中):TypeError:无法se...g.com/zone.js@0.7.5/dist/zone.js:349:25)[]”,消息:“未捕获(承诺中):TypeError:无法在 Array.forEach(本机)处设置 prope…ore.umd.js:8486:93)↵”,originalStack:“错误:未捕获(承诺中):TypeError:无法检测…ps: //unpkg.com/zone.js@0.7.5/dist/zone.js:349:25)", zoneAwareStack: "错误:未捕获(承诺中):TypeError:无法查看...g.com/zone.js@ 0.7.5/dist/zone.js:349:25) []",名称:"错误"…}

第一个模式(do)确实不非法吗?

【问题讨论】:

  • 为什么do 是非法的?您不需要,您可以将回调传递给subscribe(...)。 Plunker 不为我运行(不知道为什么,有一段时间了,因此无法调查)
  • 这正是让我困惑的地方:为什么它是非法的? Plunkr 也不总是喜欢我 - 我正在更新我的帖子,但来自 DevTools 的错误。
  • 您的 Plunker 确实错过了 do 运算符的导入。另见stackoverflow.com/questions/34515173/…
  • 有点意思,但添加 import 'rxjs/add/operator/do'; 并没有帮助。
  • 很难说,我不知道如何在 Plunker 不工作的情况下进行调试。我没有在我的机器上使用 TypeScript。也许其他人有一个想法。

标签: angular rxjs observable


【解决方案1】:

其中一种方法是:

debounceTime 将在给定毫秒后进行订阅。如果不需要,请忽略。

distinctUntilChanged 将仅在实际值更改时强制订阅。

Angular 8+ 的更新

     this.form.controls['form_control_name']
                .valueChanges
                .pipe(
                    .debounceTime(MilliSeconds)
                    .distinctUntilChanged()
                )
                .subscribe({
                    next: (value) => {
                        //use value here
                    }
                });

【讨论】:

    【解决方案2】:

    这让你的 plunker 工作了:

    1. 在 app.ts 中添加此导入语句
      import 'rxjs/add/operator/do';
    
    1. 删除 .do 语句后的分号

    .do(更改 => { console.log('名称已更改:',更改) })

    完成更改的 app.ts

    import {Component, NgModule, OnInit} from '@angular/core'
    import {BrowserModule } from '@angular/platform-browser'
    import { FormGroup, FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
    import { Observable } from 'rxjs/Observable';
    import { Subscription } from 'rxjs/Subscription';
    import 'rxjs/add/operator/do';
    
    @Component({
      selector: 'my-app',
      template: `
        <form novalidate [formGroup]="form">
          <div>
            <select type="text" class="form-control" name="title" formControlName="title">
              <option *ngFor="let title of titles" value="{{title}}">{{title}}</option>
            </select>
          </div>
          <div>
            <input type="text" class="form-control" name="name" formControlName="name">
          </div>
          <fieldset formGroupName="address">
            <legend>Address</legend>
            <input type="text" class="form-control" name="street" formControlName="street">
            <input type="text" class="form-control" name="city" formControlName="city">
          </fieldset>
        </form>
      `,
    })
    export class App implements OnInit {
      private form: FormGroup;
      private titles: string[] =  ['Mr', 'Mrs', 'Ms'];
    
      constructor(private fb: FormBuilder){
    
      }
      ngOnInit() {
        this.form = this.fb.group({
          title: 'Mr',
          name: '',
          address: this.fb.group({
            street: '',
            city: ''
          })
        });
    
        this.form.get('name').valueChanges
          .do(changes => {
            console.log('name has changed:', changes)
            })
          .subscribe();
    
        this.form.get('title').valueChanges.subscribe(
          changes => console.log('title has changed:', changes)
        );
    
        this.form.get('address').valueChanges.subscribe(
          changes => console.log('address has changed:', changes)
        );  
      }
    }
    
    @NgModule({
      imports: [ BrowserModule, FormsModule, ReactiveFormsModule ],
      declarations: [ App ],
      bootstrap: [ App ]
    })
    export class AppModule {}
    

    另一种解决方法是使用“ngModelChange”事件

    在您的模板中更改:

     <input type="text" class="form-control" (ngModelChange)="doNameChange($event)" name="name" formControlName="name">
    

    在您处理的组件中,然后处理您的更改事件:

    doNameChange(event: any){
       alert("change")
    }
    

    【讨论】:

    • 谢谢,但正如您所见,我确实知道处理更改的有效方法。问题在于为什么一个合法的模式似乎不起作用。
    • 为什么要使用“do”?如果您将功能从“做”移动到订阅块它正在工作
    • @user776686,你的模式没有错。
    • 我支持它,在 do() 和 subscribe() 之间的可观察链中间有一个分号。这是代码语法错误。
    【解决方案3】:

    我也遇到了这个问题。

    我想在我的情况下做 debounceTime,所以它没有解决我的需求。

    我居然忘记订阅了!

    但我也能够通过这种方式获得值更改来激活:

    this.form.get('name').valueChanges.forEach(
        (value) => {console.log(value);} )
    

    来自 Angular 文档中的此处:hero-detail-component.ts

    【讨论】:

      【解决方案4】:

      您可以订阅整个表单,然后选择要“获取”的内容……此外,不要忘记使用生命周期挂钩跟踪订阅和取消订阅。此外,请等到视图加载后才能订阅更改。这种模式效果很好。

      private DEBOUNCE_TIME_FORM_INPUT = 250
      
      private ngUnsubscribe: Subscription = new Subscription();
      
      constructor(private fb: FormBuilder) {} 
      
      ngOnInit(): void {
        this.initForm();
      }
      
      ngAfterViewInit(): void {
        this.subscribeToFilters();
      }
      
      ngOnDestroy(): void { // Don't forget to close possible memory leak
        this.ngUnsubscribe.unsubscribe();
      } 
      
      private initForm(): void {
        this.form = this.fb.group({
          name: ['', []],  
        });
      }
      
      private subscribeToFilters(): void {
        this.ngUnsubscribe.add(
          this.form.valueChanges
            .pipe(debounceTime(this.DEBOUNCE_TIME_FORM_INPUT)) // Debounce time optional
            .subscribe((form): void => {
              const { name } = form
              console.log('Name', name);
          }))
        )
      } 
      
      get name(): AbstractControl { return this.form.get(['name']); }
      

      【讨论】:

        猜你喜欢
        • 2017-05-12
        • 2023-01-04
        • 2017-10-06
        • 1970-01-01
        • 2021-07-22
        • 1970-01-01
        • 2017-03-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多