Estou tentando implementar algo como um padrão de delegação no Angular. Quando o usuário clica em um nav-item
, eu gostaria de chamar uma função que emite um evento que, por sua vez, deve ser tratado por algum outro componente que estiver ouvindo o evento.
Aqui está o cenário: Eu tenho um Navigation
componente:
import {Component, Output, EventEmitter} from 'angular2/core';
@Component({
// other properties left out for brevity
events : ['navchange'],
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
@Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
Aqui está o componente de observação:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
A questão principal é: como faço para o componente de observação observar o evento em questão?
Respostas:
Atualização 27/06/2016: em vez de usar Observables, use
Um Assunto é um Observável (para que possamos
subscribe()
fazê-lo) e um Observador (para que possamos chamánext()
-lo para emitir um novo valor). Nós exploramos esse recurso. Um Assunto permite que os valores sejam multicast para muitos Observadores. Não exploramos esse recurso (temos apenas um observador).BehaviorSubject é uma variante do Subject. Tem a noção de "o valor atual". Exploramos isso: sempre que criamos um ObservingComponent, ele obtém o valor atual do item de navegação do BehaviorSubject automaticamente.
O código abaixo e o plunker usam BehaviorSubject.
ReplaySubject é outra variante do Subject. Se você quiser esperar até que um valor seja realmente produzido, use
ReplaySubject(1)
. Enquanto um BehaviorSubject requer um valor inicial (que será fornecido imediatamente), ReplaySubject não. O ReplaySubject sempre fornecerá o valor mais recente, mas como ele não possui um valor inicial necessário, o serviço pode executar algumas operações assíncronas antes de retornar seu primeiro valor. Ele ainda dispara imediatamente nas chamadas subsequentes com o valor mais recente. Se você deseja apenas um valor, usefirst()
na assinatura. Você não precisa cancelar a inscrição se usarfirst()
.Plunker
Resposta original que usa um Observable: (requer mais código e lógica do que usar um BehaviorSubject, por isso não o recomendo, mas pode ser instrutivo)
Então, aqui está uma implementação que usa um Observable em vez de um EventEmitter . Diferentemente da minha implementação EventEmitter, essa implementação também armazena o atualmente selecionado
navItem
no serviço, para que, quando um componente de observação for criado, ele possa recuperar o valor atual por meio de uma chamada de APInavItem()
e ser notificado sobre alterações por meio donavChange$
Observable.Plunker
Consulte também o exemplo do Component Interaction Cookbook , que usa um
Subject
além de observáveis. Embora o exemplo seja "comunicação entre pais e filhos", a mesma técnica é aplicável a componentes não relacionados.fonte
EventEmitter
qualquer lugar, exceto nas saídas. No momento, eles estão reescrevendo os tutoriais (comunicação do servidor AFAIR) para não incentivar essa prática._observer
objeto de serviço pelo menos não é inicializado no momentongOnInit()
em que o componente Navigation é chamado.EventEmitter
porque está "quente", o que significa que já está "compartilhado", foi projetado para salvar o valor atual e, finalmente, implementa o Observable e o Observer, o que economizará pelo menos cinco linhas de código e duas propriedadessubscribe()
, portanto não pode detectar quando um observável muda. Normalmente, há algum evento assíncrono que é acionado (no meu código de exemplo, são os eventos de clique no botão), e o retorno de chamada associado chamará next () em um observável. Mas a detecção de alterações é executada por causa do evento assíncrono, não por causa da alteração observável. Veja também os comentários de Günter: stackoverflow.com/a/36846501/215945ReplaySubject(1)
. ABehaviorSubject
requer um valor inicial que será fornecido imediatamente. EleReplaySubject(1)
sempre fornecerá o valor mais recente, mas não possui um valor inicial necessário para que o serviço possa executar alguma operação assíncrona antes de retornar seu primeiro valor, mas ainda dispara imediatamente nas chamadas subseqüentes com o último valor. Se você estiver interessado apenas em um valor, poderá usarfirst()
a assinatura e não precisar cancelar a assinatura no final, pois isso será concluído.Notícias de Última Hora: de última adicionei outra resposta que usa um Observable em vez de um EventEmitter. Eu recomendo essa resposta sobre esta. E, na verdade, usar um EventEmitter em um serviço é uma prática ruim .
Resposta original: (não faça isso)
Coloque o EventEmitter em um serviço, que permite ao ObservingComponent se inscrever diretamente (e cancelar a inscrição) no evento:Se você tentar Plunker, há algumas coisas que eu não gosto nessa abordagem:
subscribe()
que o apropriadothis
seja definido quando o retorno de chamada for chamadoAtualização: Uma alternativa que resolve o segundo marcador é fazer com que o ObservingComponent assine diretamente o
navchange
propriedade EventEmitter:Se subscrevermos diretamente, não precisaremos do
subscribe()
método no NavService.Para tornar o NavService um pouco mais encapsulado, você pode adicionar um
getNavChangeEmitter()
método e usá-lo:fonte
this.subscription = this.navService.subscribe(() => this.selectedNavItem());
e ao se inscrever:return this.navchange.subscribe(callback);
Se alguém quiser seguir um estilo de programação mais reativo, então definitivamente o conceito de "Tudo é um fluxo" entra em cena e, portanto, use o Observables para lidar com esses fluxos o mais rápido possível.
fonte
Você pode usar:
BehaviorSubject é um tipo de assunto, um assunto é um tipo especial de observável que pode atuar como observável e observador. Você pode assinar mensagens como qualquer outro observável e, após a assinatura, retorna o último valor do assunto emitido pela fonte observável:
Vantagem: Nenhum relacionamento, como o relacionamento pai-filho, é necessário para transmitir dados entre componentes.
SERVIÇO NAV
COMPONENTE DE NAVEGAÇÃO
OBSERVANDO COMPONENTE
A segunda abordagem é
Event Delegation in upward direction child -> parent
por exemplo, Respondida por @Ashish Sharma.
fonte
Você precisa usar o componente Navegação no modelo de ObservingComponent (não se esqueça de adicionar um seletor ao componente Navegação .. navigation-component for ex)
E implemente onNavGhange () em ObservingComponent
Última coisa: você não precisa do atributo events em @Componennt
fonte
nav-item
mas eu só quero emitir um evento que outros possam observar.você pode usar o BehaviourSubject conforme descrito acima ou há mais uma maneira:
você pode manipular o EventEmitter assim: primeiro adicione um seletor
Agora você pode lidar com esse evento como suponhamos que observer.component.html seja a visualização do componente Observer
depois no ObservingComponent.ts
fonte
Eu descobri outra solução para este caso sem usar o Reactivex nem os serviços. Na verdade, eu amo a API rxjx, no entanto, acho que ela funciona melhor ao resolver uma função assíncrona e / ou complexa. Usá-lo dessa maneira, é muito superior a mim.
O que eu acho que você está procurando é para uma transmissão. Só isso. E eu descobri esta solução:
Este é um exemplo de trabalho completo: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE
fonte