Gokul
Gokul

Reputation: 43

Angular @Input() value is not changed when the value is same

When I run this program, initially I get the output as false, 5, 500 as I have initialized them in the child component like that, but when I try to click on update button, I am not able to revert to the previous values. I am expecting the output to be true, 10, 1000, but I am getting it as false, 5, 1000. Only the number which was different got changed.

How can I solve this issue so that I can get the values whatever I set in the parent component?

Link to stackblitz.

app-component.html

<span (click)="clickme()">Update data</span>

app-component.ts

export class AppComponent  {
  public parentBool: boolean;
  public parentNum: number;
  public p2: number;


  public ngOnInit(): void {
    this.parentBool = true;
    this.parentNum = 10;
    this.p2 = 100;
  }

  public clickme(): void {
    this.parentBool = true;
    this.parentNum = 10;
    this.p2 = 1000;
  }
}

hello.component.ts

@Component({
  selector: 'hello-comp',
  template: `
    <div>
     Boolean value is - {{boolChild}} <br/>
     Number value is - {{numChild}} <br />
     Second number is - {{numChild2}}
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
  @Input() boolChild: boolean;
  @Input() numChild: number;
  @Input() numChild2: number;


  public ngOnInit(): void {
    this.boolChild = false;
    this.numChild = 5;
    this.numChild2 = 500;
  }
  constructor(private _cd: ChangeDetectorRef){}

    public ngOnChanges(changes: {}): void {
      // this._cd.detectChanges();
      console.log(changes);
    }
}

Upvotes: 4

Views: 6527

Answers (4)

Supun De Silva
Supun De Silva

Reputation: 1487

Well your problem is when everything is initialized

Parent value is True and Child Value is False

You are clicking on parent span. This sets the parentBool value to True. This is not a change when it comes to Parent Component. Hence the Change detection does not fire.

I forked this https://stackblitz.com/edit/angular-rgdgd6 from your link below https://stackblitz.com/edit/angular-jv3sjz

You can try 2 different approachs

  1. Capture ViewChildren and update within Parent https://stackblitz.com/edit/angular-4fqlqg
  2. Have a service to maintain intermediate state and children will listen to that state. (if required use ngrx or else a simple service will suffice) (Prefer this a bit better as this is more scaleable)

Upvotes: 0

Barremian
Barremian

Reputation: 31125

This is how the input variables in the child component is initialized at the moment.

  1. Initialize AppComponent, trigger ngOnInit and render the template.
  2. Initialize MyComponent, trigger ngOnChanges. This will show in the console log as follows
{
  "boolChild": {
    "currentValue": true,
    "firstChange": true
  },
  "numChild": {
    "currentValue": 10,
    "firstChange": true
  },
  "numChild2": {
    "currentValue": 100,
    "firstChange": true
  }
}
  1. Trigger ngOnInit (note: this is triggered after ngOnChanges) in MyComponent assign values false, 5 and 500. This is the reason why these values are shown in the template and not the one sent initially from the AppComponent.

  2. Click Update data button in the AppComponent which will push values true, 10 and 1000. It will show the following output

{
  "numChild2": {
    "previousValue": 100,
    "currentValue": 1000,
    "firstChange": false
  }
}

Explanation

According to ngOnChanges in the MyComponent, the previous values were true, 10 and 100 (from the ngOnInit of the AppComponent). The values false, 5 and 500 were not registered by the ngOnChanges because they weren't changes through the Input(). And so changes shows the only object that has been changed from it's previous state, which is numChild2. The other 2 values stay the same and they aren't reflected in the ngOnChanges.

One solution would be to avoid assigning values in the ngOnInit() hook in the child and assigning the values during definition.

_boolChild: boolean = false;
_numChild: number = 5;
_numChild2: number = 500;

Even with these changes, you will still observe only numChild2 object in the changes variable when you click the button because that is how SimpleChanges works. It will only show the value that has been changed from it's previous state, it won't reflect the values that stay the same.


Here is the hook calling order for reference

  • OnChanges
  • OnInit
  • DoCheck
  • AfterContentInit
  • AfterContentChecked
  • AfterViewInit
  • AfterViewChecked
  • OnDestroy

Upvotes: 0

gsa.interactive
gsa.interactive

Reputation: 565

have you tried calling detectChanges before anything else?

  ngOnInit() {
    this._cd.detectChanges();
    ....rest of your code...
  }

Btw, I don't understand why you are setting the same values in the ngOnInit() as on line 14 & 15. Actually you don't need to...

Upvotes: 0

bryan60
bryan60

Reputation: 29335

Input bindings occur right before ngOnInit, you’re overriding the values in ngOnInit.

Put your defaults in the declaration:

@Input() numChild: number = 5;

Like that

Upvotes: 2

Related Questions