Javascript 在Angular 2中处理click和clickOutside事件执行优先级的最佳方法?
我是Angular 2的新手,正在构建一个应用程序,它在父主机组件中生成同一子组件的多个实例。单击其中一个组件将其置于编辑模式(显示表单输入),在激活的编辑组件外部单击应将编辑组件替换为默认只读版本Javascript 在Angular 2中处理click和clickOutside事件执行优先级的最佳方法?,javascript,angularjs,events,angular,Javascript,Angularjs,Events,Angular,我是Angular 2的新手,正在构建一个应用程序,它在父主机组件中生成同一子组件的多个实例。单击其中一个组件将其置于编辑模式(显示表单输入),在激活的编辑组件外部单击应将编辑组件替换为默认只读版本 @Component({ selector: 'my-app', template: ` <div *ngFor="let line of lines"> <ng-container *ngIf="!line.edit"> <rea
@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'}];
}
}
@组件({
选择器:“我的应用程序”,
模板:`
`,
})
导出类应用程序{
名称:字符串;
行:任何[];
构造函数(){
this.name='Angular2';
this.lines=[{name:'apple'},{name:'pear'},{name'banana'}];
}
}
只读组件上的(单击)处理程序将项目置于编辑模式,类似地,附加到自定义指令中定义的(单击外部)事件的处理程序将项目切换出编辑模式
只读组件
@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;
}
}
@组件({
选择器:“只读”,
模板:`
{{lineData.name}
`,
输入['lineData']
})
导出类只读{
线条数据:任何;
构造函数(){
}
setEditable(){
this.lineData.edit=true;
}
}
编辑组件
@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;
}
}
@组件({
选择器:“编辑”,
模板:`
{{lineData.name}
`,
输入['lineData']
})
导出类编辑{
线条数据:任何;
构造函数(){
}
releaseEdit(){
console.log('为'+this.lineData.name'释放编辑模式);
删除this.lineData.edit;
}
}
问题是切换到编辑模式的单击事件也会被clickOutside处理程序拾取。在内部,clickOutside处理程序由click事件触发,并测试编辑组件的nativeElement是否包含click目标-如果不包含,则发出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);
}
}
}
@Directive({
selector: '[clickOutside]'
})
export class ClickOutsideDirective {
@Output() public clickOutside = new EventEmitter<MouseEvent>()
localevent:any;
lineData:any;
globalListenFunc:Function;
localListenFunc:Function;
ready:boolean;
constructor(private elementRef:ElementRef, private renderer:Renderer){
this._initClickOutside();
}
_initHandlers(){
this.localListenFunc = this.renderer.listen(this.elementRef.nativeElement,'click',($event)=>{
this.localevent = $event;
});
}
_initClickOutside(){
this.globalListenFunc = this.renderer.listenGlobal('document','click',($event)=>{
if(!this.ready){
this.ready = true;
return;
}
if(!this.localListenFunc) this._initHandlers();
const clickedInside = ($event == this.localevent);
if(!clickedInside){
this.clickOutside.emit($event);
delete this.localevent;
}
});
}
ngOnDestroy() {
// remove event handlers to prevent memory leaks etc.
this.globalListenFunc();
this.localListenFunc();
}
}
@指令({
选择器:“[单击外部]”
})
导出类ClickOutside指令{
就绪:布尔;
构造函数(私有_elementRef:elementRef,私有呈现器:呈现器){
}
@输出()
public clickOutside=new EventEmitter();
@HostListener('文档:单击',['$event','$event.target']))
public onClick(事件:MouseEvent,targetElement:HTMLElement):void{
如果(!targetElement){
返回;
}
const clickedInside=此。_elementRef.nativeElement.contains(targetElement);
如果(!clickedInside){
点击outside.emit(事件);
}
}
}
我尝试过在ngAfterContentInit()和ngAfterContentChecked()生命周期挂钩中,使用Renderer listen()将(clickOutside)事件动态绑定到编辑组件,但没有成功。我注意到本机事件可以通过这种方式绑定,但我无法绑定到自定义指令的输出
我在这里附上了一个例子来说明这个问题。使用console up单击元素可以演示如何在创建编辑组件的相同单击事件中触发(上次)clickOutside事件-立即将组件恢复为只读模式
处理这种情况最干净的方法是什么?理想情况下,我可以动态绑定clickOutside事件,但尚未找到成功的方法。部分解决方案(依赖于DOM)
请原谅回复自己的帖子,但希望这对以类似方式挣扎的人有用
上面代码的主要问题是,定义指令(clickOutside)内部的ClickedSide设置总是测试为false
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
原因是this.\u elementRef.nativeElement
引用编辑组件的DOM元素,而targetElement
引用最初单击的DOM元素(即只读组件),因此条件始终测试为false,触发clickOutside事件
本例中使用的简单DOM元素的一个解决方法是测试每个属性中修剪后的HTML的字符串相等性
const name = this.elementRef.nativeElement.children[0].innerHTML.trim();
const clickedInside = name == $event.target.innerHTML.trim();
第二个问题是,定义的事件处理程序将持续存在并导致问题,因此我们将更改ClickOutside指令中的click处理程序,以便在构造函数中动态创建它
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
}
});
}
}
导出类ClickOutside指令{
@Output()public clickout=new EventEmitter()
globalListenFunc:函数;
构造函数(private-elementRef:elementRef,private-renderer:renderer){
//将ref分配给返回的事件处理程序销毁程序方法
//通过这里的listenGlobal()方法
this.globalListenFunc=renderer.listenGlobal('document','click',($event)=>{
const name=this.elementRef.nativeElement.innerHTML.trim();
const clickedInside=name=$event.target.innerHTML.trim();
如果(!clickedInside){
点击outside.emit(事件);
this.globalListenFunction();//销毁事件处理程序
}
});
}
}
待办事项 这是可行的,但其脆弱性和DOM依赖性。最好找到一个独立于DOM的解决方案,并且可能使用@View。。或@内容输入。理想情况下,它还将提供一种定义要应用的clickOutside测试条件的方法,以便clickOutside指令是真正通用和模块化的 有什么想法吗?部分解决方案(依赖于DOM) 请原谅回复自己的帖子,但希望这对以类似方式挣扎的人有用 上面代码的主要问题是,定义指令(clickOutside)内部的ClickedSide设置总是测试为false
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
原因是this.\u elementRef.nativeElement
引用编辑组件的DOM元素,而targetElement
引用最初单击的DOM元素(即只读组件),因此条件始终测试为false,触发clickOutside事件
在本例中使用的简单DOM元素的一个变通方法是测试修剪后的字符串是否相等