首页 文章

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

提问于
浏览
239

我有一个父组件( 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 回答

  • 331

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

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

    });

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

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

    changeDetection: ChangeDetectionStrategy.OnPush
    
  • 0

    您还可以使用一个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
    });
    }
    
  • 1
  • 0
    @Input()
      public set categoryId(categoryId: number) {
          console.log(categoryId)
      }
    

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

  • 0

    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 .

  • 0

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

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

    这是Docs .

  • 5

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

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

    EDIT:

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

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

    最安全的选择是使用 @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);
      }
    }
    

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

  • 88

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

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

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

相关问题