W Angular 1.xx po prostu prosisz o tę samą usługę i otrzymujesz tę samą instancję, umożliwiając udostępnianie danych w usłudze.
Teraz w Angular 2 mam komponent, który ma odniesienie do mojej usługi. Potrafię czytać i modyfikować dane w serwisie, co jest dobre. Kiedy próbuję wstrzyknąć tę samą usługę w innym komponencie, wydaje się, że otrzymuję nową instancję.
Co ja robię źle? Czy sam wzorzec jest nieprawidłowy (używanie usługi do udostępniania danych), czy też muszę oznaczyć usługę jako singleton (w ramach jednej instancji aplikacji), czy coś?
Tak przy 2.0.0-alpha.27/
okazji
Wstrzykuję usługę przez appInjector
(edit: now providers
) w @Component
adnotacji, a następnie zapisuję odwołanie w konstruktorze. Działa lokalnie w komponencie - ale nie między komponentami (nie współużytkują tego samego wystąpienia usługi), jak myślałem, że będą.
AKTUALIZACJA : Od Angular 2.0.0 mamy teraz @ngModule, w którym można zdefiniować usługę w ramach wspomnianej providers
właściwości @ngModule
. Zapewni to przekazanie tego samego wystąpienia tej usługi do każdego komponentu, usługi itp. W tym module.
https://angular.io/docs/ts/latest/guide/ngmodule.html#providers
AKTUALIZACJA : Generalnie wiele się wydarzyło w rozwoju Angulara i FE. Jak wspomniał @noririco, możesz również użyć systemu zarządzania stanem, takiego jak NgRx: https://ngrx.io/
źródło
Odpowiedzi:
Serwisowy singleton to fajne rozwiązanie. W inny sposób -
data/events bindings
.Oto przykład obu:
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);
Oglądać na żywo
źródło
viewInjector
.providers: [MyService]
. Po usunięciu dostawców stała się jedyną instancją aplikacjiKomentarz @maufarinelli zasługuje na własną odpowiedź, ponieważ dopóki go nie zobaczyłem, wciąż waliłem głową o ścianę tym problemem, nawet z odpowiedzią @Alexander Ermolov.
Problem polega na tym, że po dodaniu pliku
providers
docomponent
:@Component({ selector: 'my-selector', providers: [MyService], template: `<div>stuff</div>` })
Powoduje to, że nowe wystąpienie Twojej usługi zostanie wstrzyknięte ... zamiast być pojedynczym .
Więc usuń wszystkie wystąpienia
providers: [MyService]
w swojej aplikacji z wyjątkiemmodule
i to zadziała!źródło
Musisz użyć wejść i wyjść dekoratora @Component. Oto najbardziej podstawowy przykład użycia obu;
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);
źródło
ngFor
,W szablonie komponentu nadrzędnego:
<hero-child [hero]="hero"> </hero-child>
W komponencie podrzędnym:
źródło
Jest wiele sposobów. Ten jest przykładem wykorzystującym propagację między elementami nadrzędnymi i podrzędnymi. To jest bardzo wydajne.
Przedstawiłem przykład, który pozwala zobaczyć użycie dwóch sposobów wiązania danych w dwóch formularzach. Jeśli ktoś może dostarczyć próbkę plunkr, byłoby to bardzo miłe ;-)
Możesz poszukać innego sposobu, korzystając z usługodawcy. Możesz również obejrzeć ten film w celach informacyjnych: ( Udostępnianie danych między komponentami w Angular )
mymodel.ts (dane do udostępnienia)
// 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; } }
Pamiętaj: musi istnieć rodzic, który będzie współdzielił „mymodel” z komponentami podrzędnymi.
Komponent nadrzędny
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() { } }
Komponent podrzędny, 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() { } }
Uwaga: w rzadkich przypadkach może wystąpić błąd podczas analizowania kodu HTML, ponieważ model nie jest „gotowy” do użycia podczas inicjalizacji strony. W takim przypadku poprzedź kod HTML warunkiem ngIf:
<div *ngIf="model"> {{model.data1}} </div>
źródło
To zależy, czy istnieje prosty przypadek
a) A -> B -> C A ma dwa potomne B i C i jeśli chcesz udostępniać dane między A i B lub A i C, użyj (wejście / wyjście)
Jeśli chcesz udostępniać pliki między B i C, możesz również użyć (wejście / wyjście), ale zaleca się użycie usługi.
b) Jeśli drzewo jest duże i złożone. (jeśli jest tak wiele poziomów połączeń rodziców i dzieci). W tym przypadku, jeśli chcesz udostępniać dane, sugerowałbym ngrx
Implementuje architekturę flux, która tworzy magazyn po stronie klienta, do którego może subskrybować dowolny komponent i który może aktualizować bez tworzenia warunków wyścigu.
źródło