Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

detect change of nested property for component input

In a simplified way I have an Angular2 Component and an input object like this:

class MyObject{
 Prop1:string;
 Prop2:Number;
}  

@Component() 
export class MyComponent{
 @Input() myObject: MyObject;
 DoSomethingIfProp1Change(){
  console.log(myObject.Prop1);
 }
}

How can I detect if Prop1 was changed from Hostcomponent and then execute the DoSomethingIfProp1Change method from inside MyComponent?

like image 695
Xaver Avatar asked Mar 28 '16 17:03

Xaver


People also ask

How do you find changes in Angular inputs?

Use the ngOnChanges() lifecycle method in your component. ngOnChanges is called right after the data-bound properties have been checked and before view and content children are checked if at least one of them has changed.

How do you identify change in components?

Whenever there is a change in the object, the subscribe method is called, so you should manually run the change detector inside the subscribe method to update the view. To run the change detector manually: Inject ChangeDetectorRef service in the component.

Why ngOnChanges is not getting called?

ngOnChanges gets called before ngOnInit and whenever a component's bound input is changed FROM THE PARENT COMPONENT. Remember that ngOnChanges is specific to bound inputs on the component. This means if you don't have any @Input properties on a child, ngOnChanges will never get called.


2 Answers

In fact, by default Angular2 detects changes when object reference is updated not its content. But this default behavior can be changed by using the DoCheck interface.

In your case (detecting that a property was updated into the myObject object, you could use the following approach:

@Component({
  selector: 'my-component',
  (...)
}) 
export class MyComponent implements DoCheck {
  @Input() myObject: MyObject;
  differ: any;

  constructor(differs:  KeyValueDiffers) {
    this.differ = differs.find([]).create(null);
  }

  ngDoCheck() {
    var changes = this.differ.diff(this.myObject);

    if (changes) {
      changes.forEachChangedItem((elt) => {
        if (elt.key === 'prop1') {
          this.doSomethingIfProp1Change();
        }
      });
    }
  }

  doSomethingIfProp1Change() {
    console.log('doSomethingIfProp1Change');
  }
}

When the value of the prop1 property is updated, the doSomethingIfProp1Change method is called.

See this plunkr: http://plnkr.co/edit/uvOKMXQa9Ik8EiIhb60Y?p=preview.

like image 199
Thierry Templier Avatar answered Oct 23 '22 15:10

Thierry Templier


You can use observables to support notification of subscribers. Angular itself doesn't provide support for observing changes of internal object state.

class MyObject{
  constructor() {
    this.prop1Change$ = new Observable(observer => 
        this._prop1Observer = observer).share(); // share() allows multiple subscribers

    this.prop2Change$ = new Observable(observer =>
        this._prop2Observer = observer).share();
        console.debug(this._prop2Observer);
  }

  prop1Change$: Observable<string>;
  private _prop1Observer: Observer;
  _prop1:string;
  get prop1():string { return this._prop1 };
  set prop1(value:string) {
    this._prop1 = value;
    this._prop1Observer && this._prop1Observer.next(value);
  }

  prop1Change$: Observable<number>;
  private _prop2Observer: Observer;
  _prop2:Number;
  get prop2():number { return this._prop2 };
  set prop2(value:number) {
    this._prop2 = value;
    console.debug(this);
    this._prop2Observer && this._prop2Observer.next(value);
  }
}

This code could be shortened by using Subject but Observable should be favored over Subject.

@Component() 
export class MyComponent{
  @Input() myObject: MyObject;

  count2:number;

  DoSomethingIfProp1Change(){
    console.log(myObject.prop1);
  }

  ngOnChanges(changes: {[propName: string]: SimpleChange}) {
    console.log('changes');
    if(changes['myObject']) {
      if(this.prop2Subscription) {
        this.prop2Subscription.unsubscribe();
      }
      this.prop2Subscription = this.myObject.prop2Change$.subscribe(value => {
        this.count2 = value;
        console.log('count2: ' + value);
      });
      // same for `prop2`
    }
  }
}

Plunker example

See also Delegation: EventEmitter or Observable in Angular2

like image 32
Günter Zöchbauer Avatar answered Oct 23 '22 16:10

Günter Zöchbauer