programing

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

bestprogram 2023. 4. 27. 22:51

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

상위 구성 요소(CategoryComponent), 하위 구성 요소(videoListComponent) 및 ApiService가 있습니다.

나는 대부분의 이러한 작동 벌금을 가지고 있습니다. 즉, 각 구성 요소가 json api에 액세스하고 관찰 가능한 것을 통해 관련 데이터를 얻을 수 있습니다.

요소는 비디오를 . 범주의 . 저는 를 현비오목요모다가비든니져재옵디를오소를 통해 으로써 했습니다. 저는 이것을 특정 범주의 비디오로만 필터링하고 싶습니다. 저는 카테고리Id를 아이에게 전달하여 이를 달성했습니다.@Input().

CategoryComponent.html

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

이 categoryId를 됩니다.@Input()그러나 VideoListComponent에서 이를 감지하고 API 서비스를 통해 비디오 배열을 다시 요청해야 합니다(새 범주Id 사용).

Angular 앵귤러JS에서 나는 JS 것입니다했을 했을 입니다.$watch 문제를하는 가장 입니까?이것을 처리하는 가장 좋은 방법은 무엇입니까?

사실, 각도 2+에서 자식 구성 요소의 입력이 변경될 때 감지하고 작동하는 두 가지 방법이 있습니다.

  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
        
    }
    

문서 링크: ngOn 변경사항, 단순 변경사항, 단순 변경사항
데모 예제: 이 플런커 보기

  1. 또는 다음과 같이 입력 속성 설정기를 사용할 수도 있습니다.
    private _categoryId: string;
    
    @Input() set categoryId(value: string) {
    
       this._categoryId = value;
       this.doSomething(this._categoryId);
    
    }
    
    get categoryId(): string {
    
        return this._categoryId;
    
    }

Documentation Link: 여기를 보세요.

데모 예: 이 플런커를 보세요.

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

구성 요소에 여러 개의 입력이 있는 경우 ngOnChanges()를 사용하면 ngOnChanges() 내의 모든 입력에 대한 모든 변경 사항을 한 번에 얻을 수 있습니다.이 방법을 사용하면 변경된 입력의 현재 값과 이전 값을 비교하고 그에 따라 작업을 수행할 수도 있습니다.

그러나 특정 단일 입력만 변경되고 다른 입력은 상관하지 않는 경우에는 입력 속성 설정기를 사용하는 것이 더 쉬울 수 있습니다.그러나 이 접근 방식은 ngOnChanges 라이프사이클 방법으로 쉽게 수행할 수 있는 변경된 입력의 이전 값과 현재 값을 비교하는 기본 제공 방식을 제공하지 않습니다.

편집 2017-07-25: 각도 변화 감지는 일부 상황에서 여전히 작동하지 않을 수 있습니다.

일반적으로 세터 및 ngOnChanges 모두에 대한 변경 탐지는 데이터가 JS 기본 데이터 유형(문자열, 숫자, 부울)인 경우 부모 구성 요소가 자식에게 전달하는 데이터를 변경할 때마다 실행됩니다.그러나 다음 시나리오에서는 작동하지 않으므로 작동하려면 추가 작업을 수행해야 합니다.

  1. JS 기본 데이터 유형 대신 중첩된 개체 또는 배열을 사용하여 상위 데이터에서 하위 데이터로 데이터를 전달하는 경우 사용자: muetzerich의 답변에서도 언급했듯이 변경 탐지(세터 또는 ngchanges 사용)가 실행되지 않을 수 있습니다.솔루션은 여기를 참조하십시오.

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

을 합니다.ngOnChanges()구성 요소의 수명 주기 방법.

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

여기 문서들이 있습니다.

할 때 콘솔에도 가 발생했습니다.SimpleChanges함수 서명을 입력합니다.는 오를방사용다니합다음을을 합니다.any대신 서명에 키워드를 입력합니다.

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

편집:

Jon이 아래에서 지적했듯이, 당신은 다음을 사용할 수 있습니다.SimpleChanges점 표기법이 아닌 괄호 표기법을 사용하는 경우 서명합니다.

ngOnChanges(changes: SimpleChanges) {
    console.log(changes['myInput'].currentValue);
}
@Input() set categoryId(categoryId: number) {
      console.log(categoryId)
}

이 방법을 사용해 보십시오.이것이 도움이 되길 바랍니다.

가장 안전한 방법은 공유 서비스를 사용하는 것입니다.@Input매개 변수또한.@Input매개 변수는 복잡한 중첩 개체 유형의 변경을 탐지하지 않습니다.

간단한 서비스 예제는 다음과 같습니다.

서비스.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);
  }
}

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

각도 ngOn 변경 사항

ngOnChanges()는 기본 변경 디텍터가 데이터 바인딩 속성을 확인한 후 하나 이상이 변경된 경우 즉시 호출되는 내장 Angular 콜백 메서드입니다.보기 및 내용을 확인하기 전에 하위 항목을 선택합니다.

// child.component.ts

import { Component, OnInit, Input, SimpleChanges, OnChanges } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit, OnChanges {

  @Input() inputParentData: any;

  constructor() { }

  ngOnInit(): void {
  }

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

}

추가 정보: Angular Docs

제가 덧붙이고 싶은 것은 다른 라이프사이클 훅이 있다는 것입니다.DoCheck그것은 만약에 유용합니다.@Input값이 기본 값이 아닙니다.

어레이를 스토리지로 보유하고 있습니다.Input그래서 이것은 해고되지 않습니다.OnChanges(Angular가 수행하는 검사가 '단순'하고 깊지 않기 때문에 배열의 내용이 변경되더라도 배열은 여전히 배열이기 때문에) 내용이 변경되는 이벤트입니다.

그런 다음 일부 사용자 지정 확인 코드를 구현하여 변경된 배열로 보기를 업데이트할지 여부를 결정합니다.

저는 @alan-c-s가 제안한 접근 방식을 고수할 것입니다. 하지만 약간의 수정이 있습니다. - 사에반다니합대용을 사용하는 것에 반대합니다.ngOnChanges대신, 저는 변경해야 할 모든 것을 하나의 객체 아래로 이동할 것을 제안합니다.그리고 사용BehaviorSubject변경 내용 추적:

  private location$: BehaviorSubject<AbxMapLayers.Location> = new BehaviorSubject<AbxMapLayers.Location>(null);

  @Input()
  set location(value: AbxMapLayers.Location) {
    this.location$.next(value);
  }
  get location(): AbxMapLayers.Location {
    return this.location$.value;
  }

<abx-map-layer
    *ngIf="isInteger(unitForm.get('addressId').value)"
    [location]="{
      gpsLatitude: unitForm.get('address.gpsLatitude').value,
      gpsLongitude: unitForm.get('address.gpsLongitude').value,
      country: unitForm.get('address.country').value,
      placeName: unitForm.get('address.placeName').value,
      postalZip: unitForm.get('address.postalZip').value,
      streetName: unitForm.get('address.streetName').value,
      houseNumber: unitForm.get('address.houseNumber').value
    }"
    [inactive]="unitAddressForm.disabled"
    >
</abx-map-layer>

HergOnChanges는 입력 속성이 변경될 때 항상 트리거됩니다.

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

는 관찰 한 항목을 가질 .component(CategoryComponent)하위 구성 요소의 구독에서 원하는 작업을 수행합니다.(videoListComponent)

service.ts

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

범주 구성요소.ts

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

videoListComponent.ts

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

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

  • 소비자가 RXJS의 힘을 활용할 수 있습니다.
  • 지금까지 제안된 다른 솔루션보다 컴팩트함
  • 사용하는 것보다 더 안전한 유형ngOnChanges()
  • 이 방법으로 모든 클래스 필드를 관찰할 수 있습니다.

사용 예:

@Input()
num: number;
@Input()
str: number;
fields = observeFields(this); // <- call our utility function

constructor() {
  this.fields.str.subscribe(s => console.log(s));
}

유틸리티 기능:

import { BehaviorSubject, Observable, shareReplay } from 'rxjs';

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

export const observeFields = <T extends object>(target: T) => {
  const subjects = {} as { [key: string]: Observable<any> };
  return new Proxy(target, {
    get: (t, prop: string) => {
      if (subjects[prop]) { return subjects[prop]; }
      return subjects[prop] = observeField(t, prop as keyof T).pipe(
        shareReplay({refCount: true, buffer:1}),
      );
    }
  }) as Required<{ [key in keyof T]: Observable<NonNullable<T[key]>> }>;
};

데모

이벤트 이미터를 입력으로 전달할 수도 있습니다.이게 최선의 방법인지 잘 모르겠어요...

범주 구성 요소.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;
 }
}

를 사용할 수 있습니다.BehaviorSubject에서 그 하는 이벤트가 발생합니다..next()완료했습니다. 파괴 시 라이프사이클 후크 내에서 해당 구독을 닫으십시오.

data-api.sys.ts

@Injectable({
  providedIn: 'root'
})
export class DataApiFacade {

currentTabIndex: BehaviorSubject<number> = new BehaviorSubject(0);

}

some.component.ts

constructor(private dataApiFacade: DataApiFacade){}

ngOnInit(): void {
  this.dataApiFacade.currentTabIndex
    .pipe(takeUntil(this.destroy$))
       .subscribe(value => {
          if (value) {
             this.currentTabIndex = value;
          }
    });
}

setTabView(event: MatTabChangeEvent) {
  this.dataApiFacade.currentTabIndex.next(event.index);
}

ngOnDestroy() {
  this.destroy$.next(true);
  this.destroy$.complete();
}

기본적으로 제안된 두 솔루션 모두 대부분의 경우에 잘 작동합니다.ngOnChange()에 대한 저의 주요 부정적인 경험은 유형 안전성의 부족입니다.

제 프로젝트 중 하나에서 저는 몇 가지 이름을 바꿨습니다. 그 뒤를 따르는 마법의 끈들 중 일부는 변하지 않았습니다. 그리고 물론 그 벌레는 표면화되는 데 시간이 좀 걸렸습니다.

설정자는 해당 문제를 가지고 있지 않습니다. IDE 또는 컴파일러가 불일치를 알려줍니다.

ngOnChanges() 수명 주기 방법을 사용할 수 있습니다.

@Input() inputValue: string;

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

상위 구성 요소와 하위 구성 요소 간에 데이터를 공유하기 위해 사용하는 경우 라이프사이클 방법을 사용하여 데이터 변경을 감지할 수 있습니다.

 ngOnChanges(changes: SimpleChanges) {
    if (!changes.categoryId.firstChange) {
      // only logged upon a change after rendering
      console.log(changes.categoryId.currentValue);
    }
  }

또한 ChangeDetection Strategy를 추가해야 하는 하위 구성 요소에 대해 구현된 변경 전략을 고려해야 합니다.어떤 성능상의 이유로 푸시 중:

예제-코드:

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class VideoListComponent implements OnChanges {
  @Input() categoryId: string;

어떤 대답도 최선의 해결책은 아닌 것 같습니다.그들은 궁극적으로 일을 하더라도 지나치게 복잡합니다.

다음과 같은 입력 변수에 "자동 속성"이라고도 하는 get-set 패턴을 사용하기만 하면 됩니다.

    ...
    private _categoryId: number;
    @Input()
    set categoryId(value: number) {
      this._categoryId = value;
      // Do / call whatever you want when this input value changes here
    }
    get categoryId(): number {
      return this._categoryId;
    }
    ...

코드의 에서, 은 코의다곳면라이, 은다같참수이있조다될습니음과이른드것으로 참조될 수 있습니다.this.categoryId왜냐하면 Getter는 TypeScript에서 참조할 때 자동으로 호출되기 때문입니다.

자세한 내용:

export class ChildComponent implements OnChanges {
  @Input() categoryId: string;

  ngOnChanges(changes: SimpleChanges) {
    if (changes.categoryId) { // also add this check 
      console.log('Input data changed:', this.categoryId);
    }
  }
}

입력이 변경되면 탐지 호출 ngOnChanges를 변경합니다.변경 사항:SimpleChanges 개체는 모든 변경을 수행합니다.categoryId가 변경사항 중 하나인지 확인합니다.가능한 경우 필요한 작업을 수행합니다.

않는다면 사하지않경우는려를 사용하세요.ngOnChange합니다.onChange()메소드, 이벤트별로 특정 항목의 변경사항을 구독할 수도 있습니다.

myForm = new FormGroup({
  first: new FormControl(),
});

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

그자리의 markForCheck()선언에 되었습니다.

changeDetection: ChangeDetectionStrategy.OnPush

언급URL : https://stackoverflow.com/questions/38571812/how-to-detect-when-an-input-value-changes-in-angular