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, var
es 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 Angular2
el EventEmitter
.
En su servicio, crea uno EventEmitter
que 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 EventEmitter
que 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 EventEmitter
transmisió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
EventEmitter
para cualquier cosa pero no@Output()
se recomienda. Ver también stackoverflow.com/questions/34376854/…Este es un ejemplo de los documentos de Angular2 sobre cómo puede crear y usar sus propios Observables:
El servicio
El componente
Puede encontrar un ejemplo completo y funcional aquí: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
fuente
Me gustaría agregar que si el objeto que se crea es estático y no llega a través de http, se puede hacer algo así:
Editar: para el mapeo Angular 7.xx se debe hacer usando pipe () como se describe aquí ( https://stackoverflow.com/a/54085359/986160 ):
de la respuesta a mi pregunta sobre observadores y datos estáticos: https://stackoverflow.com/a/35219772/986160
fuente
Llego un poco tarde a la fiesta, pero creo que mi enfoque tiene la ventaja de que carece del uso de EventEmitters y Asignaturas.
Entonces, aquí está mi enfoque. No podemos escapar de subscribe (), y no queremos hacerlo. En ese sentido, nuestro servicio regresará
Observable<T>
con un observador que tiene nuestra preciosa carga. Desde la persona que llama, inicializaremos una variableObservable<T>
, y obtendrá el servicioObservable<T>
. A continuación, nos suscribiremos a este objeto. Finalmente, obtienes tu "T"! de su servicioPrimero, nuestro servicio de personas, pero el suyo no pasa parámetros, eso es más realista:
Ok, como pueden ver, estamos devolviendo un
Observable
tipo de "personas". La firma del método, incluso lo dice! Metimos el_people
objeto en nuestro observador. A continuación, accederemos a este tipo desde nuestra persona que llama en el Componente.En el componente:
Inicializamos nuestro
_peopleObservable
devolviendo esoObservable<people>
de nuestroPeopleService
. Luego, nos suscribimos a esta propiedad. Finalmente, establecemosthis.people
nuestrapeople
respuesta data ( ).Diseñar el servicio de esta manera tiene una gran ventaja sobre el servicio típico: mapa (...) y componente: patrón de "suscripción (...)". En el mundo real, necesitamos asignar el json a nuestras propiedades en nuestra clase y, a veces, hacemos algunas cosas personalizadas allí. Entonces este mapeo puede ocurrir en nuestro servicio. Y, típicamente, debido a que nuestra llamada de servicio se usará no una vez, sino, probablemente, en otros lugares de nuestro código, no tenemos que realizar esa asignación en algún componente, nuevamente. Además, ¿qué pasa si agregamos un nuevo campo a las personas? ...
fuente
En el archivo service.ts:
a. importar 'de' de observable / de
b. crear una lista json
c. devolver el objeto json usando Observable.of ()
Ej. -
En el componente donde estamos llamando a la función get del servicio:
fuente
Observe que está utilizando el mapa Observable # para convertir el
Response
objeto sin procesar que su Observable base emite en una representación analizada de la respuesta JSON.Si te entendí correctamente, quieres volver a hacerlo
map
. Pero esta vez, convirtiendo ese JSON sin procesar en instancias de tuModel
. Entonces harías algo como:Entonces, comenzaste con un Observable que emite un
Response
objeto, lo convertiste en un observable que emite un objeto del JSON analizado de esa respuesta, y luego lo convertiste en otro observable que convirtió ese JSON sin procesar en una matriz de tus modelos.fuente