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
EventEmitter
pour n'importe quoi mais@Output()
est déconseillé. Voir aussi stackoverflow.com/questions/34376854/…Voici un exemple tiré de la documentation Angular2 de la façon dont vous pouvez créer et utiliser vos propres observables:
Le service
Le composant
Un exemple complet et fonctionnel peut être trouvé ici: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
la source
Je voudrais ajouter que si l'objet créé est statique et ne passe pas par http, quelque chose comme ça peut être fait:
Edit: Pour Angular 7.xx, le mappage doit être effectué à l'aide de pipe () comme décrit ici ( https://stackoverflow.com/a/54085359/986160 ):
de la réponse à ma question sur les observateurs et les données statiques: https://stackoverflow.com/a/35219772/986160
la source
Je suis un peu en retard à la fête, mais je pense que mon approche a l'avantage de ne pas utiliser EventEmitters et Subjects.
Alors, voici mon approche. Nous ne pouvons pas nous passer de subscribe (), et nous ne voulons pas. Dans cette veine, notre service retournera un
Observable<T>
avec un observateur qui a notre précieuse cargaison. De l'appelant, nous initialiserons une variableObservable<T>
, et il obtiendra le serviceObservable<T>
. Ensuite, nous nous abonnerons à cet objet. Enfin, vous obtenez votre "T"! de votre service.Premièrement, notre service aux personnes, mais le vôtre ne passe pas de paramètres, c'est plus réaliste:
Ok, comme vous pouvez le voir, nous retournons un
Observable
type "personnes". La signature de la méthode le dit même! Nous rentrons l'_people
objet dans notre observateur. Nous accèderons à ce type depuis notre appelant dans le composant, ensuite!Dans le composant:
Nous initialisons notre
_peopleObservable
en renvoyant celuiObservable<people>
de notrePeopleService
. Ensuite, nous souscrivons à cette propriété. Enfin, nous définissonsthis.people
notrepeople
réponse data ( ).L'architecture du service de cette manière présente un avantage majeur par rapport au service typique: la carte (...) et le composant: le modèle "s'abonner (...)". Dans le monde réel, nous devons mapper le json à nos propriétés dans notre classe et, parfois, nous y faisons des choses personnalisées. Cette cartographie peut donc se produire dans notre service. Et, généralement, parce que notre appel de service ne sera pas utilisé une fois, mais probablement à d'autres endroits de notre code, nous n'avons pas à effectuer ce mappage dans certains composants, à nouveau. De plus, que se passerait-il si nous ajoutions un nouveau champ aux personnes? ....
la source
Dans le fichier service.ts -
une. importation «de» à partir d'observable / de
b. créer une liste json
c. retourne l'objet json en utilisant Observable.of ()
Ex. -
Dans le composant où nous appelons la fonction get du service -
la source
Notez que vous utilisez Observable # map pour convertir l'
Response
objet brut émis par votre Observable de base en une représentation analysée de la réponse JSON.Si je vous ai bien compris, vous voulez
map
encore. Mais cette fois, convertir ce JSON brut en instances de votreModel
. Donc vous feriez quelque chose comme:Donc, vous avez commencé avec un observable qui émet un
Response
objet , vous l'avez transformé en observable qui émet un objet du JSON analysé de cette réponse, puis vous l'avez transformé en un autre observable qui a transformé ce JSON brut en un tableau de vos modèles.la source