Angular4.x Event (DOM事件和自定义事件详解)


Posted in Javascript onOctober 09, 2018

Angular组件和DOM元素通过事件与外部进行通信,两者中的事件绑定语法是相同的-(eventName)="expression":

<button (click)="onClick()">Click</button>

DOM元素

DOM 元素触发的一些事件通过 DOM 层级结构传播,事件首先由最内层的元素开始,然后传播到外部元素,直到它们到根元素,这种传播过程称为事件冒泡。

DOM事件冒泡与Angular可以无缝工作,具体示例如下:

import { Component } from '@angular/core';

@Component({
 selector: 'exe-app',
 template: `
 <div (click)="onClick()">
 <button>Click</button>
 </div>
 `
})
export class AppComponent {
 onClick() {
 console.log('Click');
 }
}

以上代码成功运行后,当用户点击 Click 按钮,浏览器控制台将会输出:

Click

即表示 <div> 元素上设置的监听函数被执行,也间接证明了事件冒泡能正常工作。

Angular Component

Angular 允许开发者通过 @Output() 装饰器和 EventEmitter 自定义事件。它不同于 DOM 事件,因为它不支持事件冒泡。

首先来看一下自定义组件如何监听DOM事件:

event-bubbling.component.ts

import { Component } from '@angular/core';

@Component({
 selector: 'event-bubbling',
 template: `
 <div>
 <button>Click</button>
 </div>
 `
})
export class EventBubblingComponent { }

app.component.ts

import { Component } from '@angular/core';

@Component({
 selector: 'exe-app',
 template: `
 <div>
 <event-bubbling (click)="onClick()"></event-bubbling>
 </div>
 `
})
export class AppComponent {
 onClick() {
 console.log('Click');
 }
}

以上代码成功运行后,当用户点击 Click 按钮,浏览器控制台也会输出 Click。表示我们的自定义组件,也是可以正常处理组件内元素触发的 click 事件。但当我们在自定义组件中创建自定义事件时,事件名称也是使用 click ,那么会不会有问题呢?我们马上来实践一下。

Custom Events

event-bubbling.component

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
 selector: 'event-bubbling',
 template: `
 <div>
 <button (click)="onClick('Button 1')">Button 1</button>
 <button (click)="onClick('Button 2')">Button 2</button>
 </div>
 `
})
export class EventBubblingComponent {
 @Output() click = new EventEmitter();

 onClick(button: string) {
 this.click.next(button);
 }
}

app.component.ts

import { Component } from '@angular/core';

@Component({
 selector: 'exe-app',
 template: `
 <div>
 <event-bubbling (click)="onClick($event)"></event-bubbling>
 </div>
 `
})
export class AppComponent {
 onClick(event: any) {
 console.log(event);
 }
}

以上代码成功运行后,当用户点击 Button 1 按钮时,浏览器控制台将会输出:

Button 1 
MouseEvent {isTrusted: true, screenX: 69, screenY: 161, clientX: 43, clientY: 22…}

我们看到控制台输出了两个信息,说明在这种情况下,Angular 可以处理自定义事件和 DOM (click) 事件。当 click 事件触发后,onClick() 方法接收的 event 参数是 MouseEvent 对象。

那么要如何修复这个问题呢?我们可以利用 DOM 事件提供的防止冒泡的机制,即 stopPropagation() 方法。具体示例如下:

event-bubbling.component.ts

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
 selector: 'event-bubbling',
 template: `
 <div>
 <button (click)="onClick($event, 'Button 1')">Button 1</button>
 <button (click)="onClick($event, 'Button 2')">Button 2</button>
 </div>
 `
})
export class EventBubblingComponent {
 @Output() click = new EventEmitter();

 onClick(event: Event, button: string) {
 event.stopPropagation();
 this.click.next(button);
 }
}

以上代码成功运行后,当用户点击 Button 1 按钮时,只会输出 Button 1 ,即问题我们已经修复了。

现在我们来总结一下:

DOM 事件冒泡机制,允许在父元素监听由子元素触发的 DOM 事件

Angular 支持 DOM 事件冒泡机制,但不支持自定义事件的冒泡。

自定义事件名称与 DOM 事件的名称如 (click,change,select,submit) 同名,可能会导致问题。虽然可以使用 stopPropagation() 方法解决问题,但实际工作中,不建议这样使用。

Event Modifiers

在实际项目中,我们经常需要在事件处理器中调用 preventDefault() 或 stopPropagation() 方法:

preventDefault() - 如果事件可取消,则取消该事件,意味着该事件的所有默认动作都不会发生。需要注意的是该方法不会阻止该事件的冒泡

stopPropagation() - 阻止当前事件在 DOM 树上冒泡

对于 preventDefault() 方法,有一个经典的应用场景。即当我们希望点击链接在新窗口打开页面,但不希望当前页面跳转。这个时候可以使用 preventDefault() 阻止后面将要执行的浏览器默认动作。

<a id="link" href="https://i.cnblogs.com/EditPosts.aspx?postid=7792556" rel="external nofollow" >Angular 4.x 事件冒泡</a>
<script>
 document.getElementById('link').onclick = function(ev) {
 ev.preventDefault(); // 阻止浏览器默认动作 (页面跳转)
 window.open(this.href); // 在新窗口打开页面
 };
</script>

在 Angular 中阻止 DOM 事件冒泡,我们可以使用以下两种方式:

方式一:

<div>
 <button (click)="$event.stopPropagation(); doSomething()">Click me</button>
</div>

方式二:

@Component({
 selector: 'exe-app',
 template: `
 <div>
 <button (click)="doSomething($event)">Click me</button>
 </div>`
})
export class AboutComponent {
 doSomething($event: Event) {
 $event.stopPropagation();
 // your logic
 }
}

是不是感觉很麻烦,每次都得显式地调用 $event.stopPropagation() 方法。有没有更简便的方法呢?能不能使用声明式的语法?在 Vue 中可以通过声明式的方式,解决我们上面的问题。具体如下:

<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="doThis"></a>

<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- the click event will be triggered at most once -->
<a v-on:click.once="doThis"></a>

接下来我们也来基于 Angular 的指令系统,实现上述的功能。最终的使用示例如下:

<div class="parent" (click)="fromParent()">
 <button class="child" (click.stop)="fromChild()">Click me</button>
</div>

自定义 [click.stop] 指令

@Directive({
 selector: '[click.stop]'
})
export class StopPropagationDirective {
 @Output("click.stop") stopPropEvent = new EventEmitter();
 unsubscribe: () => void;

 constructor(
 private renderer: Renderer2, // Angular 2.x导入Renderer
 private element: ElementRef) {
 }

 ngOnInit() {
 this.unsubscribe = this.renderer.listen(
  this.element.nativeElement, 'click', event => {
  event.stopPropagation();
  this.stopPropEvent.emit(event);
  });
 }

 ngOnDestroy() {
 this.unsubscribe();
 }
}

[click.stop] 指令应用

import { Component } from '@angular/core';

@Component({
 selector: 'exe-app',
 template: `
 <div class="parent" (click)="fromParent()">
 <button class="child" (click.stop)="fromChild()">Click me</button>
 </div>
 `
})
export class AppComponent {
 fromChild() {
 console.log('I am Child');
 }

 fromParent() {
 console.log('I am Parent');
 }
}

以上代码成功运行后,当用户点击 Click me 按钮时,浏览器控制台只会输出 I am Child。若把 (click.stop) 改为 (click) ,当用户再次点击 Click me 按钮时,控制台就会输出两条信息。有兴趣的读者,可以亲自验证一下哈。

这篇Angular4.x Event (DOM事件和自定义事件详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
extjs fckeditor集成代码
May 10 Javascript
Jquery 常用方法经典总结
Jan 28 Javascript
style、 currentStyle、 runtimeStyle区别分析
Aug 01 Javascript
JavaScript实现动画打开半透明提示层的方法
Apr 21 Javascript
理解javascript中DOM事件
Dec 25 Javascript
Bootstrap与KnockoutJs相结合实现分页效果实例详解
May 03 Javascript
CSS3 media queries结合jQuery实现响应式导航
Sep 30 Javascript
JavaScript控制输入框中只能输入中文、数字和英文的方法【基于正则实现】
Mar 03 Javascript
React Native中的RefreshContorl下拉刷新使用
Oct 09 Javascript
js实现复制功能(多种方法集合)
Jan 06 Javascript
微信小程序图片轮播组件gallery slider使用方法详解
Jan 31 Javascript
使用Vue实现一个树组件的示例
Nov 06 Javascript
Vue官方推荐AJAX组件axios.js使用方法详解与API
Oct 09 #Javascript
vue router 源码概览案例分析
Oct 09 #Javascript
Angular4 Select选择改变事件的方法
Oct 09 #Javascript
对angular 监控数据模型变化的事件方法$watch详解
Oct 09 #Javascript
JS判断用户用的哪个浏览器实例详解
Oct 09 #Javascript
vue发送ajax请求详解
Oct 09 #Javascript
AngularJS 监听变量变化的实现方法
Oct 09 #Javascript
You might like
实现PHP多线程异步请求的3种方法
2014/01/17 PHP
php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证
2016/05/04 PHP
PHP 计算两个特别大的整数实例代码
2018/05/07 PHP
Swoole4.4协程抢占式调度器详解
2019/05/23 PHP
由prototype_1.3.1进入javascript殿堂-类的初探
2006/11/06 Javascript
javascript String 对象
2008/04/25 Javascript
js 字符串转化成数字的代码
2011/06/29 Javascript
iphone safari不支持position fixed的解决方法
2012/05/04 Javascript
JS小功能(button选择颜色)简单实例
2013/11/29 Javascript
javascript操作excel生成报表示例
2014/05/08 Javascript
jQuery操作select下拉框的text值和value值的方法
2014/05/31 Javascript
NodeJS学习笔记之Connect中间件模块(一)
2015/01/27 NodeJs
聊一聊Vue.js过渡效果
2016/09/07 Javascript
javascript观察者模式实现自动刷新效果
2017/09/05 Javascript
angular2 ng2 @input和@output理解及示例
2017/10/10 Javascript
详解Vue用自定义指令完成一个下拉菜单(select组件)
2017/10/31 Javascript
JS原生带缩略图的图片切换效果
2018/10/10 Javascript
Vue.js实现备忘录功能
2019/06/26 Javascript
利用PHP实现递归删除链表元素的方法示例
2020/10/23 Javascript
[01:00:06]加油DOTA_EP01_网络版
2014/08/09 DOTA
[47:42]完美世界DOTA2联赛PWL S2 GXR vs Ink 第一场 11.19
2020/11/20 DOTA
Python模块学习 datetime介绍
2012/08/27 Python
python实现基本进制转换的方法
2015/07/11 Python
在阿里云服务器上配置CentOS+Nginx+Python+Flask环境
2016/06/18 Python
Python中shutil模块的学习笔记教程
2017/04/04 Python
在Pandas中给多层索引降级的方法
2018/11/16 Python
linux面试题参考答案(8)
2016/04/19 面试题
会计主管岗位职责
2014/01/03 职场文书
个人作风剖析材料
2014/02/02 职场文书
青年文明号复核材料
2014/02/11 职场文书
幼儿园中班开学寄语
2014/04/03 职场文书
写求职信要注意什么问题
2014/04/12 职场文书
大学生上课迟到检讨书
2014/10/15 职场文书
放弃继承权公证书
2015/01/23 职场文书
文化大革命观后感
2015/06/17 职场文书
信息简报范文
2015/07/21 职场文书