Reputation: 2253
I've been working on learning Angular 2 two-way binding, and I've put together a demo that binds a component's property to another, contained component's property, as well as to several text fields. Unfortunately, a lot of it seems like magic to me, which makes it awfully hard to debug when the magic isn't working. In particular, the demo reveals several places where I don't yet fully understand binding; this question is the first of what may be several on this topic.
(The demo is at http://plnkr.co/edit/IUTy5p?p=preview, in case you want to follow along at home...)
The code implements a parent component with a pName
string property, and a contained, child component with a cName
property. The parent's property is two-way bound to the child's property using the banana box syntax:
<child [(cName)]="pName"></child>
Inside the child is a display of the cName
property, as well as three text inputs, each of which is in some way bound to the cName
property. The second text input is the important one here:
<input type="text" [ngModel]="cName" (ngModelChange)="cNameChange.emit($event)">
The ngModelChange
event invokes an emit()
method on an EventEmitter
whose name cNameChange
is derived from the cName
property's name:
@Output() public cNameChange:EventEmitter<String> = new EventEmitter<String>();
So, if you edit the second text input's value, the child's cName
property changes, the other two text inputs change their values, and the parent's pName
property changes.
But, how?
This can be split into two related questions:
cName
property?cName
property end up changing the parent's pName
property?Upvotes: 2
Views: 1182
Reputation: 208944
The only real magic is the [()]
syntax, which is specified by Angular. The rest can explained in the following sections
When we use [(cData)]="pData"
it is the same as using
[cData]="pData" (cDataChange)="pData = $event"
where when an event is emitted to cDataChange
, Angular guarantees to set the value of the bound input property ([cData]="pData"
), in this case pData
to the value emitted from the cDataChange
- How does emitting that event change the
cName
property?
@Component({
template: `
<input type="text" [ngModel]="cName" (ngModelChange)="cNameChange.emit($event)">
`
})
export class ChildComponent {
@Input() public cName:string = 'thisIsIgnored';
@Output() public cNameChange:EventEmitter<String> = new EventEmitter<String>();
}
@Component({
selector: 'parent',
template: `
<child [(cName)]="pName"></child>
`
})
export class App {
pName:string;
constructor() {
this.pName = 'InitialName'
}
}
The parent pData
is two way bound to child cData
using the simplified syntax [(cData)]="pData"
. This means that the parent is both providing the @Input('cData')
to the child and listenting to the cNameChange
event, which automatically changes the bound property, in this case pData
. This is the only really magical part. But it is what is described in Angular docs, so we just accept it to be true.
So when you type into the second child input, it emits the cDataChange
event and the pData
gets changed, per Angular specifications. That pData
is also the input for cData
, so that cData
also needs to change to match the pData
input.
You can confirm that parent is responsible for the cData
change by commenting out the first and third input of the child, and just adding
<li>cName: {{ cName }}</li>
- How does changing the child's
cName
property end up changing the parent'spName
property?
You're not ever really changing it (explicitly) at any point. This is all handled by the two-way binding. If you add something that does explicitly change it, say
<button (click)="cName = 'hello'">Change</button>
or just simply typing in the third input. When you click the button or type, you will see that it does not change the parent, but the children see the change because they're getting input from the cData
. The only way the parent will see the change is cData
is only if the cDataChange
event is emitted with the new value.
So what actually changes the parent is the activity discussed in the previous point.
Upvotes: 3