16

I am trying to make something work on angular2 and I am unable to find something about this behavior.

I have an application that implements a custom component like this one :

import {Component,Input} from 'angular2/core'
    @Component({
      selector:'my-comp',
      template:`<input type="text" style="text-align:center; [(ngModel)]="inputText"> <p>{{inputText}}</p>`
    })

    export class MyComp{
      @Input() inputText : string;
    }

And I am trying to do a bidirectional databinding on my inputText variable from my component like this:

<my-comp [(inputText)]="testString"></my-comp>

Where the testString is a variable defined in the MyApp.ts which contains a string. I want my testString variable to be modified when my inputText is modified by the user.

Here is a Plunker with a simple sample code : https://plnkr.co/edit/zQiCQ3hxSSjCmhWJMJph?p=preview

Is there a way to make this works simply ? Do I have to implements an Angular2 class on my custom components and overload functions in order to make this works like an ngModel ? Do i necessarily have to create a inputTextChanged variable of EventEmitter type that emit my data when it's changed and do something like this :

<my-comp [inputText]="testString" (inputTextChanged)="testString = $event;"></my-comp>

Thank you in advance.

4
  • 3
    Yes you have to create an event emitter "inputTextChanged", and fire the event inside of your component. Then [(inputText)] should work as expected. Commented Jan 5, 2016 at 10:03
  • As a notice: there is no such thing as bidirectional data-binding in Angular2, but you can have a similar behaviour. victorsavkin.com/post/110170125256/… Commented Jan 5, 2016 at 12:59
  • Well according to the Angular2 Cheat Sheet from the official website : " <my-cmp [(title)]="name"> Sets up two-way data binding. Equivalent to: <my-cmp [title]="name" (titleChange)="name=$event">". So i think we can say that there is bidirectional data binding in Angular2. Commented Jan 5, 2016 at 13:29
  • 1
    @jornare, I think you/Savkin and David are both correct. I believe Savkin is trying to make the point that there is no code in Angualr 2 to handle two-way data binding. I.e., [(something)] is just syntactic sugar for one property binding and one event binding. So Angular does not treat [(something)] as one two-way databinding, but rather as two one-way data bindings. Commented Jan 5, 2016 at 15:45

4 Answers 4

32

This is explained in the Template Syntax doc, in the Two-Way Binding with NgModel section:

<input [(ngModel)]="currentHero.firstName">

Internally, Angular maps the term, ngModel, to an ngModel input property and an ngModelChange output property. That’s a specific example of a more general pattern in which it matches [(x)] to an x input property for Property Binding and an xChange output property for Event Binding.

We can write our own two-way binding directive/component that follows this pattern if we're ever in the mood to do so.

Note also that [(x)] is just syntactic sugar for a property binding and an event binding:

[x]="someParentProperty" (xChange)="someParentProperty=$event"

In your case, you want

<my-comp [(inputText)]="testString"></my-comp>

so your component must have an inputText input property and an inputTextChange output property (which is an EventEmitter).

export class MyComp {
  @Input()  inputText: string;
  @Output() inputTextChange: EventEmitter<string> = new EventEmitter();
}

To notify the parent of changes, whenever your component changes the value of inputText, emit an event:

inputTextChange.emit(newValue);

In your scenario, the MyComp component binds input property inputText using the [(x)] format to ngModel, so you used event binding (ngModelChange) to be notified of changes, and in that event handler you notified the parent component of the change.

In other scenarios where ngModel isn't used, the important thing is to emit() an event whenever the value of property inputText changes in the MyComp component.

Sign up to request clarification or add additional context in comments.

1 Comment

Update Angular4: In the child component it is no longer necessary to specify the EventEmitter output. This is handled by @input automatically. Parent view: <my-comp [(inputText)]="testString"></my-comp> Child component: export class MyComp { @Input() inputText: string; }
8

I'll combine @pixelbits and @Günter Zöchbauer answers and comments to make a clear answer on my question if someone in the future is searching for this.

To make bidirectional data binding works on custom variables you need to creates your component based on the following.

MyComp.ts file :

import {Component,Input,Output,EventEmitter} from 'angular2/core'
@Component({
  selector:'my-comp',
  templateUrl:`<input type="text" style="text-align:center;"
     [ngModel]="inputText" (ngModelChange)="inputText=$event;inputTextChange.emit($event);">`
})

export class MyComp{
  @Input() inputText : string;
  @Output() inputTextChange = new  EventEmitter();
}

MyApp.ts file:

import {Component} from 'angular2/core'
import {MyComp} from './MyComp'

@Component({
  selector:'my-app',
  templateUrl:`<h1>Bidirectionnal Binding test </h1>
    <my-comp [(inputText)]="testString"></my-comp><p>
    <b>My Test String :</b> {{testString}}</p>`,
  directives:[MyComp]
})

export class MyApp{
  testString : string;
  constructor(){
    this.testString = "This is a test string";
  }
}

There the bidirectional data binding to the inputText variable works correctly. You can comment the answer for a more beautiful or simpler way to implement this code.

Comments

1

Your Plunker already contains the EventEmitter. The @Output() annotation is missing. To change the value call inputTextChanged.emit(newValue) (this also changes the value on inputText)

3 Comments

Yes Sorry, I am new to plunker and forgot to lock the plunker to the state i wanted you to see and continued my test on it so you saw my modifications.
Ho I just remembered and i don't know if you know it but at least from the beta version of Angular2 ".next()" is deprecated and you should use ".emit()".
Thanks! haven't seen that.
-1

What I do is use a property, so when I change the data the change is emitted automatically

private _data: AnyType;
@Input()  get data(): AnyType{
    return this._data;
}
set data(value: AnyType) {
    this._data = value;
    this.dataChange.emit(this._data);
}
@Output() dataChange: EventEmitter<AnyType> = new EventEmitter();

In html you will bind the property using [(data)]

<comp [(data)]="getData()"></comp>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.