MISE À JOUR: 9/24/16 Angular 2.0 Stable
Cette question génère encore beaucoup de trafic, alors je voulais la mettre à jour. Avec la folie des changements apportés par les candidats Alpha, Beta et 7 RC, j'ai arrêté de mettre à jour mes réponses SO jusqu'à ce qu'elles se stabilisent.
C'est le cas parfait pour utiliser des sujets et des replaySubjects
Personnellement, je préfère utiliser ReplaySubject(1)
car il permet à la dernière valeur stockée d'être transmise lorsque de nouveaux abonnés se connectent même en retard:
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
Ainsi, même si je me connecte en retard ou que je dois charger plus tard, je peux toujours recevoir le dernier appel et ne pas craindre de manquer le rappel.
Cela vous permet également d'utiliser le même flux pour pousser vers le bas:
project.next(5678);
//output
//Subscription Streaming: 5678
Mais que faire si vous êtes sûr à 100% que vous n'avez besoin de faire l'appel qu'une seule fois? Laisser les sujets ouverts et les observables n'est pas bon mais il y a toujours ce "Et si?"
C'est là qu'AsyncSubject entre en jeu.
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
Impressionnant! Même si nous avons fermé le sujet, il a quand même répondu avec la dernière chose chargée.
Une autre chose est la façon dont nous nous sommes abonnés à cet appel http et avons géré la réponse. La carte est idéale pour traiter la réponse.
public call = http.get(whatever).map(res => res.json())
Mais que se passerait-il si nous devions imbriquer ces appels? Oui, vous pouvez utiliser des sujets avec une fonction spéciale:
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();
Mais c'est beaucoup et cela signifie que vous avez besoin d'une fonction pour le faire. Entrez FlatMap :
var myThing = http.get('path').flatMap(result1 =>
http.get('other/' + result1).flatMap(response2 =>
http.get('another/' + response2)));
Sweet, le var
est un observable qui obtient les données de l'appel http final.
OK c'est super mais je veux un service angular2!
Je vous ai compris:
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);
}
}
Je suis un grand fan d'observateurs et d'observables alors j'espère que cette mise à jour vous aidera!
Réponse originale
Je pense que c'est un cas d'utilisation d'utilisation d'un sujet observable ou dans Angular2
leEventEmitter
.
Dans votre service, vous créez un EventEmitter
qui vous permet d'y pousser des valeurs. Dans Alpha 45, vous devez le convertir avec toRx()
, mais je sais qu'ils travaillaient pour s'en débarrasser, donc dans Alpha 46, vous pourrez peut-être simplement renvoyer le fichier EvenEmitter
.
class EventService {
_emitter: EventEmitter = new EventEmitter();
rxEmitter: any;
constructor() {
this.rxEmitter = this._emitter.toRx();
}
doSomething(data){
this.rxEmitter.next(data);
}
}
De cette façon, EventEmitter
vos différentes fonctions de service peuvent désormais pousser.
Si vous souhaitez renvoyer une observable directement à partir d'un appel, vous pouvez faire quelque chose comme ceci:
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();
});
});
}
Cela vous permettrait de faire cela dans le composant:
peopleService.myHttpCall('path').subscribe(people => this.people = people);
Et gâchez les résultats de l'appel dans votre service.
J'aime créer le EventEmitter
flux seul au cas où j'aurais besoin d'y accéder à partir d'autres composants, mais je pouvais voir les deux façons fonctionner ...
Voici un plunker qui montre un service de base avec un émetteur d'événements: Plunkr