viewchild input property not updated when needed angular 2

destined picture destined · Oct 27, 2016 · Viewed 10.8k times · Source

I have been looking for some reason of this behavior, the value of a Input() property in a child component 'Param' is not been updated at the right time, I need to use the updated value to call a service as a parameter. By clicking the 'update child value' button the LOG B is displayed first (with an old value) after that the LOG A is displayed (LOG A it's in the setter of the property).

Parent component

@Component({
  selector: 'parent',
  template: `
     <child #child [param]="parentParam"></child>
     <button (click)="updateChildValue()">Update child value</button>
`})
export class Parent {
  @ViewChild('child') child: Child;
  public parentParam: number;

  public updateChildValue() {
    this.parentParam = 9;
    this.child.showParam();
  }
}

the child component

@Component({
   selector: 'child',
   template: '' })

export class Child {

   private _param: number;

   public get param() : number {
       return this._param;
   }

   @Input('param')
   public set param(v : number) {
      this._param  = v;
      console.log('LOG A');
      console.log(this.param);        
   }

   public showParam() {
      console.log('LOG B');
      console.log(this.param);
      //I need to call a service using the latest value of param here...
   }
}

The desired behavior is to have first the value of the param updated and then use that value as a parameter on a service. LOG A then LOG B

Answer

Harry Ninh picture Harry Ninh · Oct 27, 2016

Using @Input() implies you are relying on Angular2 mechanism to propagate the value changes, which means you should not expect your changes to take immediate effect. In fact, your this.parentParam = 9; will go through a whole Angular2 cycle to update your child's param and the showParam() function will always be executed first.

To achieve your task, you have 2 choices:

1) Add parameter directly to your showParam, as in:

// child component
public showParam(newVal: number) {
  this._param = newVal;
  // do your service call here
}

// parent component
public updateChildValue()
{
  this.parentParam = 9;
  this.child.showParam(9);
}

2) Leverage ngOnChanges on your Child component. This function will be invoked by Angular2 every time there's a change to any @Input. However, if you have more than 1 input, this could be problematic.

ngOnChanges(changes) {
  console.log(this.param); // new value updated
}