Érase una vez, escribí un artículo sobre trabajar con EventManager en Angular . En él, hablé sobre cómo puede guardar la sintaxis habitual para las suscripciones de eventos, mientras evita desencadenantes innecesarios para verificar cambios en eventos frecuentes y confidenciales.
Sin embargo, el método que describí es engorroso y difícil de entender. Es hora de reescribir el filtrado en los decoradores.

Breve repetición
Para aquellos que no han leído y no quieren leer el artículo anterior, un resumen del problema:
- Angular permite suscribirse declarativamente a eventos en la plantilla (
(eventName)
) y a través de decoradores ( @HostListener(‘eventName’)
). - Con una estrategia de validación de cambio,
OnPush
Angular ejecutará una validación si ocurre un evento al que nos suscribimos de esta manera. - Los eventos como
scroll
, mousemove
, drag
desencadenan muy a menudo. En la práctica, solo necesita responder a algunos de ellos (por ejemplo, cuando el usuario desplaza el contenedor hasta el final, cargue nuevos elementos). - Angular maneja el procesamiento de eventos
EventManager
utilizando los EventManagerPlugin
s proporcionados . - Angular , .
. . .
Angular (keydown.ctrl.enter
).
, EventManager
. . Angular , EVENT_MANAGER_PLUGINS
. , , . addEventListener
, , , , , . .
preventDefault
stopPropagation
. , , , :
@Injectable()
export class StopEventPlugin {
supports(event: string): boolean {
return event.split('.').includes('stop');
}
addEventListener(
element: HTMLElement,
event: string,
handler: Function
): Function {
const wrapped = (event: Event) => {
event.stopPropagation();
handler(event);
};
return this.manager.addEventListener(
element,
event
.split('.')
.filter(v => v !== 'stop')
.join('.'),
wrapped,
);
}
}
. , :
- Angular, .
- .
- .
, NgZone
:
@Injectable()
export class SilentEventPlugin {
supports(event: string): boolean {
return event.split('.').includes('silent');
}
addEventListener(
element: HTMLElement,
event: string,
handler: Function
): Function {
return this.manager.getZone().runOutsideAngular(() =>
this.manager.addEventListener(
element,
event
.split('.')
.filter(v => v !== 'silent')
.join('.'),
handler,
),
);
}
}
, .
, -. /, this
.
, , . — - , , . $event
@HostListener
. :
export function shouldCall<T>(
predicate: Predicate<T>
): MethodDecorator {
return (_target, _key, desc: PropertyDescriptor) => {
const {value} = desc;
desc.value = function(this: T, ...args: any[]) {
if (predicate.apply(this, args)) {
value.apply(this, args);
}
};
};
}
. — - Angular, .
Angular 10 Ivy , markDirty(this)
. , - NgZone
. . , . , NgZone
, :
@Injectable()
export class ZoneEventPlugin {
supports(event: string): boolean {
return event.split('.').includes('init');
}
addEventListener(
_element: HTMLElement,
_event: string,
handler: Function
): Function {
const zone = this.manager.getZone();
const subscription = zone.onStable.subscribe(() => {
subscription.unsubscribe();
handler(zone);
});
return () => {};
}
}
— .init
, ( , ). @HostListener(‘prop.init’, [‘$event’])
:
export function shouldCall<T>(
predicate: Predicate<T>
): MethodDecorator {
return (_, key, desc: PropertyDescriptor) => {
const {value} = desc;
desc.value = function() {
const zone = arguments[0] as NgZone;
Object.defineProperty(this, key, {
value(this: T, ...args: any[]) {
if (predicate.apply(this, args)) {
zone.run(() => {
value.apply(this, args);
});
}
},
});
};
};
}
, . , . Ivy.

, , :
https://stackblitz.com/edit/angular-event-filter-decorator
, , , . , , . async
Observable
:
<p *ngFor="let item of service.items$ | async">{{item}}</p>
, :
export function scrolledToBottom(
{scrollTop, scrollHeight, clientHeight}: HTMLElement
): boolean {
return scrollTop >= scrollHeight - clientHeight - 20;
}
@Component({
selector: 'awesome-component',
templateUrl: './awesome-component.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AwesomeComponent {
constructor(@Inject(Service) readonly service: Service) {}
@HostListener('scroll.silent', ['$event.currentTarget'])
@HostListener('init.onScroll', ['$event'])
@shouldCall(scrolledToBottom)
onScroll() {
this.service.loadMore();
}
}
. :
https://stackblitz.com/edit/angular-event-filters-scroll
, . CustomEvent
, . .
(1 gzip) open-source- @tinkoff/ng-event-filters
. Angular 10 2.0.0, markDirty(this)
, Angular 4.
npm-
-, open source, ? Angular Open-source Library Starter, . CI, , , CHANGELOG, .