@Input () 값이 Angular에서 언제 변경되는지 감지하는 방법은 무엇입니까?


470

부모 구성 요소 ( CategoryComponent ), 자식 구성 요소 ( videoListComponent ) 및 ApiService가 있습니다.

나는이 작업을 대부분 잘 수행하고 있습니다. 즉, 각 구성 요소가 json API에 액세스하고 관찰 가능 항목을 통해 관련 데이터를 얻을 수 있습니다.

현재 비디오 목록 구성 요소는 모든 비디오를 가져옵니다.이를 특정 카테고리의 비디오로만 필터링하고 싶습니다 @Input().

CategoryComponent.html

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

이것은 작동하며 부모 CategoryComponent 범주가 변경되면 categoryId 값이 전달 @Input()되지만 VideoListComponent에서이를 감지하고 APIService를 통해 비디오 배열을 다시 요청해야합니다 (새 categoryId 포함).

AngularJS $watch에서 변수에 대해 수행했을 것 입니다. 이것을 처리하는 가장 좋은 방법은 무엇입니까?




답변:


720

실제로 angular2 +에서 하위 구성 요소의 입력이 변경 될 때이를 감지하고 작동하는 두 가지 방법이 있습니다.

  1. 이전 답변에서 언급 한 것처럼 ngOnChanges () 수명주기 메소드 를 사용할 수 있습니다 .
    @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
Demo 예 : 이 플런저를보십시오

  1. 또는 다음과 같이 입력 특성 설정기를 사용할 수도 있습니다 .
    private _categoryId: string;

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

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

    }

    get categoryId(): string {

        return this._categoryId;

    }

설명서 링크 : 여기를 참조하십시오 .

데모 예 : 이 플 런커를보십시오 .

어떤 접근법을 사용해야합니까?

컴포넌트에 여러 개의 입력이있는 경우 ngOnChanges ()를 사용하면 ngOnChanges () 내에서 모든 입력에 대한 모든 변경 사항을 한 번에 가져옵니다. 이 방법을 사용하면 변경된 입력의 현재 값과 이전 값을 비교하고 그에 따라 조치를 취할 수 있습니다.

그러나 특정 단일 입력 만 변경하고 다른 입력에 신경 쓰지 않을 때 무언가를 수행하려는 경우 입력 속성 설정기를 사용하는 것이 더 간단 할 수 있습니다. 그러나이 방법은 변경된 입력의 이전 값과 현재 값을 비교할 수있는 기본 방법을 제공하지 않습니다 (ngOnChanges 수명주기 방법으로 쉽게 수행 할 수 있음).

2017-07-25 편집 : 각도 변경 감지로 인해 일부 상황에서 여전히 화재가 발생하지 않을 수 있음

일반적으로 setter 및 ngOnChanges 모두에 대한 변경 감지 는 데이터가 JS 기본 데이터 유형 (string, number, boolean) 이면 상위 구성 요소가 하위 컴포넌트로 전달하는 데이터를 변경할 때마다 실행됩니다 . 그러나 다음 시나리오에서는 실행되지 않으며 작동하려면 추가 조치를 취해야합니다.

  1. JS 프리미티브 데이터 유형 대신 중첩 객체 또는 배열을 사용하여 부모에서 자식으로 데이터를 전달하는 경우 사용자의 답변에서 언급했듯이 변경 감지 (setter 또는 ngchanges 사용)가 발생하지 않을 수 있습니다 (muetzerich). 해결책은 여기를 참조하십시오 .

  2. 각도 컨텍스트 외부에서 데이터를 변경하는 경우 (즉, 외부에서) 각도는 변경 사항을 알 수 없습니다. 외부 변경을 각도로 인식하여 변경 감지를 트리거하려면 구성 요소에서 ChangeDetectorRef 또는 NgZone을 사용해야 할 수도 있습니다. 이것을 참조하십시오 .


8
입력과 함께 setter를 사용하는 것과 관련하여 매우 좋은 점은 더 포괄적이므로 허용되는 답변으로 업데이트하겠습니다. 감사.
Jon Catmull

2
@trichetriche, 부모가 입력을 변경할 때마다 setter (set 메소드)가 호출됩니다. 또한 ngOnChanges ()도 마찬가지입니다.
Alan CS

글쎄요, 분명히 ... 나는 이것을 알아 내려고 노력할 것입니다. 아마 내가 잘못한 것일 수도 있습니다.

1
방금이 질문에 걸려 넘어졌습니다. 세터를 사용하면 값을 비교할 수 없다고 말했지만 사실이 아닙니다. doSomething실제로 새 값을 설정하기 전에 메소드를 호출 하고 새로운 값과 기존 값을 2 개의 인수로 취할 수 있습니다. 설정하고 이전에 메소드를 호출하기 전에 이전 값을 저장합니다.
T.Aoukar

1
@ T.Aoukar 내가 말하는 것은 입력 세터가 기본적으로 ngOnChanges ()와 같은 오래된 / 새로운 값 비교를 지원하지 않는다는 것입니다. 해킹을 사용하여 이전 값을 저장하고 새 값과 비교할 수 있습니다.
Alan CS

105

ngOnChanges()컴포넌트에서 라이프 사이클 방법을 사용하십시오 .

ngOnChanges는 데이터 바인딩 된 속성을 확인한 직후와 뷰 및 내용 자식 중 하나 이상이 변경되었는지 확인하기 전에 호출됩니다.

다음은 문서 입니다.


6
예를 들어 변수가 새로운 값으로 설정되었을 때 작동 MyComponent.myArray = []하지만 입력 값이 변경 MyComponent.myArray.push(newValue)되면 ngOnChanges()트리거되지 않습니다. 이 변화를 포착하는 방법에 대한 아이디어가 있습니까?
Levi Fuller

4
ngOnChanges()중첩 된 객체가 변경되면 호출되지 않습니다. 어쩌면 여기
muetzerich

33

SimpleChanges함수 서명에서 유형을 사용할 때 콘솔과 컴파일러 및 IDE에서 오류가 발생했습니다 . 오류를 방지하려면 any대신 서명 에서 키워드를 사용하십시오 .

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

편집하다:

Jon이 아래에 지적했듯이 점 SimpleChanges표기법 대신 대괄호 표기법을 사용할 때 서명을 사용할 수 있습니다 .

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

1
SimpleChanges파일 맨 위에 인터페이스 를 가져 왔습니까?
Jon Catmull

@Jon-예. 문제는 서명 자체가 아니라 런타임시 SimpleChanges 오브젝트에 지정된 매개 변수에 액세스하는 것이 었습니다. 예를 들어 내 구성 요소에서 내 User 클래스를 입력 (예 :)에 바인딩했습니다 <my-user-details [user]="selectedUser"></my-user-details>. 이 속성은를 호출하여 SimpleChanges 클래스에서 액세스합니다 changes.user.currentValue. 공지 사항은 userSimpleChanges 객체의 런타임이 아닌 부분에 바인딩
다아시

1
그래도 이것을 시도 했습니까? ngOnChanges(changes: {[propName: string]: SimpleChange}) { console.log('onChanges - myProp = ' + changes['myProp'].currentValue); }
Jon Catmull

@ Jon-대괄호 표기법으로 전환하면 트릭이 수행됩니다. 대안으로 포함하도록 답변을 업데이트했습니다.
Darcy

1
'@ angular / core'에서 {SimpleChanges} 가져 오기; 만약 그것이 앵귤러 문서에 있고 네이티브 타입 스크립트 타입이 아니라면 아마도 앵귤러 모듈에서 왔을 것입니다. 아마도 '앵귤러 / 코어'일 것입니다
BentOnCoding

13

가장 안전한 방법은 매개 변수 대신 공유 서비스를 사용 하는 입니다 @Input. 또한 @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);
  }
}

다른 구성 요소에서 유사한 구현을 사용할 수 있으며 모든 구성 요소가 동일한 공유 값을 공유합니다.


1
감사합니다. 이것은 매우 좋은 시점이며 적절한 곳에서 더 나은 방법입니다. @Input 변경 감지와 관련된 특정 질문에 대한 답변으로 그대로 허용됩니다.
Jon Catmull

1
@Input 매개 변수는 복잡한 중첩 객체 유형 에서 변경 사항을 감지하지 못하지만 객체 자체가 변경되면 실행됩니까? 예를 들어 입력 매개 변수 "parent"가 있으므로 부모를 전체적으로 변경하면 변경 감지가 시작되지만 parent.name을 변경하면 그렇지 않습니다.
yogibimbi

솔루션이 더 안전한 이유를 제시해야합니다.
Joshua Kemmerer

1
@JoshuaKemmerer @Input매개 변수는 복잡한 중첩 개체 유형의 변경을 감지하지 않지만 간단한 기본 개체에서는 변경을 감지합니다.
Sanket Berde

6

값이 기본 값이 아닌 DoCheck경우 유용한 다른 수명주기 후크가 있음을 추가하고 싶습니다 @Input.

배열 Input이 있으므로 OnChanges내용이 변경 될 때 이벤트가 발생하지 않습니다 (앵귤러가 '간단하고'깊지 않기 때문에 배열의 내용이 변경되었지만 배열이 여전히 배열이기 때문에).

그런 다음 변경된 배열로 뷰를 업데이트할지 여부를 결정하기 위해 사용자 정의 검사 코드를 구현합니다.


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

이 방법을 사용해보십시오. 도움이 되었기를 바랍니다


4

부모 구성 요소 (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

입력 속성이 변경되면 ngOnChanges가 항상 트리거됩니다.

ngOnChanges(changes: SimpleChanges): void {
 console.log(changes.categoryId.currentValue)
}

1

이 솔루션은 프록시 클래스를 사용하며 다음과 같은 장점을 제공합니다.

  • 소비자는 RXJS 의 힘을 활용할 수 있습니다
  • 지금까지 제안 된 다른 솔루션보다 더 컴팩트
  • 사용하는 것보다 더 안전한ngOnChanges()

사용법 예 :

@Input()
public num: number;
numChanges$ = observeProperty(this as MyComponent, 'num');

유틸리티 기능 :

export function observeProperty<T, K extends keyof T>(target: T, key: K) {
  const subject = new BehaviorSubject<T[K]>(target[key]);
  Object.defineProperty(target, key, {
    get(): T[K] { return subject.getValue(); },
    set(newValue: T[K]): void {
      if (newValue !== subject.getValue()) {
        subject.next(newValue);
      }
    }
  });
  return subject;
}

0

ngOnChange 구현 og onChange () 메소드를 사용하지 않으려는 경우 valueChanges 이벤트 등으로 특정 항목의 변경 사항을 구독 할 수도 있습니다 .

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

});

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

이 선언에서 사용하기 때문에 작성된 markForCheck () :

  changeDetection: ChangeDetectionStrategy.OnPush

3
이것은 질문과 완전히 다르며 도움이되는 사람들은 코드가 다른 유형의 변경에 관한 것임을 알고 있어야합니다. 이 질문은 구성 요소 외부에서 데이터를 변경 한 다음 구성 요소가 데이터가 변경된 사실을 알고 응답하도록하는 데 대한 질문입니다. 귀하의 답변은 구성 요소 자체의 변화를 감지하여 구성 요소에 대한 로컬 형식의 입력과 같은 것들에 대해 이야기하고 있습니다.
rmcsharry 2016 년

0

EventEmitter를 입력으로 전달할 수도 있습니다. 이것이 모범 사례인지 확실하지 않습니다 ...

CategoryComponent.ts :

categoryIdEvent: EventEmitter<string> = new EventEmitter<>();

- OTHER CODE -

setCategoryId(id) {
 this.category.id = id;
 this.categoryIdEvent.emit(this.category.id);
}

CategoryComponent.html :

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

그리고 VideoListComponent.ts에서 :

@Input() categoryIdEvent: EventEmitter<string>
....
ngOnInit() {
 this.categoryIdEvent.subscribe(newID => {
  this.categoryId = newID;
 }
}

솔루션을 지원하는 유용한 링크, 코드 스 니펫을 제공하십시오.
mishsx

예를 추가했습니다.
Cédric Schweizer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.