Dom元素会通过一些事件通过Dom的层级结构进行传播,这种传播的过程叫做事件冒泡,事件冒泡是指的最内层的事件类型触发导致了父级的Dom触发了相同的事件类型事件,由内到外的一个过程,而由外到内指的是事件捕获。
import { Component } from "@angular/core";
@Component({
selector:'my-component',
template:`
<div (click)="handleClick()">
<button >点击</button>
</div>
`
})
export class AppComponent {
handleClick() {
console.log('触发了点击事件...');
}
}
上述表示div的盒子添加点击事件后被执行。
angular中组件使用事件冒泡
在angular中,我们能使用@Output
装饰器和EventEmitter
自定义事件,它不同于Dom事件,因为它不支持事件冒泡。
比如我们先自定义一个组件:
import { Component } from '@angular/core';
@Component({
selector: 'test-com',
template: `
<div>
<button>Click</button>
</div>
`
})
export class TestComComponent { }
在主程序中使用自定义的组件作为子组件进行使用。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<div>
<test-com (click)="handleClick()"></test-com>
</div>
`
})
export class AppComponent {
handleClick() {
console.log('Click');
}
}
这种情况下可以看到依旧是正常输出的,表示我们在触发点击事件时,触发的是添加在子组件上的click事件,但是如果此时我们的子组件中也有click点击事件呢?
修改上面的自定义组件的代码:
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'test-com',
template: `
<div>
<button (click)="handleClick('child')">Click child</button>
</div>
`
})
export class TestComComponent {
@Output() click = new EventEmitter();
handleClick(button: string) {
this.click.next(button);
}
}
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<div>
<test-com (click)="handleClick($event)"></test-com>
</div>
`
})
export class AppComponent {
handleClick(event: any) {
console.log(event);
}
}
当点击Click child
时,Angular内部会处理自定义事件和DOM事件,当Click事件触发后,handleClick方法接收的参数是MouseEvent对象。
那么可以直接在点击事件时添加stopPropagation()
方法
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'test-com',
template: `
<div>
<button (click)="handleClick('child')">Click child</button>
</div>
`
})
export class TestComComponent {
@Output() click = new EventEmitter();
handleClick(event: Event, button: string) {
event.stopPropagation();
this.click.next(button);
}
}
DOM元素的冒泡机制,允许父元素去监听子元素触发的事件,在Angular框架中,虽然可以直接使用stopPropagation
直接解决问题,但是实际开发中,并不建议这么操作。
Event Modifiers
在dom元素中,两种情形需要注意的点,一种是会触发默认事件,则使用preventDefault()
取消事件的所有默认行为,一种是事件冒泡,使用stopPropagation()
阻止当前事件在Dom上的冒泡。
在Angular中阻止事件冒泡的写法大致有几种:
<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();
}
}
在Angular框架中,我们可以自定义指令声明阻止事件冒泡
import { Directive, Output, EventEmitter, Renderer2, ElementRef } from '@angular/core';
@Directive({
selector: '[click.stop]'
})
export class StopPropagationDirective {
@Output("click.stop") stopPropEvent = new EventEmitter();
unsubscribe: () => void;
constructor(
private renderer: Renderer2,
private element: ElementRef) {
}
ngOnInit() {
this.unsubscribe = this.renderer.listen(
this.element.nativeElement, 'click', event => {
event.stopPropagation();
this.stopPropEvent.emit(event);
});
}
ngOnDestroy() {
this.unsubscribe();
}
}
因此,在子组件中使用click
事件时,只需要(click.stop)="handleClick()"
则可以阻止事件的冒泡,更适合多人开发,避免了遇到一次就使用一次stopPropagation
。
<button (click.stop)="handleClick()">点击</button>
需要注意的是:自定义指令文件创建好后,需要在你使用到的module.ts下声明一下。
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appMyDirective]'
})
export class MyDirectiveDirective {
constructor(public elementRef: ElementRef) { }
@HostListener('keyup', ['$event.target'])
keyupFun(evt:any) {
if (evt.value) {
console.log(this.elementRef);
this.elementRef.nativeElement.value = evt.value.trim();
}
}
}
创建好之后,在module.ts声明
import { MyDirectiveDirective } from "../../../my-directive.directive";
@NgModule({
declarations: [
...
StopPropagationDirective,
MyDirectiveDirective
]
})
image.png
网友评论