plone1
plone1

Reputation: 676

Angular2 : two way binding inside parent/child component

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) ?

Upvotes: 33

Views: 44955

Answers (6)

WantToDo
WantToDo

Reputation: 441

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

Upvotes: 0

Hassen Tayech
Hassen Tayech

Reputation: 51

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)

Upvotes: 0

Milan Hlin&#225;k
Milan Hlin&#225;k

Reputation: 4430

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

}

Upvotes: 11

aryan
aryan

Reputation: 115

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

Upvotes: 2

Thierry Templier
Thierry Templier

Reputation: 202176

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

Upvotes: 2

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657288

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

Upvotes: 54

Related Questions