Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use two way data binding between components in angular 2?

Tags:

First I've created a User class:

export class User {   name: string;   email: string; } 

Then I've got my CoreComponent which uses the FormInputComponent as well as creating a public user from the User class:

import {Component} from 'angular2/core'; import {FormInputComponent} from '../form-controls/form-input/form-input.component'; import {User} from '../core/user';  @Component({   selector: 'core-app',   templateUrl: './app/assets/scripts/modules/core/core.component.html',   styleUrls: ['./app/assets/scripts/modules/core/core.component.css'],   directives: [FormInputComponent] })  export class CoreComponent {   public user: User = {     name: '',     email: ''   } } 

Then I've created an input component, which is a re-useable input component that will take a model value as an input and when changes are made export the new value so that CoreComponent can update the model with the new value:

import {Component, Input, Output, EventEmitter, DoCheck} from 'angular2/core';  @Component({   selector: 'form-input',   templateUrl: './app/assets/scripts/modules/form-controls/form-input/form-input.component.html',   styleUrls: ['./app/assets/scripts/modules/form-controls/form-input/form-input.component.css'],   inputs: [     'model',     'type',     'alt',     'placeholder',     'name',     'label'   ] })  export class FormInputComponent implements DoCheck {   @Input() model: string;   @Output() modelExport: EventEmitter = new EventEmitter();    ngDoCheck() {     this.modelExport.next(this.model);   } } 

The CoreComponent's template uses two FormInputComponents and passes user.name and user.email as the input for them:

<form-input [model]="user.name" type="text" name="test" placeholder="This is a test" alt="A test input" label="Name"></form-input> <form-input [model]="user.email" type="email" name="test" placeholder="This is a test" alt="A test input" label="Email"></form-input> <pre>{{user.name}}</pre> 

The FormInputComponent template:

<div>   <label attr.for="{{name}}">{{label}}</label>   <input [(ngModel)]="model" type="{{type}}" placeholder="{{placeholder}}" alt="{{alt}}" id="{{name}}"> </div> <pre>{{model}}</pre> 

Now the problem is that I can only see the changes from the pre element that lies inside the FormInputComponent template, but the parent, CoreComponent's pre element remains unchanged.

I looked at this question which is in the ballpark of what I want to achieve but not quite since using a service for just returning a value up the hierarchy seems like overkill and a bit messy if you have multiple FormInputComponents on the same page.

So my question is simple, how can I pass a model to FormInputComponent and letting it return a new value whenever the value changes so that the public user in CoreComponent changes automatically?

like image 999
Chrillewoodz Avatar asked Mar 27 '16 11:03

Chrillewoodz


People also ask

Is angular 2 support two way data binding only?

Angular v2+ supports two-way data binding using ngModel directive and also by having getter and setter methods.

How do I bind data in angular 2?

Two-way binding was a functionality in Angular JS, but has been removed from Angular 2. x onwards. But now, since the event of classes in Angular 2, we can bind to properties in AngularJS class. Suppose if you had a class with a class name, a property which had a type and value.

Which module is needed to implement two way data binding?

Note: For two way data binding, we have to enable the ngModel directive. It depends upon FormsModule in angular/forms package, so we have to add FormsModule in imports[] array in the AppModule.

What is one-way data binding and two way data binding in Angular?

Difference between One-way & Two-way Binding This means that the flow of code is from ts file to Html file as well as from Html file to ts file. In order to achieve one-way binding, we used the property binding concept in Angular. In order to achieve a two-way binding, we will use ngModel or banana in a box syntax.

How to bind two-way data in angular?

You can see that the former is property binding where as the latter is event binding and the combination of both results in two-way binding in Angular. Syntax for two-way data binding in Angular is [ ()]. The [ ()] syntax combines the brackets of property binding, [], with the parentheses of event binding, ().

What is the [ () ] syntax in Angular 2?

The [ ()] syntax combines the brackets of property binding, [], with the parentheses of event binding, (). This syntax for two-way binding is also known as banana in a box. [ ( )] = BANANA IN A BOX It is just a visual way to remember that the parentheses go inside the brackets. In Angular ngModel directive is used for two-way bindings.

How to use ngmodel directive in two-way data binding?

Also any change in the value done by the user in the view changes the bound property value in the component too. For using the ngModel directive in a two-way data binding, you must do these two things- import { FormsModule } from '@angular/forms'; .. ..

What does two-way data binding do?

Two-way data binding performs the following actions: 1 Sets a property of a component class 2 Listens for a DOM element change event More ...


2 Answers

To be able to use two way binding short when using your component you need to readme your output property to modelChange:

export class FormInputComponent implements DoCheck {   @Input() model: string;   @Output() modelChange: EventEmitter = new EventEmitter();    ngDoCheck() {     this.modelChange.next(this.model);   } } 

And use it this way:

<form-input [(model)]="user.name" type="text" name="test" placeholder="This is a test" alt="A test input" label="Name"></form-input> <form-input [(model)]="user.email" type="email" name="test" placeholder="This is a test" alt="A test input" label="Email"></form-input> <pre>{{user.name}}</pre> 
like image 185
Thierry Templier Avatar answered Sep 28 '22 09:09

Thierry Templier


As an addition: Using ngDoCheck to emit model changes will DRASTICALLY affect performance as the new value gets emited on every check cycle, no matter if it was changed or not. And that is really often! (Try a console.log!)

So what I like to do to get a comparable convenience, but without the side effects, is something like this:

private currentSelectedItem: MachineItem; @Output() selectedItemChange: EventEmitter<MachineItem> = new EventEmitter<MachineItem>();  @Input() set selectedItem(machineItem: MachineItem) {     this.currentSelectedItem = machineItem;     this.selectedItemChange.emit(machineItem);  }  get selectedItem(): MachineItem {     return this.currentSelectedItem;  } 

And use it like

<admin-item-list [(selectedItem)]="selectedItem"></admin-item-list> 

You can also emit the new value where it is actually changed. But I find it quite convenient to do that gloabaly in a setter method and don't have to bother when I bind it directly in my view.

like image 23
Mario Eis Avatar answered Sep 28 '22 08:09

Mario Eis