ACTUALIZACIÓN: 9/24/16 Angular 2.0 Estable
Esta pregunta todavía tiene mucho tráfico, así que quería actualizarla. Con la locura de los cambios de Alpha, Beta y 7 candidatos RC, dejé de actualizar mis respuestas SO hasta que se estabilizaron.
Este es el caso perfecto para usar Sujetos y ReplaySubjects
Yo personalmente prefiero usar ReplaySubject(1)ya que permite que el último valor almacenado que se pasa cuando se unen nuevos suscriptores incluso cuando tarde:
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
Entonces, incluso si adjunto tarde o necesito cargar más tarde, siempre puedo recibir la última llamada y no preocuparme por perder la devolución de llamada.
Esto también le permite usar la misma secuencia para presionar hacia abajo:
project.next(5678);
//output
//Subscription Streaming: 5678
Pero, ¿qué sucede si está 100% seguro de que solo necesita hacer la llamada una vez? Dejar temas abiertos y observables no es bueno, pero siempre está ese "¿Y si?"
Ahí es donde entra AsyncSubject .
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
¡Increíble! Aunque cerramos el tema, todavía respondió con lo último que cargó.
Otra cosa es cómo nos suscribimos a esa llamada http y manejamos la respuesta. El mapa es excelente para procesar la respuesta.
public call = http.get(whatever).map(res => res.json())
Pero, ¿y si necesitáramos anidar esas llamadas? Sí, podría usar sujetos con una función especial:
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();
Pero eso es mucho y significa que necesita una función para hacerlo. Ingrese FlatMap :
var myThing = http.get('path').flatMap(result1 =>
http.get('other/' + result1).flatMap(response2 =>
http.get('another/' + response2)));
Dulce, vares un observable que obtiene los datos de la última llamada http.
OK, eso es genial, pero quiero un servicio angular2.
Te tengo:
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);
}
}
Soy un gran admirador de los observadores y observables, ¡así que espero que esta actualización ayude!
Respuesta original
Creo que este es un caso de uso del uso de un sujeto observable o en Angular2el EventEmitter.
En su servicio, crea uno EventEmitterque le permite insertar valores en él. En Alpha 45 tienes que convertirlo toRx(), pero sé que estaban trabajando para deshacerse de eso, por lo que en Alpha 46 puedes simplemente devolver el EvenEmitter.
class EventService {
_emitter: EventEmitter = new EventEmitter();
rxEmitter: any;
constructor() {
this.rxEmitter = this._emitter.toRx();
}
doSomething(data){
this.rxEmitter.next(data);
}
}
De esta manera tiene el único EventEmitterque sus diferentes funciones de servicio ahora pueden empujar.
Si desea devolver un observable directamente de una llamada, puede hacer algo como esto:
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();
});
});
}
Eso le permitiría hacer esto en el componente:
peopleService.myHttpCall('path').subscribe(people => this.people = people);
Y meterse con los resultados de la llamada en su servicio.
Me gusta crear la EventEmittertransmisión por sí sola en caso de que necesite acceder a ella desde otros componentes, pero pude ver que ambas formas funcionan ...
Aquí hay un plunker que muestra un servicio básico con un emisor de eventos: Plunkr