Daniel Griscom
Daniel Griscom

Reputation: 2253

Angular 2 property binding: how do EventEmitters work?

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:

  1. How does emitting that event change the cName property?
  2. How does changing the child's cName property end up changing the parent's pName property?

Upvotes: 2

Views: 1182

Answers (1)

Paul Samsotha
Paul Samsotha

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

  1. 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>
  1. How does changing the child's cName property end up changing the parent's pName 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

Related Questions