tyrion
tyrion

Reputation: 742

Angular2 decorator for 2 way property binding

From Victor Savkin's post on Angular2 template syntax, shows how to use input and output property binding -

<todo-cmp [model]="todo" (complete)="onCompletingTodo(todo)"></todo-cmp>

@Component({selector: 'todo-cmp'})
class TodoCmp {
  @Input() model;
  @Output() complete = new EventEmitter(); // TypeScript supports initializing fields
}

The input property is decorated with @Input() while output property has @Output(). How should I declare a property which is going to have a 2 way property binding? Example: Assuming rootpanel component has 'suggestions' property (of type string) and searchPanel has 'getSuggestions property. Now I want the two properties to be bound to each other both ways. I tried -

rootpanel.html:

<search-panel [(getSuggestions)]="suggestions"> </search-panel>

But I am stuck while declaring the getSuggestions property in the searchPanel component. Also what should be the type of the getSuggestions property - string or EventEmitter<string>?

Please suggest.

Upvotes: 3

Views: 1271

Answers (3)

Uifalean Sergiu Mihai
Uifalean Sergiu Mihai

Reputation: 60

The approach pixelbits recommended is exactly how you should do that, but if you have multiple two-way data binding properties on a component, or even one that changes frequently in your codebase, I created a decorator for that. If you are using npm here it is. Just go to the gihub page if you need the code.

With this, you can directly use:

import { Component } from '@angular/core';
import { TwoWay } from 'two-way-decorator';
 
@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
 
  @TwoWay()
  public text: string;
 
  @TwoWay()
  public count: number;
 
}

Upvotes: 0

Michael Kang
Michael Kang

Reputation: 52837

If you want two-way model binding from the parent component:

[(model)]

You need the following in your child component:

@Input() model: string;
@Output() modelChange:EventEmitter<string>;

At some point when the model is overwritten in your child component, you'll emit the modelChange event:

updateModel(newValue:string) {
    this.model = newValue;
    this.modelChange.emit(this.model);
}

From the parent component's perspective, [(model)] is equivalent to:

[model]="model"  (modelChange)="model=$event"

In this way, when the model property changes inside a child component, the change in the model propagates upwards though two-way binding, synching all the bound models along the way.

Upvotes: 4

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

Reputation: 657018

If you want to use [(getSuggestions)]-style for two-way-binding declare the fields like

class TodoCmp {
  @Input() getSuggestions;
  @Output() getSuggestionsChange = new EventEmitter(); 

  onClick() {
    getSuggestions = 'someValue';
    getSuggestionsChange.emit(getSuggestions);
  }
}

getSuggestions is probably not a good choice for such a input/output combination but it should demonstrate how they are connected. The output needs to have the same name as the input with an additional Change. If this naming scheme doesn't fit use your component like

<search-panel [suggestions]="suggestions" (getSuggestions)="updateSuggestions($event)> </search-panel>

with input/output like

class TodoCmp {
  @Input() suggestions;
  @Output() getSuggestions = new EventEmitter(); 

  onClick() {
    suggestions = 'someValue';
    getSuggestions.emit(getSuggestions);
  }
}

Upvotes: 2

Related Questions