Как использовать двухстороннюю привязку данных между компонентами в angular 2?
Сначала я создал класс User
:
export class User {
name: string;
email: string;
}
Затем у меня есть мой CoreComponent
, который использует FormInputComponent
, а также создает public user
из класса User
:
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: ''
}
}
Затем я создал компонент ввода, который является повторно используемым входным компонентом, который будет принимать значение модели в качестве ввода, и когда изменения будут производиться, экспортируйте новое значение, чтобы CoreComponent
мог обновить модель с помощью нового значение:
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);
}
}
Шаблон CoreComponent
использует два FormInputComponent
и передает user.name
и user.email
в качестве входа для них:
<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>
Шаблон FormInputComponent
:
<div>
<label attr.for="{{name}}">{{label}}</label>
<input [(ngModel)]="model" type="{{type}}" placeholder="{{placeholder}}" alt="{{alt}}" id="{{name}}">
</div>
<pre>{{model}}</pre>
Теперь проблема в том, что я могу видеть только изменения из элемента pre
, который находится внутри шаблона FormInputComponent
, но элемент parent, CoreComponent
pre
не изменяется.
Я посмотрел на этот вопрос, который находится в шаге от того, чего я хочу достичь, но не совсем так, потому что, используя сервис для просто возвращающего значения, иерархия кажется overkill и немного беспорядочно, если на одной странице есть несколько FormInputComponent
.
Итак, мой вопрос прост, как передать модель в FormInputComponent
и позволить ей возвращать новое значение всякий раз, когда значение изменяется, так что public user
в CoreComponent
автоматически изменяется?
Ответы
Ответ 1
Чтобы иметь возможность использовать двустороннюю привязку коротко при использовании вашего компонента, вам нужно прочитать свое свойство вывода в modelChange:
export class FormInputComponent implements DoCheck {
@Input() model: string;
@Output() modelChange: EventEmitter = new EventEmitter();
ngDoCheck() {
this.modelChange.next(this.model);
}
}
И используйте его так:
<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>
Ответ 2
В качестве дополнения: Использование ngDoCheck для изменения изменений модели будет ДРАКЦИОННО влиять на производительность по мере того, как новое значение будет испускаться в каждом контрольном цикле, независимо от того, было ли оно изменено или нет. И это действительно часто! (Попробуйте console.log!)
Итак, что мне нравится делать, чтобы получить сопоставимое удобство, но без побочных эффектов, это примерно так:
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;
}
И используйте его как
<admin-item-list [(selectedItem)]="selectedItem"></admin-item-list>
Вы также можете испустить новое значение, где оно фактически изменено. Но мне очень удобно делать эту злободневность в методе сеттера и не нужно беспокоиться, когда я привязываю его прямо к себе.
Ответ 3
Это слишком долго, чтобы добавить комментарий к ответу Тьерри...
Используйте emit()
вместо next()
(который устарел) и вызывайте emit(newValue)
только при изменении значения model
. Скорее всего, вы хотите что-то подобное в шаблоне FormInputComponent:
<input [ngModel]="model" (ngModelChange)="onChanges($event)">
Затем в вашей логике FormInputComponent:
onChanges(newValue) {
this.model = newValue;
this.changeModel.emit(newValue);
}