Agregue dinámicamente el detector de eventos en Angular 2

Estoy empezando a perder el tiempo con Angular 2 y me pregunto si alguien puede decirme la mejor manera de agregar y eliminar dinámicamente los detectores de eventos de los elementos.

Tengo un componente configurado. Cuando se hace clic en un determinado elemento de la plantilla, quiero agregar un oyente para mousemove a otro elemento de la misma plantilla. Luego quiero eliminar este oyente cuando se hace clic en un tercer elemento.

En cierto modo conseguí que funcionara simplemente usando JavaScript para tomar los elementos y luego llamar al addEventListener() estándar addEventListener() pero me pregunté si había una forma más ” Angular2.0 ” de hacer esto que debería estar investigando.

Saludos por cualquier consejo 🙂

Renderer ha quedado obsoleto en Angular 4.0.0-rc.1, lea la actualización a continuación

La forma angular2 es usar listen o listen listenGlobal desde Renderer

Por ejemplo, si desea agregar un evento de clic a un Componente, debe usar Renderer y ElementRef (esto le da también la opción de usar ViewChild, o cualquier cosa que recupere el elemento nativeElement )

 constructor(elementRef: ElementRef, renderer: Renderer) { // Listen to click events in the component renderer.listen(elementRef.nativeElement, 'click', (event) => { // Do something with 'event' }) ); 

Puede usar listenGlobal que le dará acceso a document , body , etc.

 renderer.listenGlobal('document', 'click', (event) => { // Do something with 'event' }); 

Tenga en cuenta que, dado que beta.2 listen y listen listenGlobal devuelve una función para eliminar el oyente (consulte la sección de interrupción de cambios del registro de cambios para beta.2). Esto es para evitar memory leaks en aplicaciones grandes (ver # 6686 ).

Entonces, para eliminar el oyente que agregamos dinámicamente debemos asignar listen o listenGlobal a una variable que mantendrá la función devuelta, y luego la ejecutaremos.

 // listenFunc will hold the function returned by "renderer.listen" listenFunc: Function; // globalListenFunc will hold the function returned by "renderer.listenGlobal" globalListenFunc: Function; constructor(elementRef: ElementRef, renderer: Renderer) { // We cache the function "listen" returns this.listenFunc = renderer.listen(elementRef.nativeElement, 'click', (event) => { // Do something with 'event' }); // We cache the function "listenGlobal" returns this.globalListenFunc = renderer.listenGlobal('document', 'click', (event) => { // Do something with 'event' }); } ngOnDestroy() { // We execute both functions to remove the respectives listeners // Removes "listen" listener this.listenFunc(); // Removs "listenGlobal" listener this.globalListenFunc(); } 

Aquí hay un plnkr con un ejemplo de trabajo. El ejemplo contiene el uso de listen y listen listenGlobal .

Usando RendererV2 con Angular 4.0.0-rc.1 + (Renderer2 desde 4.0.0-rc.3)

  • 25/02/2017 : Renderer ha quedado obsoleto, ahora deberíamos usar RendererV2 (ver la línea a continuación). Ver el compromiso .

  • 10/03/2017 : RendererV2 pasó a llamarse Renderer2 . Vea los cambios de última hora .

RendererV2 no tiene más función listenGlobal para eventos globales (documento, cuerpo, ventana). Solo tiene una función de listen que logra ambas funcionalidades.

Como referencia, estoy copiando y pegando el código fuente de la implementación de DOM Renderer, ya que puede cambiar (¡sí, es angular!).

 listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean): () => void { if (typeof target === 'string') { return <() => void>this.eventManager.addGlobalEventListener( target, event, decoratePreventDefault(callback)); } return <() => void>this.eventManager.addEventListener( target, event, decoratePreventDefault(callback)) as() => void; } 

Como puede ver, ahora verifica si estamos pasando una cadena (documento, cuerpo o ventana), en cuyo caso usará una función interna addGlobalEventListener . En cualquier otro caso, cuando pasemos un elemento (nativeElement) usará un simple addEventListener

Para eliminar el oyente, es lo mismo que con Renderer en 2.x angular. listen devuelve una función, luego llama a esa función.

Ejemplo

 // Add listeners let global = this.renderer.listen('document', 'click', (evt) => { console.log('Clicking the document', evt); }) let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => { console.log('Clicking the button', evt); }); // Remove listeners global(); simple(); 

plnkr con Angular 4.0.0-rc.1 usando RendererV2

plnkr con Angular 4.0.0-rc.3 usando Renderer2

Yo también encuentro esto extremadamente confuso. como @EricMartinez señala Renderer2 listen () devuelve la función para eliminar el oyente:

 ƒ () { return element.removeEventListener(eventName, /** @type {?} */ (handler), false); } 

Si estoy agregando un oyente

 this.listenToClick = this.renderer.listen('document', 'click', (evt) => { alert('Clicking the document'); }) 

Esperaría que mi función ejecutara lo que pretendía, no el total opuesto que es eliminar al oyente.

 // I´d expect an alert('Clicking the document'); this.listenToClick(); // what you actually get is removing the listener, so nothing... 

En el escenario dado, en realidad tendría más sentido nombrarlo así:

 // Add listeners let unlistenGlobal = this.renderer.listen('document', 'click', (evt) => { console.log('Clicking the document', evt); }) let removeSimple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => { console.log('Clicking the button', evt); }); 

Debe haber una buena razón para esto, pero en mi opinión es muy engañoso y no es intuitivo.

Aquí está mi solución:

commonlib-header una biblioteca con Angular 6. commonlib-header un componente común commonlib-header que se usa así en una aplicación externa.

Tenga en cuenta la serviceReference que es la clase (inyectada en el constructor(public serviceReference: MyService) del componente constructor(public serviceReference: MyService) que usa el commonlib-header ) que contiene el método stringFunctionName :

   

El componente de la biblioteca está progtwigdo así. El evento dynamic se agrega en el método onClick(fn: any) :

 export class HeaderComponent implements OnInit { _buttons: Array = [] @Input() set buttons(buttons: Array) { buttons.forEach(navItem => { let _navItem = new NavItem(navItem.href, navItem.innerHtml) _navItem.class = navItem.class _navItem.onClick = navItem.onClick // this is the array from the component @Input properties above this._buttons[navItem.index] = _navItem }) } constructor() {} ngOnInit() {} onClick(fn: any){ let ref = fn[0] let fnName = fn[1] let args = fn[2] ref[fnName].apply(ref, args) } 

El header.component.html reutilizable: