如何检查 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() 将首先触发,因为事件冒泡:

  1. 事件达到 child。
  2. 事件触发切换和交换模板。
  3. 事件到达文档。
  4. 事件检查包含,但图标已更改。

如果删除 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)
);