Search code examples
angularangular-changedetection

Changing @Input() in child compoent properties prevents further updates from parent component


I have 2 components - Parent and Child. Please see stackblitz demo Parent's template looks like below - enter image description here

When 'Show Child' is clicked, isChild1Visible is set to true causing the Child component to display its template. enter image description here

When you click 'Hide me' the Child component sets isVisible to false, causing it to be hidden. Now when 'Show Child' is clicked again, the child component is not shown.

I add ngOnChanges() in the Child component to see the change detection, however I see the below When child component is initialized: enter image description here

After 'Show me' is clicked for 1st time enter image description here

After 'Hide me' is clicked, the ngOnChanges() does not print anything even when 'Show me' is clicked later on.

So, why does change detection stop working after input property is update in the child component?


Solution

  • The issue

    The first time you set isChild1Visible, with the one way binding it also sets the child isVisible to true.

    From the inside, you set isVisible to false, so the div disappears as you have *ngIf="isVisible".

    However, the outer isVisible is still true!

    When you hit again the outer button, there's no changes to detect as the value remains the same, so nothing is passed to the child.

    See this stackblitz demo with both booleans exposed.

    Option 1: Explicitly listen to on output

    If you want to control a component from the outside, you should keep the logic outside.

    A possible way is to add an @Output on your child that is an EventEmitter that emits when you want to close the child.

    The parent will listen to that event and do something, like setting isVisible to false.

    You can see it on this updated stackblitz.

    Option 2: Two-way binding

    Similar to solution Option 1, you can create an emitter called with the same name of your input, ending with Change and trigger there your change.

    In this case you only need to replace the binding with [(isVisible)]="isChild1Visible" and add isVisibleChange as @Output EventEmitter<any>.

    Demo on stackblitz.