4

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);
    }
}

4 Answers 4

2

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

Sign up to request clarification or add additional context in comments.

2 Comments

In my case, an update to the values happen inside a function in the child component. The value update doesn't happen in ngOnInit but in some other function. I want to revert back my values when the user clicks on the parent component. That's not happening.
For example: public ngOnInit(): void {setTimeout(() => {this.boolChild = false;this.numChild = 5;},3000);}. Now values from parent will be displayed first, after 3 seconds values from child will be displayed without any problems. But when I try to update from parent component again by calling the update function, it won't set the boolean value rather it will stick to the value from child component.
0

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)

3 Comments

Is there any way to force the change detection so that it can display the 'TRUE' from the parent component?
Updated the answer
Thanks for this. I have solved it using another hack, I just had to update the value before I call with the right value. In my actual code, this happens before and after an API call, so I don't have to use set timeout. stackblitz.com/edit/angular-uqara7?file=src/app/…. I am selecting yours as the right answer as you have a better working solution using viewchild.
0

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...

Comments

0

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

7 Comments

stackblitz.com/edit/… I have made the example simpler. First it is set to true, then in child in some function it changes to false. Now I want to set it to true when clicking on update text from parent component. How can I achieve that? This issue is killing me :( Don't worry about ngOnInit, the update in child component happens in a separate function.
Again you are trying to change from this.parentBool = true to this.parentBool = true. There is no change. If there is no change to the value, the ngOnChanges hook won't be triggered.
I've modified your Stackblitz: stackblitz.com/edit/angular-naxqcz. I am sending difference strings for each call. Now the value will be changed and you can get a clear idea of what is happening.
But the value got changed in the child component to 'false', right? How can I update it from parent component then? Is there any way to solve this particular problem? I know it is weird, but this is what I have to solve. I have tried for more than 4 hours already :(
You are trying to change from true to true from parent's point of view. If you want to change the input variable in both the parent and child, it's better to look into singleton services and observables. They are more flexible. You could refer my other answer here: stackoverflow.com/a/60154572. Input is better suited for one-way data binding from parent to child.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.