日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Angular 4.x 事件管理器及自定义EventManagerPlugin

發(fā)布時(shí)間:2023/12/4 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Angular 4.x 事件管理器及自定义EventManagerPlugin 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在 Angular 中如何為同一個(gè)表達(dá)式綁定多個(gè)事件呢?如果我們這樣做可能會(huì)是這樣的:

<div><button (click, mouseover)="onClick()">Click me</button> </div>復(fù)制代碼

在繼續(xù)分析綁定多個(gè)事件之前,我們先來分析一下,如果在模板中綁定一個(gè)事件如 click 事件,Angular 是如何工作的?

<div><button (click)="onClick()">Click me</button> </div>復(fù)制代碼

Angular 在解析 DOM 樹的時(shí)候,對(duì)于事件綁定它會(huì)調(diào)用 DomRenderer 實(shí)例的 listen() 方法,進(jìn)行事件綁定,listen() 方法具體實(shí)現(xiàn)如下:

// angular2/packages/platform-browser/src/dom/dom_renderer.ts class DefaultDomRenderer2 implements Renderer2 {....listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean):() => void {checkNoSyntheticProp(event, 'listener');if (typeof target === 'string') {return <() => void>this.eventManager.addGlobalEventListener(target, event, decoratePreventDefault(callback));}return <() => void>this.eventManager.addEventListener(target, event, decoratePreventDefault(callback)) as() => void;} }復(fù)制代碼

通過源碼我們發(fā)現(xiàn),不管走哪條分支,最終都是調(diào)用 this.eventManager 對(duì)象的方法設(shè)置事件監(jiān)聽。這里的 this.eventManager 是什么?它是 Angular 中的事件管理器 EventManager,我們先來會(huì)會(huì)它。

EventManager (事件管理器)

在 Angular 中所有的事件綁定都是由一個(gè)事件管理器來驅(qū)動(dòng),事件管理器本身由多個(gè)事件插件提供支持。Angular 中內(nèi)置的事件插件如下:

  • KeyEventsPlugin - 處理鍵盤事件
  • HammerGesturesPlugin - 處理手勢(shì)
  • DomEventsPlugin - 處理 DOM 事件

看完上面的內(nèi)容,相信很多人也會(huì)有疑問 - EventManager 到底是如何管理不同事件的呢?要揭開這背后的秘密,我們的唯一途徑就是看源碼,因?yàn)樗亲钫\(chéng)實(shí)的,它對(duì)你毫無保留,此刻腦海中突然想起一首歌:

美麗的神話

解開我 最神秘的等待
星星墜落 風(fēng)在吹動(dòng)
終于再將你擁入懷中
….

愛是心中唯一不變美麗的神話

放松一下,馬上回到正題 - EventManager 類:

EventManager 類

// angular2/packages/platform-browser/src/dom/events/event_manager.ts export class EventManager {// EventManagerPlugin列表private _plugins: EventManagerPlugin[]; // 緩存已匹配的eventName與對(duì)應(yīng)的插件private _eventNameToPlugin = new Map<string, EventManagerPlugin>();constructor(@Inject(EVENT_MANAGER_PLUGINS) plugins: EventManagerPlugin[], private _zone: NgZone) {plugins.forEach(p => p.manager = this);/*** {provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},* {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},* {provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true}* * slice(): 創(chuàng)建新的plugins數(shù)組* reverse(): 讓DomEventsPlugin插件作為列表最后一項(xiàng),因?yàn)樗軌蛱幚硭械氖录?/this._plugins = plugins.slice().reverse();}// 獲取能處理eventName的插件,并調(diào)用對(duì)應(yīng)插件提供的addEventListener()方法addEventListener(element: HTMLElement, eventName: string,handler: Function): Function {const plugin = this._findPluginFor(eventName);return plugin.addEventListener(element, eventName, handler);}// 獲取能處理eventName的插件,并調(diào)用對(duì)應(yīng)插件提供的addGlobalEventListener()方法addGlobalEventListener(target: string, eventName: string, handler: Function): Function {const plugin = this._findPluginFor(eventName);return plugin.addGlobalEventListener(target, eventName, handler);}// 獲取NgZonegetZone(): NgZone { return this._zone; }/** @internal */_findPluginFor(eventName: string): EventManagerPlugin {// 優(yōu)先從_eventNameToPlugin對(duì)象中獲取eventName對(duì)應(yīng)的EventManagerPluginconst plugin = this._eventNameToPlugin.get(eventName); if (plugin) {return plugin;}// 遍歷插件列表,判斷當(dāng)前插件是否支持eventName對(duì)應(yīng)的事件名const plugins = this._plugins;for (let i = 0; i < plugins.length; i++) {const plugin = plugins[i];if (plugin.supports(eventName)) {this._eventNameToPlugin.set(eventName, plugin);return plugin;}}throw new Error(`No event manager plugin found for event ${eventName}`);} }復(fù)制代碼

相關(guān)說明

  • 在 addEventListener() 或 addGlobalEventListener() 方法內(nèi)部都會(huì)調(diào)用 _findPluginFor() 方法,查詢對(duì)應(yīng)的能夠處理 eventName 對(duì)應(yīng)的 EventManagerPlugin 插件對(duì)象。
  • _findPluginFor() 方法中,會(huì)遍歷插件列表,然后以 eventName 作為參數(shù)調(diào)用插件對(duì)象提供的 supports() 方法,判斷當(dāng)前是否能夠處理 eventName 對(duì)應(yīng)的事件。因此對(duì)于 EventManagerPlugin 插件對(duì)象,如果要聲明能夠處理某類事件,就需要在 supports() 方法中進(jìn)行相應(yīng)處理。
  • DomEventsPlugin 插件作為列表最后一項(xiàng),因?yàn)樗軌蛱幚硭械氖录?/li>
  • KeyEventsPlugin、HammerGesturesPlugin、DomEventsPlugin 插件類都繼承于 EventManagerPlugin 抽象類。

EventManagerPlugin 抽象類

export abstract class EventManagerPlugin {constructor(private _doc: any) {}manager: EventManager;// 判斷是否支持eventName對(duì)應(yīng)的事件abstract supports(eventName: string): boolean;// 添加事件監(jiān)聽abstract addEventListener(element: HTMLElement, eventName: string, handler: Function): Function;// 添加全局的事件監(jiān)聽addGlobalEventListener(element: string, eventName: string, handler: Function): Function {const target: HTMLElement = getDOM().getGlobalEventTarget(this._doc, element);if (!target) {throw new Error(`Unsupported event target ${target} for event ${eventName}`);}return this.addEventListener(target, eventName, handler);}; }復(fù)制代碼

時(shí)機(jī)已成熟,接下來我們開始實(shí)現(xiàn)上述的功能。

自定義插件

Step 1: Creating a new plugin

正如上面提到的,我們希望在我們的 Angular 模板上有多個(gè)事件綁定到同一個(gè)表達(dá)式:

<div><button (click, mouseover)="onClick()">Click me</button> </div>復(fù)制代碼

如果是這樣,我們的 supports() 函數(shù)的內(nèi)部規(guī)則應(yīng)該很清楚。我們需要一個(gè)字符串,其中有一個(gè)或多個(gè)逗號(hào),分隔事件名稱。當(dāng)人們把一些愚蠢的東西放在(,click)中時(shí),我們也應(yīng)該處理。所以我們的 supports() 函數(shù)如下:

getMultiEventArray(eventName: string): string[] {return eventName.split(",").filter((item, index): boolean => { return item && item != '' }) }supports(eventName: string): boolean {return this.getMultiEventArray(eventName).length > 1 }復(fù)制代碼

這將允許 EventManager 將事件字符串如 (click, mouseover) 委派給此插件。

Step 2: Implementing the eventListeners

現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了supports() 方法,EventManager 將調(diào)用 plugin.addEventListener() 方法,因此插件需要實(shí)現(xiàn) addEventListener() 方法,從而實(shí)現(xiàn)我們的自定義行為。我們的自定義行為很簡(jiǎn)單 - 為我們解析的eventArray 中的所有事件添加事件偵聽器。

addEventListener

addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {let zone = this.manager.getZone();let eventsArray = this.getMultiEventArray(eventName);// Entering back into angular to trigger changeDetectionlet outsideHandler = (event: any) => {zone.runGuarded(() => handler(event));};// Executed outside of angular so that change detection is not // constantly triggered.let addAndRemoveHostListenersForOutsideEvents = () => {eventsArray.forEach((singleEventName: string) => {this.manager.addEventListener(element, singleEventName, outsideHandler);});}return this.manager.getZone().runOutsideAngular(addAndRemoveHostListenersForOutsideEvents);}復(fù)制代碼

addGlobalEventListener

addGlobalEventListener(target: string, eventName: string, handler: Function): Function {let zone = this.manager.getZone();let eventsArray = this.getMultiEventArray(eventName);let outsideHandler = (event: any) => zone.runGuarded(() => handler(event));return this.manager.getZone().runOutsideAngular(() => {eventsArray.forEach((singleEventName: string) => {this.manager.addGlobalEventListener(target, singleEventName, outsideHandler);})}); }復(fù)制代碼

Step 3: Register plugin

import { EVENT_MANAGER_PLUGINS } from '@angular/platform-browser';@NgModule({...providers: [{ provide: EVENT_MANAGER_PLUGINS, useClass: MultiEventPlugin, multi: true }] }) export class AppModule { }復(fù)制代碼

完整示例

multi-event.plugin.ts

import { Injectable, Inject } from '@angular/core'; import { EventManager, DOCUMENT, ?d as EventManagerPlugin } from '@angular/platform-browser';/*** Support Multi Event*/ @Injectable() export class MultiEventPlugin extends EventManagerPlugin {manager: EventManager;constructor( @Inject(DOCUMENT) doc: any) { super(doc); }getMultiEventArray(eventName: string): string[] { return eventName.split(",") // click,mouseover => [click,mouseover].filter((item, index): boolean => { return item && item != '' })}supports(eventName: string): boolean {return this.getMultiEventArray(eventName).length > 1;}addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {let zone = this.manager.getZone();let eventsArray = this.getMultiEventArray(eventName);// Entering back into angular to trigger changeDetectionlet outsideHandler = (event: any) => {zone.runGuarded(() => handler(event));};// Executed outside of angular so that change detection is// not constantly triggered.let addAndRemoveHostListenersForOutsideEvents = () => {eventsArray.forEach((singleEventName: string) => {this.manager.addEventListener(element, singleEventName, outsideHandler);});}return this.manager.getZone().runOutsideAngular(addAndRemoveHostListenersForOutsideEvents);}addGlobalEventListener(target: string, eventName: string, handler: Function): Function {let zone = this.manager.getZone();let eventsArray = this.getMultiEventArray(eventName);let outsideHandler = (event: any) => zone.runGuarded(() => handler(event));return this.manager.getZone().runOutsideAngular(() => {eventsArray.forEach((singleEventName: string) => {this.manager.addGlobalEventListener(target, singleEventName, outsideHandler);});});} }復(fù)制代碼

app.component.ts

import { Component } from '@angular/core';@Component({selector: 'exe-app',template: `<div><button (click,mouseover)="onClick()">Click me</button></div>` }) export class AppComponent {onClick() {console.log('Click');} }復(fù)制代碼

app.module.ts

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { EVENT_MANAGER_PLUGINS } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms';import { AppComponent } from './app.component'; import { MultiEventPlugin } from './plugins/multi-event.plugin';@NgModule({imports: [BrowserModule],declarations: [AppComponent],bootstrap: [AppComponent],providers: [{ provide: EVENT_MANAGER_PLUGINS, useClass: MultiEventPlugin, multi: true }],schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class AppModule { }復(fù)制代碼

參考資源

  • Hacking Angular2: Binding Multiple DOM Events

總結(jié)

以上是生活随笔為你收集整理的Angular 4.x 事件管理器及自定义EventManagerPlugin的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。