In Angular, it's well known how to pass data from the parent component to the child. What I would like to demonstrate is how in Angular with two way binding we can ensure that changes in the child are also propagated to the parent when needed.
To see a working example, check out the stackblitz.
Let's have a look first at input properties!
@Component({
selector: 'child',
templateUrl: './child.html'
})
export class ChildComponent {
//Input() decorator means that
//we can pass parameters to this component
@Input() count: number;
}
And we can make use of it from a parent component like this:
<child [count]="counterOnParent">
What this means now is that whenever the parent modifies the variable (counterOnParent) that's being passed to the child component, the child will also receive the new value in its input (@Input()) variable.
Ok, so far so good.
What would happen to the variable in the parent if we modified it in the child component though?
@Component({
selector: 'child',
templateUrl: './child.html'
})
export class ChildComponent {
@Input() count: number;
//What will happen to the value in the parent?
increment(): void {
this.count += 1;
}
}
The problem
Well it turns out... nothing. The parent is not notified of this change that was made in the child. Which may not be a problem, unless we are also modifying it in the parent. This can lead to some strange results as can be seen in the first example in the stackblitz.
Let's have a look at some pseudocode as an example of what happens exactly:
parent.increment();//parent.count: 1, child.count: 1
parent.increment();//parent.count: 2, child.count: 2
child.increment(); //parent.count: 2, child.count: 3
child.increment(); //parent.count: 2, child.count: 4
parent.increment();//parent.count: 3, child.count: 3
Note how in the last line, the child count goes back to match the parent state.
You may have noticed that this problem is because we are managing two different pieces of state (one in the parent and one in the child) and they are going out of sync. By modifying the value in the child, we are breaking the usual data flow that Angular expects in this case, which is from parent to child. Not child to parent.
The solution
It's best to keep the flow of data changes from parent to child.
This is where two-way binding comes in. It's fortunately such a commonly used pattern in Angular that there is a convention for creating it.
Create an EventEmitter with the same name as the input variable but with the word "Change" appended to it.
@Component({
selector: 'child',
templateUrl: './child.html'
})
export class ChildComponent {
@Input() count: number;
@Output() countChange = new EventEmitter<number>();
increment(): void {
this.countChange.emit(this.count + 1);
}
}
And now the usage from the parent is like so:
<child [(count)]="counterOnParent">
As you can see we are combining the syntax for binding to input properties (square brackets) and binding to an event (round brackets). Since we've used this convention that Angular understands there was no need to specify the actual event name on the parent!
Now everytime the child component wants to change it's variable value, instead of modifying it directly, it will send an event to the parent which will change its variable and propogate it appropriately in the correct data flow direction.
We can see this in the second example of the stackblitz.
Happy coding :-)
Top comments (1)
Thanks for sharing this tip!