4

Please have a look at the Stackblitz Editor link. I've set up one angular application.

Brief working overview of the angular app

  • Two components i.e app-component and child-component
  • Initially, child component not display. There is a button in the parent app component when we click on the button, we enable one variable 'this.showChildComponent = true'.
  • child component controlled by the variable this.showChildComponent.
  • I'm using the 'firstParam' and 'secondParam' as a variable and sending these variables values to the child component.

Now, Please have a look at the issue

When we click on the button as I explained above, child component mounted and ngOnInit method will be called, you can also check the message on the console screen 'ngOnInit called: child component' but if I again click on the button, then the child component not re-render again and thus, ngOnInit method of child component not called.

// This method is defined in the app-component (parent) and it is called when user clicks on 
// button
showChild() {
    if (this.showChildComponent)
    {
      this.showChildComponent = false;
    }
    this.showChildComponent = true;
    // child component ngOnInit call when
    // I use the timer
    // setTimeout(()=> {this.showChildComponent = true;}, 10);
    this.firstParam = 'first param from parent';
    this.secondParam = 'second param from parent';
  }

// Parent component - app-component
<button (click)="showChild()">Click here to display the child component</button>

<div *ngIf="showChildComponent">
  <app-child-component
    [first_param]="firstParam"
    [second_param]="secondParam"
  ></app-child-component>
</div>

As you can see in the code, if I use the setTimeout then child component ngOnInit method will be called whenever I click on the parent component button. But I don't want to use the timer here, what is the alternative solution to resolve this issue? whenever user clicks on the button, ngOnInit method of child component should be call.

Shubham
  • 1,163
  • 2
  • 12
  • 36
  • what you want to achieve in child component when user clicks on button again ? – YogendraR Dec 31 '19 at 12:37
  • @YogendraR whenever the user clicks on the button, ngOnInit method of child component should be called. – Shubham Dec 31 '19 at 12:42
  • that's what I am asking, what you want to do inside ngOnInit with every button click. What is the use case here ? As I can see, you don't want to destroy child either. – YogendraR Dec 31 '19 at 12:47
  • @Shubham you have set it to false and then true before the change detection has had a chance to read that there is a change. This means that the child component will not be removed and then re-added. This is why it works by using a timeout, because the timeout will get executed after the change detection. If you really want the behavior to run in the ngOnInit then you would need to use a settimeout to wait for the component to be destroyed. – jgerstle Dec 31 '19 at 13:05
  • @jgerstle thanks for your input and time. But is there an alternate approach to destroy the child component? – Shubham Dec 31 '19 at 13:08

4 Answers4

2

Copied from comment to give a bit of reference for the answer:

You have set it to false and then true before the change detection has had a chance to read that there is a change. This means that the child component will not be removed and then re-added. This is why it works by using a timeout, because the timeout will get executed after the change detection. If you really want the behavior to run in the ngOnInit then you would need to use a settimeout to wait for the component to be destroyed.

Since it seems that you don't want to use the setTimeout, even though that's how I'd suggest doing it, you can force the change detection using one of the methods provided here. Here is a stackblitz fork from your original question, using the third approach.

jgerstle
  • 1,674
  • 5
  • 25
  • 35
  • Thanks for the answer but why we can't use the changeDetection Ref for manually detecting the changes. Is there any difference between changeDetection ref or application ref ? – Shubham Jan 02 '20 at 05:12
  • 1
    To be honest that might be better, I just chose the first result to prove that it fixes your issue. I can try to post a stackblitz later with the `changeDetectionRef` – jgerstle Jan 02 '20 at 05:19
  • 1
    Ok I'll update the answer later since I think that it is more correct to use the `changeDetectionRef` because you won't cause the whole app to run a change detection cycle – jgerstle Jan 02 '20 at 05:22
  • 1
    I changed the stackblitz example to use the third suggestion – jgerstle Jan 04 '20 at 20:42
1

Try this one to toggle your component:

  showChild() {
    this.showChildComponent = !this.showChildComponent;
    this.firstParam = 'first param from parent';
    this.secondParam = 'second param from parent';
  }
Dmitry Sobolevsky
  • 1,171
  • 6
  • 12
0

You need to add some additional condition to your if statement. Right now, the logic always assigns this.showChildComponent to true (even after you've assigned it to false). Change your showChild method to:

showChild() {
    if (this.showChildComponent)
    {
      this.showChildComponent = false;
    } else {
      this.showChildComponent = true;
    }
    // child component ngOnInit call when
    // I use the timer
    // setTimeout(()=> {this.showChildComponent = true;}, 10);
    this.firstParam = 'first param from parent';
    this.secondParam = 'second param from parent';
  }

You were previously always setting the showChildComponent attribute to true and never were destroying the child. Thus, it was never re-rendering.

tlm
  • 952
  • 10
  • 20
  • I don't want to implement toggling functionality. Please re-read the description again. – Shubham Dec 31 '19 at 12:32
  • 1
    The `ngOnInit` is called only once when the component is created. Even in your `setTimeout` scenario, you are still destroying the original component, which is the only reason that you are able to then "re-call" `ngOnInit`. The only way to call `ngOnInit` again, is to destroy and remount your original component, which you have accomplished with your timeout OR you can accomplish by destroying the original component (such as in a toggle). – tlm Dec 31 '19 at 12:42
  • Is there any other way to destroy the component without using the timeout and toggling ? – Shubham Dec 31 '19 at 12:44
  • 1
    If you wanted to re-render some information in the child based on a change in the parent, you could use `ngOnChanges` (`OnChanges`) lifecycle, but you would have to pass down some new information from the parent to the child. That wouldn't really destroy the component, though -- it will simply rerender whatever has changed. But, the only way to really destroy a component, is to take it out of the DOM, and I don't think you'll be able to do that without setting that variable to `false`. – tlm Dec 31 '19 at 12:51
  • I know about ngOnChanges but for the knowledge purpose, I want to know, how to call ngOnInit again in this scenario. – Shubham Dec 31 '19 at 12:55
  • 1
    You cannot call `ngOnInit` twice on the same instance of the component. You can only call it once. It will only be called "again" after you destroy the original component. If you change the `ngIf` to be `false`, then you will destroy that component. The only way to destroy, is to take that component out of the DOM. And, in your scenario, the `boolean` that you are using is the way that you take the component out of the DOM. You need that variable to change through some toggle. Technically, your `setTimeout` is a toggle. It just feels different due to the "async" nature of `setTimeout`. – tlm Dec 31 '19 at 13:21
0

For this you can manually trigger the child component by using ChangeDetectorRef

Create an instance of ChangeDetectorRef and use like this below in app component.

this.changeDetectorRef.detectChanges();

Or Alternative way to achieve this is to use ngOnChanges, which will change everytime when you pass params as Input to child component. Here is a stackblitz fork by using ngOnChanges lifecycle hook.

Nimish
  • 1,053
  • 13
  • 29