如何检测Angular中@Input()值何时发生变化?

我有一个父组件( CategoryComponent ),一个子组件( videoListComponent )和一个ApiService .

我有大部分工作正常,即每个组件可以访问json api并通过observable获取其相关数据 .

目前视频列表组件只获取所有视频,我想将其过滤为特定类别中的视频,我通过 @Input() 将categoryId传递给孩子来实现这一点 .

CategoryComponent.html

<video-list *ngIf="category" [categoryId]="category.id"></video-list>

这有效,当父CategoryComponent类别更改时,categoryId值通过 @Input() 传递,但我需要在VideoListComponent中检测到这一点,并通过APIService(使用新的categoryId)重新请求视频数组 .

在AngularJS中,我会在变量上做 $watch . 处理这个问题的最佳方法是什么?

回答(9)

2 years ago

Actually, there are two ways of detecting and acting up on when an input changes in the child component in angular2+ :

  • 您可以使用旧答案中提到的 ngOnChanges() lifecycle method
@Input() categoryId: string;

    ngOnChanges(changes: SimpleChanges) {

        this.doSomething(changes.categoryId.currentValue);
        // You can also use categoryId.previousValue and 
        // categoryId.firstChange for comparing old and new values

    }

文档链接:ngOnChanges, SimpleChanges, SimpleChange
演示示例:查看this plunker

  • 或者,您也可以使用 input property setter ,如下所示:
private _categoryId: string;

    @Input() set categoryId(value: string) {

       this._categoryId = value;
       this.doSomething(this._categoryId);

    }

    get categoryId(): string {

        return this._categoryId;

    }

文档链接:看here .

演示示例:查看this plunker .

WHICH APPROACH SHOULD YOU USE?

如果您的组件有多个输入,那么,如果您使用ngOnChanges(),您将在ngOnChanges()中一次性获得所有输入的所有更改 . 使用此方法,您还可以比较已更改的输入的当前值和先前值,并相应地执行操作 .

但是,如果您只想在特定的单个输入发生更改时执行某些操作(并且您不关心其他输入),那么使用输入属性设置器可能会更简单 . 但是,此方法不提供内置方法来比较已更改输入的先前值和当前值(您可以使用ngOnChanges生命周期方法轻松完成) .

EDIT 2017-07-25: ANGULAR CHANGE DETECTION MAY STILL NOT FIRE UNDER SOME CIRCUMSTANCES

通常,只要父组件更改传递给子项的数据 provided that the data is a JS primitive datatype(string, number, boolean) ,就会触发setter和ngOnChanges的更改检测 . 但是,在以下情况下,它不会触发,您必须采取额外的操作才能使其正常工作 .

  • 如果您使用嵌套对象或数组(而不是JS原始数据类型)将数据从Parent传递给Child,则更改检测(使用setter或ngchanges)可能不会触发,如用户的答案中所述:muetzerich . 对于解决方案,请查看here .

  • 如果要改变角度上下文之外的数据(即外部),则angular将不知道更改 . 您可能必须在组件中使用ChangeDetectorRef或NgZone来使角度感知外部更改,从而触发更改检测 . 请参阅this .

2 years ago

在组件中使用 ngOnChanges() 生命周期方法 .

检查数据绑定属性后立即调用ngOnChanges,并在检查视图和内容子项之前调用它们,如果其中至少有一个已更改 .

这是Docs .

2 years ago

在函数签名中使用 SimpleChanges 类型时,我在控制台以及编译器和IDE中遇到错误 . 要防止出现错误,请在签名中使用 any 关键字 .

ngOnChanges(changes: any) {
    console.log(changes.myInput.currentValue);
}

EDIT:

正如Jon在下面指出的那样,当使用括号表示法而不是点符号时,可以使用 SimpleChanges 签名 .

ngOnChanges(changes: SimpleChanges) {
    console.log(changes['myInput'].currentValue);
}

2 years ago

最安全的选择是使用 @Input 参数的共享service instead . 此外, @Input 参数不会检测复杂嵌套对象类型中的更改 .

一个简单的示例服务如下:

Service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SyncService {

    private thread_id = new Subject<number>();
    thread_id$ = this.thread_id.asObservable();

    set_thread_id(thread_id: number) {
        this.thread_id.next(thread_id);
    }

}

Component.ts

export class ConsumerComponent implements OnInit {

  constructor(
    public sync: SyncService
  ) {
     this.sync.thread_id$.subscribe(thread_id => {
          **Process Value Updates Here**
      }
    }

  selectChat(thread_id: number) {  <--- How to update values
    this.sync.set_thread_id(thread_id);
  }
}

您可以在其他组件中使用类似的实现,并且您的所有组件将共享相同的共享值 .

2 years ago

我只想补充说,如果 @Input 值不是原始值,那么另一个名为 DoCheck 的Lifecycle钩子很有用 .

我有一个数组作为 Input 所以这不会在内容发生变化时触发 OnChanges 事件(因为Angular的检查是'simple'并且不深,所以Array仍然是一个数组,即使数组上的内容已经改变) .

然后,我实现了一些自定义检查代码,以决定是否要使用更改的Array更新我的视图 .

2 years ago

您还可以使用一个observable触发父组件(CategoryComponent)中的更改,并在子组件的订阅中执行您想要执行的操作 . (videoListComponent)

service.ts 
public categoryChange$ : ReplaySubject<any> = new ReplaySubject(1);

-----------------
CategoryComponent.ts
public onCategoryChange(): void {
  service.categoryChange$.next();
}

-----------------
videoListComponent.ts
public ngOnInit(): void {
  service.categoryChange$.subscribe(() => {
   // do your logic
});
}

2 years ago

如果您不想使用ngOnChange实现og onChange()方法,您还可以通过 valueChanges event ,ETC订阅特定项目的更改 .

myForm= new FormGroup({
first: new FormControl()

});

this.myForm.valueChanges.subscribe(formValue => {
  this.changeDetector.markForCheck();
});

markForCheck()因为在此声明中使用而写入:

changeDetection: ChangeDetectionStrategy.OnPush

2 years ago

@Input()
  public set categoryId(categoryId: number) {
      console.log(categoryId)
  }

请尝试使用此方法 . 希望这可以帮助