如何检查 ElementRef 是否包含 angular typescript 中的 mat-icon 节点?
How to check if ElementRef contains a mat-icon node in angular typescript?
我试图通过单击组件外部将 hello 组件切换回使用 viewMode
模板。但是,我的方法如下所示不起作用。单击垫图标时,contains 方法 return false 即使它是组件的一部分。如何编写处理程序来检查点击不是由 mat-icon 或文本触发的?
// app.component.html
<ng-template #viewMode>
This is {{ data }}.
<mat-icon (click)="child.toggleMode()">create</mat-icon>
</ng-template>
<ng-template #editMode>
<form [formGroup]="simpleForm">
<input type="text" formControlName="name">
</form>
<mat-icon (click)="child.toggleMode()">done</mat-icon>
</ng-template>
<app-hello #child [viewModeTemplate]="viewMode" [editModeTemplate]="editMode" (update)="handleUpdate()">
</app-hello>
// app.component.ts
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
data: string = "42";
simpleForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.simpleForm = this.fb.group({
name: [this.data]
});
}
handleUpdate() {
this.data = this.simpleForm.value.name;
}
}
// hello.component.ts
@Component({
selector: "app-hello",
templateUrl: "./hello.component.html"
})
export class HelloComponent {
@Input() viewModeTemplate: TemplateRef<any>;
@Input() editModeTemplate: TemplateRef<any>;
@Output() update = new EventEmitter();
mode: "view" | "edit" = "view";
constructor(private el: ElementRef){
}
get element(){
return this.el.nativeElement;
}
@HostListener('document:click', ['$event'])
editModeHandler(event) {
// not working
// if (!this.element.contains(event.target)){
// this.mode = "view";
// }
}
toggleMode() {
if (this.mode === "edit") {
this.mode = "view";
this.update.next();
} else {
this.mode = "edit";
}
}
}
// hello.component.html
<ng-container *ngTemplateOutlet="mode === 'view' ? viewModeTemplate : editModeTemplate">
</ng-container>
这里是 Stackblitz:https://stackblitz.com/edit/angular-xsgjcb
可以从element.nativeElement.children
获取子节点。
对于您的场景,请尝试以下代码:
@HostListener('document:click', ['$event'])
editModeHandler(event) {
let childnodes = this.element.children;
for(var index in childnodes) {
console.log(childnodes[index].tagName, event.target.tagName)
if(childnodes[index].tagName === event.target.tagName){
this.mode = "view";
break;
}
}
}
使用tagname
,可以找到子节点标签。
它出错了,因为你的 child.toggleNode()
将首先触发,因为事件冒泡:
- 事件达到 child。
- 事件触发切换和交换模板。
- 事件到达文档。
- 事件检查包含,但图标已更改。
如果删除 child.toggleNode()
,您的代码将按预期工作。
您必须推迟切换,直到主体上的 HostListener 触发。您可以使用 RxJS 或仅使用 setTimeout 来做到这一点。我认为最好的解决方案是完全删除 HostListener 并使用 RxJS 执行此操作:
const outsideClick$ = merge(
fromEvent(this.element, 'click').pipe(map(() => false))
fromEvent(document.body, 'click').pipe(map(() => true))
).pipe(
throttleTime(0)
);
我试图通过单击组件外部将 hello 组件切换回使用 viewMode
模板。但是,我的方法如下所示不起作用。单击垫图标时,contains 方法 return false 即使它是组件的一部分。如何编写处理程序来检查点击不是由 mat-icon 或文本触发的?
// app.component.html
<ng-template #viewMode>
This is {{ data }}.
<mat-icon (click)="child.toggleMode()">create</mat-icon>
</ng-template>
<ng-template #editMode>
<form [formGroup]="simpleForm">
<input type="text" formControlName="name">
</form>
<mat-icon (click)="child.toggleMode()">done</mat-icon>
</ng-template>
<app-hello #child [viewModeTemplate]="viewMode" [editModeTemplate]="editMode" (update)="handleUpdate()">
</app-hello>
// app.component.ts
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
data: string = "42";
simpleForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.simpleForm = this.fb.group({
name: [this.data]
});
}
handleUpdate() {
this.data = this.simpleForm.value.name;
}
}
// hello.component.ts
@Component({
selector: "app-hello",
templateUrl: "./hello.component.html"
})
export class HelloComponent {
@Input() viewModeTemplate: TemplateRef<any>;
@Input() editModeTemplate: TemplateRef<any>;
@Output() update = new EventEmitter();
mode: "view" | "edit" = "view";
constructor(private el: ElementRef){
}
get element(){
return this.el.nativeElement;
}
@HostListener('document:click', ['$event'])
editModeHandler(event) {
// not working
// if (!this.element.contains(event.target)){
// this.mode = "view";
// }
}
toggleMode() {
if (this.mode === "edit") {
this.mode = "view";
this.update.next();
} else {
this.mode = "edit";
}
}
}
// hello.component.html
<ng-container *ngTemplateOutlet="mode === 'view' ? viewModeTemplate : editModeTemplate">
</ng-container>
这里是 Stackblitz:https://stackblitz.com/edit/angular-xsgjcb
可以从element.nativeElement.children
获取子节点。
对于您的场景,请尝试以下代码:
@HostListener('document:click', ['$event'])
editModeHandler(event) {
let childnodes = this.element.children;
for(var index in childnodes) {
console.log(childnodes[index].tagName, event.target.tagName)
if(childnodes[index].tagName === event.target.tagName){
this.mode = "view";
break;
}
}
}
使用tagname
,可以找到子节点标签。
它出错了,因为你的 child.toggleNode()
将首先触发,因为事件冒泡:
- 事件达到 child。
- 事件触发切换和交换模板。
- 事件到达文档。
- 事件检查包含,但图标已更改。
如果删除 child.toggleNode()
,您的代码将按预期工作。
您必须推迟切换,直到主体上的 HostListener 触发。您可以使用 RxJS 或仅使用 setTimeout 来做到这一点。我认为最好的解决方案是完全删除 HostListener 并使用 RxJS 执行此操作:
const outsideClick$ = merge(
fromEvent(this.element, 'click').pipe(map(() => false))
fromEvent(document.body, 'click').pipe(map(() => true))
).pipe(
throttleTime(0)
);