33

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>

Code sample

What the easiest way to do it (less verbose) ?

6 Answers 6

54

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;
}
Sign up to request clarification or add additional context in comments.

4 Comments

Works... you forgot to mention the need to do banana binding on the parent child property
This is not example of parent-child banana in a box.
Of course it is - <textarea> is the child and can be a custom component.
for me (with angular 6) this worked only after I specified the name of the name of the 2-way-binded prop: in component.ts: @Input('selected') selected; @Output('selectedChange') selectedChange:EventEmitter<any> = new EventEmitter<any>();, in component.html: <ng-select ... [(ngModel)]="selected" (ngModelChange)="selectedChange.emit($event)"></ng-select>, in template that uses the component: <component ... [(selected)]="formFields.okpdCode"></component>
11

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>

Example: https://stackblitz.com/edit/angular-two-way-data-binding-between-parent-and-child-component?file=src%2Fapp%2Fapp.component.ts

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);
  }

}

Comments

2

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);
  }
}

1 Comment

In your last code snippet, since you use [ngModel] instead of [(ngModel)] (and I prefer what you did), your onChange() event handler needs to update this.name when the event fires: onChange($event) then onChange(newValue) { this.name = newValue; this.nameChange.emit(this.name);}
2

Yes we can use banana property to share the data between child to parent [(propertyname)] and propertynameChange emitter event. Sample plunker available https://plnkr.co/edit/FYXv36?p=preview

Comments

0

You can use @Input & @Output to achive 2 way binding between parent and child as mentioned in previous answers.

Otherwise, there another solution, you can use a service that contains your variable, and you can access it with ngModel in the same time from different components (even if they are not parent-child)

Comments

0

you can use a reactive form control and send a parent form to a child.

parent component:

.ts

myForm: FormGroup = this.formBuilder.group({
    control_child: ['']
})

.html

<child [parentForm]="myForm"></child>

child componenet:

.ts

@Input() parentForm:FormGroup

.html

<form [formGroup]="parentForm"> 
     <input formControlName="control_child"/>
</form>

a parent component form is updated immediately

here the full code

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.