typescript - Hvordan deler jeg data mellom komponenter i Angular 2?
I Angular 1.x.x spør du enkelt etter den samme tjenesten, og du ender opp med den samme instansen, noe som gjør det mulig å dele dataene i tjenesten.
Nå i Angular 2 har jeg en komponent som har en referanse til tjenesten min. Jeg kan lese og endre dataene i tjenesten, noe som er bra. Når jeg prøver å injisere den samme tjenesten i en annen komponent, ser det ut som om jeg får en ny instans.
Hva gjør jeg galt? Er det selve mønsteret som er feil (å bruke en tjeneste for å dele data), eller må jeg markere tjenesten som en singleton (innenfor én instans av appen) eller noe?
Jeg er på 2.0.0-alpha.27/ forresten
Jeg injiserer en tjeneste gjennom appInjector (redigert: nå providers) i @Component-annotasjonen og lagrer deretter en referanse i konstruktøren. Det fungerer lokalt i komponenten - bare ikke på tvers av komponenter (de deler ikke samme tjenesteinstans) slik jeg trodde de ville.
OPPDATERING: Fra Angular 2.0.0 har vi nå @ngModule hvor du kan definere tjenesten under providers-egenskapen på nevnte @ngModule. Dette vil sikre at den samme instansen av tjenesten blir sendt til hver komponent, tjeneste osv. i det modulen. https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
OPPDATERING: Mye har skjedd med Angular og frontend-utvikling generelt. Som @noririco nevnte, kan du også bruke et tilstandsstyringssystem som NgRx: https://ngrx.io/
Svar #1
En tjeneste-singleton er en fin løsning. Andre måte - data/events-bindinger.
Her er et eksempel på begge:
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);
Se live
Svar #2
Kommentaren fra @maufarinelli fortjener sitt eget svar fordi inntil jeg så den, slo jeg fortsatt hodet mitt mot veggen med dette problemet selv med @Alexander Ermolovs svar.
Problemet er at når du legger til en providers i din component:
@Component({
selector: 'my-selector',
providers: [MyService],
template: `<div>stuff</div>`
})
Dette forårsaker at en ny instans av tjenesten din blir injisert... i stedet for å være en singleton.
Så fjern alle forekomster av din providers: [MyService] i applikasjonen din unntatt i module, så vil det fungere!
Svar #3
Du må bruke inputs og outputs på en @Component-dekoratør. Her er det mest grunnleggende eksempelet på å bruke begge;
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);
