Dynamiczne dodawanie detektora zdarzeń w Angular 2
Właśnie zaczynam zadzierać z Angular 2 i zastanawiam się, czy ktoś może mi powiedzieć, jak najlepiej dynamicznie dodawać i usuwać słuchacze zdarzeń z elementów.
Mam ustawiony komponent. Po kliknięciu określonego elementu w szablonie chcę dodać listener dla mousemove
do innego elementu tego samego szablonu. Następnie chcę usunąć tego słuchacza, gdy trzeci element jest kliknięty.
Tak jakby to działało, używając zwykłego Javascript, aby pobrać elementy, a następnie wywołanie standardu addEventListener()
ale zastanawiałem się, czy nie ma bardziej "Angular2.0" sposób, w jaki to robię, że powinienem się przyjrzeć.
3 answers
Renderer został wycofany w Angular 4.0.0-RC.1, przeczytaj aktualizację poniżej
angular2 sposób jest użycie listen
lub listenGlobal
z Renderer
Na przykład, jeśli chcesz dodać zdarzenie click do komponentu, musisz użyć renderera i ElementRef (daje to również opcję użycia ViewChild lub czegokolwiek, co pobiera nativeElement
)
constructor(elementRef: ElementRef, renderer: Renderer) {
// Listen to click events in the component
renderer.listen(elementRef.nativeElement, 'click', (event) => {
// Do something with 'event'
})
);
Możesz użyć listenGlobal
, który da ci dostęp do document
, body
, itd.
renderer.listenGlobal('document', 'click', (event) => {
// Do something with 'event'
});
Zauważ, że od wersji beta.2 zarówno listen
, jak i listenGlobal
zwracają funkcję, która usuwa słuchacz (zobacz sekcję breaking changes W changelog dla wersji beta.2). Ma to na celu uniknięcie wycieków pamięci w dużych aplikacjach (patrz #6686).
Więc aby usunąć detektor dodaliśmy dynamicznie musimy przypisać listen
lub listenGlobal
do zmiennej, która będzie zawierała zwracaną funkcję, a następnie ją wykonujemy.
// 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();
}
Oto plnkr Z działającym przykładem. Na przykład zawiera użycie listen
i listenGlobal
.
Używanie RendererV2 z Angular 4.0.0-RC.1+ (Renderer2 od 4.0.0-rc.3)
25/02/2017:
Renderer
został przestarzały, teraz powinniśmy użyć(patrz linia poniżej). Zobacz commit .RendererV2
10/03/2017:
RendererV2
został przemianowany naRenderer2
. Zobacz przełomowe zmiany .
RendererV2
nie ma więcej funkcji listenGlobal
dla zdarzeń globalnych (document, body, window). Posiada tylko listen
funkcję, która spełnia obie funkcje.
W celach informacyjnych kopiuję i wklejam Kod źródłowy implementacji renderera DOM, ponieważ może się on zmienić (tak, jest kanciasty!).
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;
}
Jak widzisz, teraz sprawdza, czy przekazujemy ciąg znaków (dokument, treść lub okno), w którym to przypadku użyje wewnętrznej funkcji addGlobalEventListener
. W każdym innym przypadku, gdy mijamy element (nativeElement) użyje prostej addEventListener
Aby usunąć słuchacza jest tak samo jak z Renderer
w angular 2.x. listen
zwraca funkcję, a następnie wywołuje tę funkcję.
Przykład
// 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 Z Angular 4.0.0-rc.1 using RendererV2
Plnkr Z Angular 4.0.0-rc.3 using Renderer2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-10 21:22:48
Uważam to za bardzo mylące. jak @EricMartinez wskazuje renderer2 listen () zwraca funkcję usuwania słuchacza:
ƒ () { return element.removeEventListener(eventName, /** @type {?} */ (handler), false); }
If im adding a listener
this.listenToClick = this.renderer.listen('document', 'click', (evt) => {
alert('Clicking the document');
})
Oczekuję, że moja funkcja wykona to, co zamierzałem, a nie całkowite przeciwieństwo, jakim jest usunięcie słuchacza.
// I´d expect an alert('Clicking the document');
this.listenToClick();
// what you actually get is removing the listener, so nothing...
W danym scenariuszu, itp rzeczywiście bardziej sensowne jest nazwanie go tak:
// 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);
});
Musi być ku temu dobry powód, ale moim zdaniem jest to bardzo mylące i nie intuicyjne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-26 09:34:59
Oto moje obejście:
Stworzyłem bibliotekę z Angular 6. Dodałem wspólny komponent commonlib-header
, który jest używany w ten sposób w zewnętrznej aplikacji.
Zwróć uwagę na serviceReference
, która jest klasą (wtryskiwaną w komponent constructor(public serviceReference: MyService)
, który używa metody commonlib-header
), która przechowuje metodę stringFunctionName
:
<commonlib-header
[logo]="{ src: 'assets/img/logo.svg', alt: 'Logo', href: '#' }"
[buttons]="[{ index: 0, innerHtml: 'Button', class: 'btn btn-primary', onClick: [serviceReference, 'stringFunctionName', ['arg1','arg2','arg3']] }]">
</common-header>
Komponent biblioteki jest zaprogramowany w ten sposób. Zdarzenie dynamiczne jest dodawane w metodzie onClick(fn: any)
:
export class HeaderComponent implements OnInit {
_buttons: Array<NavItem> = []
@Input()
set buttons(buttons: Array<any>) {
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)
}
Wielokrotnego użytku header.component.html
:
<div class="topbar-right">
<button *ngFor="let btn of _buttons"
class="{{ btn.class }}"
(click)="onClick(btn.onClick)"
[innerHTML]="btn.innerHtml | keepHtml"></button>
</div>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 18:45:27