Как обмен данными между компонентами в Angular 2?
В Angular 1.x.x вы просто запрашиваете одну и ту же услугу, и в итоге вы получаете один и тот же экземпляр, что позволяет совместно использовать данные в службе.
Теперь в Angular 2 у меня есть компонент, который имеет ссылку на мою службу. Я могу читать и изменять данные в сервисе, что хорошо. Когда я пытаюсь ввести одну и ту же услугу в другой компонент, кажется, что я получаю новый экземпляр.
Что я делаю неправильно? Является ли этот шаблон неправильным (с использованием службы для обмена данными) или мне нужно отметить службу как одноэлемент (в одном экземпляре приложения) или что-то еще?
Я нахожусь на 2.0.0-alpha.27/
btw
Я вставляю службу через appInjector
(edit: now providers
) в аннотацию @Component
, а затем сохраняю ссылку в конструкторе. Он работает локально в компоненте - только не через компоненты (они не используют один и тот же экземпляр службы), как я думал, они будут.
ОБНОВЛЕНИЕ: Начиная с Angular 2.0.0, теперь мы имеем @ngModule, где вы определяете сервис под свойством providers
в указанном @ngModule
. Это обеспечит передачу одного и того же экземпляра этой услуги каждому компоненту, службе и т.д. В этом модуле.
https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
Ответы
Ответ 1
Сервис Singleton - хорошее решение. Другой способ - data/events bindings
.
Вот пример обоих:
class BazService{
n: number = 0;
inc(){
this.n++;
}
}
@Component({
selector: 'foo'
})
@View({
template: '<button (click)="foobaz.inc()">Foo {{ foobaz.n }}</button>'
})
class FooComponent{
constructor(foobaz: BazService){
this.foobaz = foobaz;
}
}
@Component({
selector: 'bar',
properties: ['prop']
})
@View({
template: '<button (click)="barbaz.inc()">Bar {{ barbaz.n }}, Foo {{ prop.foobaz.n }}</button>'
})
class BarComponent{
constructor(barbaz: BazService){
this.barbaz = barbaz;
}
}
@Component({
selector: 'app',
viewInjector: [BazService]
})
@View({
template: '
<foo #f></foo>
<bar [prop]="f"></bar>
',
directives: [FooComponent, BarComponent]
})
class AppComponent{}
bootstrap(AppComponent);
Смотреть вживую
Ответ 2
Комментарий @maufarinelli заслуживает собственного ответа, потому что, пока я его не увидел, я все еще пробивал себе голову против стены этой проблемой, даже с ответом @Alexander Ermolov.
Проблема заключается в том, что при добавлении providers
к вашему component
:
@Component({
selector: 'my-selector',
providers: [MyService],
template: `<div>stuff</div>`
})
Это приводит к введению нового экземпляра вашей службы... а не к singleton.
Поэтому удалите все экземпляры вашего providers: [MyService]
в приложении, кроме как в module
, и он будет работать!
Ответ 3
Вы должны использовать входы и выходы декоратора @Component. Вот наиболее простой пример использования обоих:
import { bootstrap } from 'angular2/platform/browser';
import { Component, EventEmitter } from 'angular2/core';
import { NgFor } from 'angular2/common';
@Component({
selector: 'sub-component',
inputs: ['items'],
outputs: ['onItemSelected'],
directives: [NgFor],
template: `
<div class="item" *ngFor="#item of items; #i = index">
<span>{{ item }}</span>
<button type="button" (click)="select(i)">Select</button>
</div>
`
})
class SubComponent {
onItemSelected: EventEmitter<string>;
items: string[];
constructor() {
this.onItemSelected = new EventEmitter();
}
select(i) {
this.onItemSelected.emit(this.items[i]);
}
}
@Component({
selector: 'app',
directives: [SubComponent],
template: `
<div>
<sub-component [items]="items" (onItemSelected)="itemSelected($event)">
</sub-component>
</div>
`
})
class App {
items: string[];
constructor() {
this.items = ['item1', 'item2', 'item3'];
}
itemSelected(item: string): void {
console.log('Selected item:', item);
}
}
bootstrap(App);
Ответ 4
В родительском шаблоне компонента:
<hero-child [hero]="hero">
</hero-child>
В дочернем компоненте:
@Input() hero: Hero;
Источник: https://angular.io/docs/ts/latest/cookbook/component-communication.html
Ответ 5
Есть много способов. Этот пример использует распространение между родительскими и дочерними элементами. Это очень эффективно.
Я представил пример, который позволяет просматривать использование двух способов привязки данных в двух формах. Если кто-то может предоставить образец plunkr, это было бы очень хорошо ;-)
Вы можете искать другой способ, используя поставщика услуг. Вы можете посмотреть это видео тоже для справки: (Обмен данными между компонентами в Angular)
mymodel.ts (данные для обмена)
// Some data we want to share against multiple components ...
export class mymodel {
public data1: number;
public data2: number;
constructor(
) {
this.data1 = 8;
this.data2 = 45;
}
}
Помните: должен быть родитель, который поделится "mymodel" с дочерними компонентами.
Родительский компонент
import { Component, OnInit } from '@angular/core';
import { mymodel } from './mymodel';
@Component({
selector: 'app-view',
template: '<!-- [model]="model" indicates you share model to the child component -->
<app-mychild [model]="model" >
</app-mychild>'
<!-- I add another form component in my view,
you will see two ways databinding is working :-) -->
<app-mychild [model]="model" >
</app-mychild>',
})
export class MainComponent implements OnInit {
public model: mymodel;
constructor() {
this.model = new mymodel();
}
ngOnInit() {
}
}
Дочерний компонент, mychild.component.ts
import { Component, OnInit,Input } from '@angular/core';
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
import { mymodel } from './mymodel';
@Component({
selector: 'app-mychild',
template: '
<form #myForm="ngForm">
<label>data1</label>
<input type="number" class="form-control" required id="data1 [(ngModel)]="model.data1" name="data1">
<label>val {{model.data1}}</label>
label>data2</label>
<input id="data2" class="form-control" required [(ngModel)]="model.data2" name="data2" #data2="ngModel">
<div [hidden]="data2.valid || data2.pristine"
class="alert alert-danger">
data2 is required
</div>
<label>val2 {{model.data2}}</label>
</form>
',
})
export class MychildComponent implements OnInit {
@Input() model: mymodel ; // Here keywork @Input() is very important it indicates that model is an input for child component
constructor() {
}
ngOnInit() {
}
}
Примечание. В некоторых редких случаях может возникать ошибка при разборе HTML-кода, поскольку модель не "готова" для использования при инициализации страницы. В этом случае добавьте к HTML-коду условие ngIf:
<div *ngIf="model"> {{model.data1}} </div>
Ответ 6
Это зависит, если есть простой случай
a) A → B → C A имеет два дочерних B и C, и если вы хотите обмениваться данными между A и B или и C, используйте (ввод/вывод)
Если вы хотите поделиться между B и C, то вы также можете использовать (ввод/вывод), но рекомендуется использовать Service.
б) Если дерево большое и сложное, как это, например, И в этом случае, если вы хотите поделиться данными, то я бы предложил ngrx
Он реализует архитектуру потока, которая создает хранилище на стороне клиента, на которое может подписаться любой компонент, и может обновлять его, не создавая условия гонки.