Reputation: 1494
I have 2 components - Parent and Child. Please see stackblitz demo
Parent's template looks like below -
When 'Show Child' is clicked, isChild1Visible is set to true causing the Child component to display its template.
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:
After 'Show me' is clicked for 1st time
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?
Upvotes: 2
Views: 3095
Reputation: 1736
Since data isChild1Visible is being passed as an primitive type(boolean) it will "Pass by Value."
Therefore, if passing an object, array, or the like, then it is Passed by Reference, and for primitive types like numbers, it is Passed by Value.
try this without adding any extra outputs :
MyAppChild1Component
export class MyAppChild1Component implements OnInit {
@Input() isVisible;
constructor() {}
ngOnInit() {}
onClick() {
this.isVisible.value = false;
}
}
<div *ngIf="isVisible.value">
<span>
Child
</span>
<button (click)="onClick()">Hide me</button>
</div>
AppComponent
export class AppComponent {
isChild1Visible = { value: false };
ngOnInit() {}
onShowChild() {
this.isChild1Visible.value = true;
}
}
<div>
<p>Parent</p>
<button (click)="onShowChild()">show Child</button>
<app-my-app-child1 [isVisible]="isChild1Visible"></app-my-app-child1>
</div>
Upvotes: 3
Reputation: 3675
That happens because isChild1Visible
in app.component isn't updated when the child component sets
isVisible
.
onShowChild()
isChild1Visible
= trueisVisible
= trueonClick()
in childisVisible
= false;isChild1Visible
= trueonShowChild()
isChild1Visible
= true // => No changes hereisVisible
= falseSince the value of isChild1Visible
is not changed, Angular does not update anything.
In order to trigger Change Detection and keep values in sync, you need to use Two-Way binding.
<app-my-app-child1 [(isVisible)]="isChild1Visible"></app-my-app-child1>
export class MyAppChild1Component implements OnInit {
@Input() isVisible;
@Output() isVisibleChange = new EventEmitter<boolean>();
constructor() {}
ngOnInit() {}
onClick() {
this.isVisibleChange.emit(!this.isVisibleChange);
}
}
Upvotes: 1
Reputation: 4474
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.
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.
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.
Upvotes: 8