Es war einmal ein Artikel über die Arbeit mit EventManager in Angular . Darin habe ich darüber gesprochen, wie Sie die übliche Syntax für Ereignisabonnements speichern und gleichzeitig unnötige Auslöser für die Überprüfung von Änderungen bei häufigen und sensiblen Ereignissen vermeiden können.
Die von mir beschriebene Methode ist jedoch umständlich und schwer zu verstehen. Es ist Zeit, die Filterung für Dekorateure neu zu schreiben.

Kurze Wiederholung
Für diejenigen, die den vorherigen Artikel nicht gelesen haben und nicht lesen möchten, eine Zusammenfassung des Problems:
- Angular ermöglicht das deklarative Abonnieren von Ereignissen in der Vorlage (
(eventName)
) und über Dekoratoren ( @HostListener(‘eventName’)
). - Bei einer Änderungsvalidierungsstrategie führt
OnPush
Angular eine Validierung durch, wenn ein Ereignis auftritt, das wir auf diese Weise abonniert haben. - Ereignisse wie
scroll
, mousemove
, drag
ausgelöst sehr oft. In der Praxis müssen Sie nur auf einige von ihnen antworten (z. B. wenn der Benutzer den Container bis zum Ende gescrollt hat - laden Sie neue Elemente). - Angular behandelt die Ereignisverarbeitung
EventManager
mit den bereitgestellten EventManagerPlugin
s. - 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, .