Estoy tratando de implementar algo como un patrón de delegación en Angular. Cuando el usuario hace clic en un nav-item
, me gustaría llamar a una función que luego emite un evento que a su vez debe ser manejado por algún otro componente que escuche el evento.
Aquí está el escenario: tengo un 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)
}
}
Aquí está el componente de observación:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
La pregunta clave es, ¿cómo hago para que el componente de observación observe el evento en cuestión?
Respuestas:
Actualización 2016-06-27: en lugar de usar Observables, use cualquiera
Un Sujeto es tanto un Observable (para que podamos
subscribe()
hacerlo) como un Observador (para que podamos invocarlonext()
y emitir un nuevo valor). Explotamos esta característica. Un sujeto permite que los valores sean multidifusión para muchos observadores. No explotamos esta característica (solo tenemos un observador).BehaviorSubject es una variante de Subject. Tiene la noción de "el valor actual". Explotamos esto: cada vez que creamos un componente Observador, obtiene el valor del elemento de navegación actual del BehaviorSubject automáticamente.
El código a continuación y el plunker usan BehaviorSubject.
ReplaySubject es otra variante de Subject. Si desea esperar hasta que se produzca un valor, use
ReplaySubject(1)
. Mientras que BehaviorSubject requiere un valor inicial (que se proporcionará de inmediato), ReplaySubject no. ReplaySubject siempre proporcionará el valor más reciente, pero dado que no tiene un valor inicial requerido, el servicio puede realizar algunas operaciones asíncronas antes de devolver su primer valor. Todavía se disparará inmediatamente en llamadas posteriores con el valor más reciente. Si solo quieres un valor, úsalofirst()
en la suscripción. No tiene que darse de baja si lo usafirst()
.Plunker
Respuesta original que usa un Observable: (requiere más código y lógica que usar un BehaviorSubject, por lo que no lo recomiendo, pero puede ser instructivo)
Entonces, aquí hay una implementación que usa un Observable en lugar de un EventEmitter . A diferencia de mi implementación EventEmitter, esta implementación también almacena el seleccionado actualmente
navItem
en el servicio, de modo que cuando se crea un componente de observación, puede recuperar el valor actual a través de una llamada APInavItem()
y luego ser notificado de los cambios a través delnavChange$
Observable.Plunker
Consulte también el ejemplo del Libro de cocina de interacción de componentes , que utiliza
Subject
además de observables. Aunque el ejemplo es "comunicación entre padres e hijos", la misma técnica es aplicable para componentes no relacionados.fuente
EventEmitter
cualquier lugar, excepto en las salidas. Actualmente están reescribiendo los tutoriales (comunicación del servidor AFAIR) para no fomentar esta práctica._observer
al menos el objeto de servicio no se inicializa en el momento enngOnInit()
que se llama al componente de navegación.EventEmitter
porque está "caliente", lo que significa que ya está "compartido", está diseñado para guardar el valor actual y finalmente implementa tanto Observable como Observer, lo que le ahorrará al menos cinco líneas de código y dos propiedadessubscribe()
, por lo que no puede detectar cuándo cambia un observable. Normalmente, hay algún evento asíncrono que se dispara (en mi código de muestra, son los eventos de clic del botón), y la devolución de llamada asociada llamará a next () en un observable. Pero la detección de cambios se ejecuta debido al evento asíncrono, no debido al cambio observable. Ver también los comentarios de Günter: stackoverflow.com/a/36846501/215945ReplaySubject(1)
. ABehaviorSubject
requiere un valor inicial que se proporcionará inmediatamente. ElReplaySubject(1)
siempre va a proporcionar el valor más reciente, pero no tiene un valor inicial requiere que el servicio pueda hacer alguna operación asíncrona antes de regresar a su primer valor, pero aún así el fuego inmediato en llamadas posteriores con el último valor. Si solo está interesado en un valor que puede usarfirst()
en la suscripción y no tiene que darse de baja al final porque se completará.Noticias de última hora: he agregado otra respuesta que usa un Observable en lugar de un EventEmitter. Recomiendo esa respuesta sobre esta. Y en realidad, usar un EventEmitter en un servicio es una mala práctica .
Respuesta original: (no hagas esto)
Ponga el EventEmitter en un servicio, que permite que ObservingComponent se suscriba directamente (y cancele la suscripción) al evento:Si prueba el Plunker, hay algunas cosas que no me gustan de este enfoque:
subscribe()
quethis
se establezca el correcto cuando se llama la devolución de llamadaActualización: una alternativa que resuelve la segunda viñeta es hacer que el componente Observador se suscriba directamente al
navchange
propiedad EventEmitter:Si nos suscribimos directamente, entonces no necesitaríamos el
subscribe()
método en NavService.Para hacer que NavService esté un poco más encapsulado, puede agregar un
getNavChangeEmitter()
método y usarlo:fuente
this.subscription = this.navService.subscribe(() => this.selectedNavItem());
y al suscribirse:return this.navchange.subscribe(callback);
Si se quiere seguir un estilo de programación más reactivo, entonces definitivamente aparece el concepto de "Todo es un flujo" y, por lo tanto, se utilizan los Observables para tratar estos flujos con la mayor frecuencia posible.
fuente
Puedes usar:
BehaviorSubject es un tipo de sujeto, un sujeto es un tipo especial de observable que puede actuar como observable y observador puede suscribirse a mensajes como cualquier otro observable y al suscribirse, devuelve el último valor del sujeto emitido por la fuente observable:
Ventaja: No se requiere una relación como la relación padre-hijo para pasar datos entre componentes.
SERVICIO NAV
COMPONENTE DE NAVEGACIÓN
OBSERVANDO COMPONENTE
El segundo enfoque es
Event Delegation in upward direction child -> parent
Por ejemplo, Respondido por @Ashish Sharma.
fuente
Debe usar el componente de navegación en la plantilla de ObservingComponent (no olvide agregar un selector al componente de navegación ... componente de navegación para ex)
E implementar onNavGhange () en ObservingComponent
Lo último ... no necesitas el atributo de eventos en @Componennt
fuente
nav-item
pero solo quiero emitir un evento que otros puedan observar.puede usar BehaviourSubject como se describe anteriormente o hay una forma más:
puede manejar EventEmitter de esta manera: primero agregue un selector
Ahora puede manejar este evento como supongamos que observer.component.html es la vista del componente Observador
luego en ObservingComponent.ts
fuente
Encontré otra solución para este caso sin usar Reactivex ni servicios. De hecho, me encanta la API rxjx, sin embargo, creo que funciona mejor al resolver una función asincrónica y / o compleja. Utilizándolo de esa manera, es bastante excedido para mí.
Lo que creo que estás buscando es una transmisión. Solo eso. Y descubrí esta solución:
Este es un ejemplo de trabajo completo: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE
fuente