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);

Relaterte artikler