Version: "angular2": "2.0.0-beta.6"
I would like to implement a two way binding inside a parent/child component case.
On my child component, I'm using two-way binding to display text while editing.
Child component (InputTestComponent [selector:'input-test']):
<form (ngSubmit)="onSubmit()" #testform="ngForm">
{{name}}
<textarea #textarea [(ngModel)]="name" ngControl="name" name="name"></textarea>
<button type="submit">Go</button>
</form>
Then, I would like to propagate this change to his parent component.
I tried with [(name)]="name" with no success.
Parent component:
<div>
{{name}}
<input-test [(name)]="name"></input-test>
</div>
What the easiest way to do it (less verbose) ?
For 2-way binding use @Input() and @Output(). The names should be propName and propNameChange to allow the shorthand binding syntax [(propName)]="someModel" otherwise you'd need the longer version [propName]="someModel" (propNameOtherOutputName)="propName=$event;propNameOtherOutputName.emit($event)"
@Component{
...
template: `
<textarea #textarea [(ngModel)]="name" (ngModelChange)="nameChange.emit($event)" ngControl="name" name="name"></textarea>
`})
export class InputTestComponent {
@Output() nameChange:EventEmitter<String> = new EventEmitter<String>();
@Input() name:string;
}
You can setup two-way data binding between parent and child component in the following ways:
<app-child [(counter)]="counter"></app-child>
<app-child [counter]="counter" (counterChange)="counter=$event"></app-child>
<app-child [counter]="counter" (counterChange)="onCounterChange($event)"></app-child>
According to Angular - Template Syntax - Two-way binding:
Angular offers a special two-way data binding syntax for this purpose, [(x)]. The [(x)] syntax combines the brackets of property binding, [x], with the parentheses of event binding, (x).
<app-child [(counter)]="counter"></app-child>
The [(x)] syntax is easy to demonstrate when the element has a settable property called x and a corresponding event named xChange.
@Input() counter: number;
@Output() counterChange = new EventEmitter<number>();
The two-way binding syntax is really just syntactic sugar for a property binding and an event binding. Angular desugars the ChildComponent binding into this:
<app-child [counter]="counter" (counterChange)="counter=$event"></app-child>
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div style="background-color: red; padding: 10px;">
<div>{{counter}}</div>
<button (click)="increment()">increment from parent</button>
<app-child [(counter)]="counter"></app-child>
<app-child [counter]="counter" (counterChange)="counter=$event"></app-child>
<app-child [counter]="counter" (counterChange)="onCounterChange($event)"></app-child>
</div>
`
})
export class AppComponent {
counter = 0;
increment() {
this.counter++;
}
onCounterChange(counter: number) {
this.counter = counter;
}
}
@Component({
selector: 'app-child',
template: `
<div style="background-color: green; padding: 10px; margin: 10px;">
<div>{{counter}}</div>
<button (click)="increment()">increment from child</button>
</div>
`,
})
export class ChildComponent {
@Input() counter: number;
@Output() counterChange = new EventEmitter<number>();
constructor() { }
increment() {
this.counterChange.emit(++this.counter);
}
}
You need to use input / output elements in the child component, as described below:
@Component({
selector:'input-test'
template: `
<form (ngSubmit)="onSubmit()" #testform="ngForm">
{{name}}
<textarea #textarea [(ngModel)]="name" ngControl="name" name="name"></textarea>
<button type="submit">Go</button>
</form>
`
})
export class InputTestComponent {
@Input()
name:string;
@Output()
nameChange:EventEmitter<string> = new EventEmitter();
}
When a change is detected, you need to fire an event using the EventEmitter:
onSubmit() {
this.nameChange.emit(this.name);
}
This way the bound element of the parent component will be automatically updated when using the following syntax:
<input-test [(name)]="name"></input-test>
You can notice that you can leverage, the ngModelChange event if you want to detect input change instead of using form submission:
@Component({
selector:'input-test'
template: `
<form #testform="ngForm">
{{name}}
<textarea #textarea [ngModel]="name" (ngModelChange)="onChange($event)" ngControl="name" name="name"></textarea>
</form>
`
})
export class InputTestComponent {
@Input()
name:string;
@Output()
nameChange:EventEmitter<string> = new EventEmitter();
onChange(newName) {
this.name = newName;
this.nameChange.emit(this.name);
}
}