gpt4 book ai didi

javascript - Angular 2 中处理 click 和 clickOutside 事件执行优先级的最佳方法?

转载 作者:行者123 更新时间:2023-11-28 05:19:58 27 4
gpt4 key购买 nike

我对 Angular 2 相当陌生,正在构建一个应用程序,该应用程序会在父主机组件中生成同一子组件的多个实例。单击这些组件之一会将其置于编辑模式(显示表单输入),并且单击事件编辑组件外部应将编辑组件替换为默认的只读版本。

@Component({
selector: 'my-app',
template: `
<div *ngFor="let line of lines">

<ng-container *ngIf="!line.edit">
<read-only
[lineData]="line"
></read-only>
</ng-container>

<ng-container *ngIf="line.edit">
<edit
[lineData]="line"
></edit>
</ng-container>
</div>
`,
})

export class App {
name:string;
lines:any[];

constructor() {
this.name = 'Angular2';
this.lines = [{name:'apple'},{name:'pear'},{name'banana'}];
}
}

通过只读组件上的(单击)处理程序将项目置于编辑模式,并通过附加到自定义指令中定义的(clickOutside)事件的处理程序将项目从编辑模式切换出来。

只读组件

@Component({
selector: 'read-only',
template: `
<div
(click)="setEditable()"
>
{{lineData.name}}
</div>
`,
inputs['lineData']
})

export class ReadOnly {

lineData:any;
constructor(){

}

setEditable(){
this.lineData.edit=true;
}
}

编辑组件

@Component({
selector: 'edit',
template: `
<div style="
background-color:#cccc00;
border-width:medium;
border-color:#6677ff;
border-style:solid"

(clickOutside)="releaseEdit()"
>
{{lineData.name}}
`,
inputs['lineData']
})
export class Edit {

lineData:any;
constructor(){

}

releaseEdit(){
console.log('Releasing edit mode for '+this.lineData.name);
delete this.lineData.edit;
}
}

问题是切换到编辑模式的单击事件也被 clickOutside 处理程序拾取。在内部,clickOutside 处理程序由单击事件触发,并测试编辑组件的 nativeElement 是否包含单击目标 - 如果不包含,则会发出 clickOutside 事件。

ClickOutside 指令

@Directive({
selector: '[clickOutside]'
})
export class ClickOutsideDirective {

ready: boolean;


constructor(private _elementRef: ElementRef, private renderer: Renderer) {
}

@Output()
public clickOutside = new EventEmitter<MouseEvent>();

@HostListener('document:click', ['$event', '$event.target'])


public onClick(event: MouseEvent, targetElement: HTMLElement): void {
if (!targetElement) {
return;
}

const clickedInside = this._elementRef.nativeElement.contains(targetElement);

if (!clickedInside) {
this.clickOutside.emit(event);
}
}
}

我尝试将 (clickOutside) 事件动态绑定(bind)到 ngAfterContentInit() 和 ngAfterContentChecked() 生命周期 Hook 中的编辑组件,并使用 Renderer Listen() as detailed here但没有成功。我注意到 native 事件可以通过这种方式绑定(bind),但我无法绑定(bind)到自定义指令的输出。

我已附上Plunk这里演示了这个问题。使用控制台单击元素演示了如何从创建编辑组件的同一单击事件(最后)触发 clickOutside 事件 - 立即将组件恢复为只读模式。

处理这种情况最干净的方法是什么?理想情况下,我可以动态绑定(bind) clickOutside 事件,但尚未找到成功的方法。

最佳答案

部分解决方案(依赖 DOM)

请原谅对自己帖子的回复,但希望这对以类似方式苦苦挣扎的人有用。

上述代码的主要问题是指令定义 (clickOutside) 内的 clickedInside 设置始终测试为 false。

const clickedInside = this._elementRef.nativeElement.contains(targetElement);

原因是 this._elementRef.nativeElement 引用了编辑组件的 DOM 元素,而 targetElement 引用了最初点击的 DOM 元素(即只读组件) )因此条件始终测试为 false,从而触发 clickOutside 事件。

适用于本例中使用的简单 DOM 元素的解决方法是测试每个属性中 trim 后的 HTML 上的字符串相等性。

const name = this.elementRef.nativeElement.children[0].innerHTML.trim();
const clickedInside = name == $event.target.innerHTML.trim();

第二个问题是定义的事件处理程序将持续存在并导致问题,因此我们将更改 ClickOutsideDirective 指令中的点击处理程序,以便在构造函数中动态创建它。

export class ClickOutsideDirective {
@Output() public clickOutside = new EventEmitter<MouseEvent>()

globalListenFunc:Function;

constructor(private elementRef:ElementRef, private renderer:Renderer){

// assign ref to event handler destroyer method returned
// by listenGlobal() method here
this.globalListenFunc = renderer.listenGlobal('document','click',($event)=>{

const name = this.elementRef.nativeElement.innerHTML.trim();
const clickedInside = name == $event.target.innerHTML.trim();

if(!clickedInside){
this.clickOutside.emit(event);
this.globalListenFunction(); //destroy event handler
}
});

}

}
<小时/>

待办事项

这可行,但它很脆弱并且依赖于 DOM。最好找到一个独立于 DOM 的解决方案 - 并且可能使用 @View.. 或 @Content 输入。理想情况下,它还可以提供一种定义要应用的 clickOutside 测试条件的方法,以便 ClickOutsideDirective 真正实现通用和模块化。

有什么想法吗?

关于javascript - Angular 2 中处理 click 和 clickOutside 事件执行优先级的最佳方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40645533/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com