ATUALIZAÇÃO: 24/09/16 Angular 2.0 Estável
Como essa questão ainda causa muito tráfego, eu queria atualizá-la. Com a insanidade das alterações dos candidatos Alpha, Beta e 7 RC, parei de atualizar minhas respostas de SO até que se tornassem estáveis.
Esse é o caso perfeito para usar Assuntos e ReplaySubjects
Eu pessoalmente prefiro usar ReplaySubject(1)
, pois permite que o último valor armazenado para ser passado quando novos assinantes anexar mesmo quando final:
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
Portanto, mesmo se eu conectar tarde ou precisar carregar mais tarde, sempre posso receber a última chamada e não me preocupar em perder a chamada de retorno.
Isso também permite que você use o mesmo fluxo para pressionar para:
project.next(5678);
//output
//Subscription Streaming: 5678
Mas e se você tiver 100% de certeza de que precisa fazer a ligação apenas uma vez? Deixar assuntos abertos e observáveis não é bom, mas sempre existe o "E se?"
É aí que entra o 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
Impressionante! Apesar de termos encerrado o assunto, ele ainda respondeu com a última coisa que carregou.
Outra coisa é como assinamos a chamada http e lidamos com a resposta. O mapa é ótimo para processar a resposta.
public call = http.get(whatever).map(res => res.json())
Mas e se precisássemos aninhar essas chamadas? Sim, você pode usar assuntos com uma função 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();
Mas isso é muito e significa que você precisa de uma função para fazer isso. Digite FlatMap :
var myThing = http.get('path').flatMap(result1 =>
http.get('other/' + result1).flatMap(response2 =>
http.get('another/' + response2)));
Doce, var
é um observável que obtém os dados da chamada http final.
OK, isso é ótimo, mas eu quero um serviço angular2!
Entendi:
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);
}
}
Sou um grande fã de observadores e observáveis, então espero que esta atualização ajude!
Resposta original
Acho que este é um caso de uso de usar um Assunto Observable ou Angular2
a EventEmitter
.
No seu serviço, você cria um EventEmitter
que permite inserir valores nele. No Alpha 45 você precisa convertê-lo toRx()
, mas eu sei que eles estavam trabalhando para se livrar disso; portanto, no Alpha 46, você pode simplesmente devolver o EvenEmitter
.
class EventService {
_emitter: EventEmitter = new EventEmitter();
rxEmitter: any;
constructor() {
this.rxEmitter = this._emitter.toRx();
}
doSomething(data){
this.rxEmitter.next(data);
}
}
Dessa maneira, é possível selecionar EventEmitter
as diferentes funções de serviço.
Se você quiser retornar um observável diretamente de uma chamada, poderá fazer algo assim:
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();
});
});
}
Isso permitiria que você fizesse isso no componente:
peopleService.myHttpCall('path').subscribe(people => this.people = people);
E mexa com os resultados da chamada em seu serviço.
Gosto de criar o EventEmitter
fluxo por conta própria, caso precise obter acesso a partir de outros componentes, mas pude ver as duas maneiras de trabalhar ...
Aqui está um desentupidor que mostra um serviço básico com um emissor de evento: Plunkr
EventEmitter
para qualquer coisa, mas@Output()
é desencorajado. Veja também stackoverflow.com/questions/34376854/…Este é um exemplo dos documentos do Angular2 de como você pode criar e usar seus próprios Observables:
O serviço
O componente
Um exemplo completo e funcional pode ser encontrado aqui: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
fonte
Gostaria de acrescentar que, se o objeto criado for estático e não for enviado através de http, algo assim poderá ser feito:
Edit: Para o mapeamento Angular 7.xx, é necessário usar o pipe (), conforme descrito aqui ( https://stackoverflow.com/a/54085359/986160 ):
da resposta à minha pergunta sobre observadores e dados estáticos: https://stackoverflow.com/a/35219772/986160
fonte
Estou um pouco atrasado para a festa, mas acho que minha abordagem tem a vantagem de não ter o uso de EventEmitters e Subject.
Então, aqui está a minha abordagem. Não podemos deixar de subscrever () e não queremos. Nesse sentido, nosso serviço retornará
Observable<T>
com um observador que possui nossa preciosa carga. No chamador, inicializaremos uma variávelObservable<T>
, e ela receberá os serviçosObservable<T>
. Em seguida, assinaremos este objeto. Finalmente, você recebe o seu "T"! do seu serviço.Primeiro, nosso pessoal atende, mas o seu não passa parâmetros, isso é mais realista:
Ok, como você pode ver, estamos retornando um
Observable
tipo de "pessoas". A assinatura do método, até diz isso! Colocamos o_people
objeto em nosso observador. Acessaremos esse tipo de nosso chamador no Componente, a seguir!No componente:
Inicializamos nosso
_peopleObservable
retornando issoObservable<people>
de nossoPeopleService
. Em seguida, assinamos esta propriedade. Por fim, definimosthis.people
nossapeople
resposta data ( ).A arquitetura do serviço dessa maneira possui uma grande vantagem sobre o serviço típico: mapa (...) e componente: padrão "subscrever (...)". No mundo real, precisamos mapear o json para nossas propriedades em nossa classe e, às vezes, fazemos algumas coisas personalizadas lá. Portanto, esse mapeamento pode ocorrer em nosso serviço. E, normalmente, como nossa chamada de serviço será usada não apenas uma vez, mas, provavelmente, em outros lugares do nosso código, não precisamos executar esse mapeamento em algum componente novamente. Além disso, e se adicionarmos um novo campo às pessoas? ....
fonte
No arquivo service.ts -
uma. importação 'de' de observável / de
b. crie uma lista json
c. retornar objeto json usando Observable.of ()
Ex. -
No componente em que estamos chamando a função get do serviço -
fonte
Observe que você está usando o mapa Observable # para converter o
Response
objeto bruto que o Observable básico emite em uma representação analisada da resposta JSON.Se eu entendi você corretamente, você quer
map
novamente. Mas desta vez, convertendo esse JSON bruto em instâncias do seu arquivoModel
. Então você faria algo como:Então, você começou com um Observable que emite um
Response
objeto, transformou isso em um observável que emite um objeto do JSON analisado dessa resposta e, em seguida, transformou isso em outro observável que transformou esse JSON bruto em uma matriz de seus modelos.fonte