UPDATE: 24.09.16 Angular 2.0 Stable
Diese Frage hat immer noch viel Verkehr, deshalb wollte ich sie aktualisieren. Mit dem Wahnsinn der Änderungen von Alpha, Beta und 7 RC-Kandidaten hörte ich auf, meine SO-Antworten zu aktualisieren, bis sie stabil waren.
Dies ist der perfekte Fall für die Verwendung von Subjects und ReplaySubjects
Ich persönlich bevorzuge die Verwendung, ReplaySubject(1)
da dadurch der zuletzt gespeicherte Wert übergeben werden kann, wenn neue Abonnenten auch dann zu spät anhängen:
let project = new ReplaySubject(1);
//subscribe
project.subscribe(result => console.log('Subscription Streaming:', result));
http.get('path/to/whatever/projects/1234').subscribe(result => {
//push onto subject
project.next(result));
//add delayed subscription AFTER loaded
setTimeout(()=> project.subscribe(result => console.log('Delayed Stream:', result)), 3000);
});
//Output
//Subscription Streaming: 1234
//*After load and delay*
//Delayed Stream: 1234
Selbst wenn ich zu spät anhänge oder später laden muss, kann ich immer den neuesten Anruf erhalten und muss mir keine Sorgen machen, dass der Rückruf verpasst wird.
Auf diese Weise können Sie auch denselben Stream verwenden, um auf Folgendes zuzugreifen:
project.next(5678);
//output
//Subscription Streaming: 5678
Aber was ist, wenn Sie zu 100% sicher sind, dass Sie den Anruf nur einmal tätigen müssen? Offene Themen und Observablen zu lassen ist nicht gut, aber es gibt immer das "Was wäre wenn?"
Das ist , wo AsyncSubject kommt.
let project = new AsyncSubject();
//subscribe
project.subscribe(result => console.log('Subscription Streaming:', result),
err => console.log(err),
() => console.log('Completed'));
http.get('path/to/whatever/projects/1234').subscribe(result => {
//push onto subject and complete
project.next(result));
project.complete();
//add a subscription even though completed
setTimeout(() => project.subscribe(project => console.log('Delayed Sub:', project)), 2000);
});
//Output
//Subscription Streaming: 1234
//Completed
//*After delay and completed*
//Delayed Sub: 1234
Genial! Obwohl wir das Thema geschlossen haben, antwortete es immer noch mit dem letzten geladenen Element.
Eine andere Sache ist, wie wir diesen http-Aufruf abonniert und die Antwort verarbeitet haben. Map ist großartig, um die Antwort zu verarbeiten.
public call = http.get(whatever).map(res => res.json())
Aber was wäre, wenn wir diese Anrufe verschachteln müssten? Ja, Sie könnten Themen mit einer speziellen Funktion verwenden:
getThing() {
resultSubject = new ReplaySubject(1);
http.get('path').subscribe(result1 => {
http.get('other/path/' + result1).get.subscribe(response2 => {
http.get('another/' + response2).subscribe(res3 => resultSubject.next(res3))
})
})
return resultSubject;
}
var myThing = getThing();
Aber das ist viel und bedeutet, dass Sie eine Funktion benötigen, um dies zu tun. Geben Sie FlatMap ein :
var myThing = http.get('path').flatMap(result1 =>
http.get('other/' + result1).flatMap(response2 =>
http.get('another/' + response2)));
Süß, das var
ist ein Observable, das die Daten vom letzten http-Aufruf erhält.
OK, das ist großartig, aber ich möchte einen Angular2-Service!
Ich habe dich:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { ReplaySubject } from 'rxjs';
@Injectable()
export class ProjectService {
public activeProject:ReplaySubject<any> = new ReplaySubject(1);
constructor(private http: Http) {}
//load the project
public load(projectId) {
console.log('Loading Project:' + projectId, Date.now());
this.http.get('/projects/' + projectId).subscribe(res => this.activeProject.next(res));
return this.activeProject;
}
}
//component
@Component({
selector: 'nav',
template: `<div>{{project?.name}}<a (click)="load('1234')">Load 1234</a></div>`
})
export class navComponent implements OnInit {
public project:any;
constructor(private projectService:ProjectService) {}
ngOnInit() {
this.projectService.activeProject.subscribe(active => this.project = active);
}
public load(projectId:string) {
this.projectService.load(projectId);
}
}
Ich bin ein großer Fan von Beobachtern und Observablen, also hoffe ich, dass dieses Update hilft!
Ursprüngliche Antwort
Ich denke, dies ist ein Anwendungsfall für die Verwendung eines beobachtbaren Subjekts oder in Angular2
der EventEmitter
.
In Ihrem Dienst erstellen Sie eine EventEmitter
, mit der Sie Werte darauf übertragen können. In Alpha 45 müssen Sie es konvertieren toRx()
, aber ich weiß, dass sie daran gearbeitet haben, dies zu beseitigen. In Alpha 46 können Sie das möglicherweise einfach zurückgeben EvenEmitter
.
class EventService {
_emitter: EventEmitter = new EventEmitter();
rxEmitter: any;
constructor() {
this.rxEmitter = this._emitter.toRx();
}
doSomething(data){
this.rxEmitter.next(data);
}
}
Auf diese Weise haben Sie die Single EventEmitter
, auf die Ihre verschiedenen Servicefunktionen jetzt zugreifen können.
Wenn Sie ein Observable direkt von einem Anruf zurückgeben möchten, können Sie Folgendes tun:
myHttpCall(path) {
return Observable.create(observer => {
http.get(path).map(res => res.json()).subscribe((result) => {
//do something with result.
var newResultArray = mySpecialArrayFunction(result);
observer.next(newResultArray);
//call complete if you want to close this stream (like a promise)
observer.complete();
});
});
}
Damit können Sie dies in der Komponente tun:
peopleService.myHttpCall('path').subscribe(people => this.people = people);
Und verwirren Sie die Ergebnisse des Anrufs in Ihrem Dienst.
Ich mag es, den EventEmitter
Stream selbst zu erstellen, falls ich von anderen Komponenten aus darauf zugreifen muss, aber ich konnte sehen, dass beide Möglichkeiten funktionieren ...
Hier ist ein Plunker, der einen Basisdienst mit einem Ereignisemitter zeigt: Plunkr