Angular12 - 使用 @HostBinding 或 @HostListener 而不是 `host` 元数据属性

Posted

技术标签:

【中文标题】Angular12 - 使用 @HostBinding 或 @HostListener 而不是 `host` 元数据属性【英文标题】:Angular12 - Use @HostBinding or @HostListener rather than the `host` metadata property 【发布时间】:2021-10-03 02:11:59 【问题描述】:

我正在使用带有 ESLint 的 Angular 12,而 ESLint 打印出以下错误:

70:3 错误使用 @HostBinding 或 @HostListener 而不是 host 元数据属性 (https://angular.io/styleguide#style-06-03) @angular-eslint/no-host-metadata-property

类似的话题还有很多,比如this one,但是我的宿主比较复杂。

  host: 
    '[class.active-menuitem]':
      '(selectedKey && app.isHorizontal()) || (active && !app.isHorizontal()) ' +
      '|| (active && !root && app.isHorizontal())',
    '[class.active-rootmenuitem]': 'active && root && app.isHorizontal()'
  ,

我应该做什么?比如:

@HostBinding('[class.active-menuitem]') menuItem = '(selectedKey && app.isHorizontal()) || (active && !app.isHorizontal()) ' + '|| (active && !root && app.isHorizontal())'; 

?

完整片段:

import  Component, Input, OnInit, ChangeDetectorRef, OnDestroy  from '@angular/core';
import  Router, NavigationEnd  from '@angular/router';
import  trigger, state, style, transition, animate  from '@angular/animations';
import  Subscription  from 'rxjs';
import  filter  from 'rxjs/operators';
import  LayoutComponent  from '../layout.component';
import  MenuService  from '@core/services';

@Component(
  selector: '[app-menu-item]',
  template: `
    <ng-container>
      <a
        [attr.href]="item.url"
        (click)="itemClick($event)"
        *ngIf="!item.routerLink || item.items"
        pRipple
        [ngClass]="item.class"
        (mouseenter)="onMouseEnter()"
        (keydown.enter)="itemClick($event)"
        [attr.target]="item.target"
        [attr.tabindex]="0"
      >
        <i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
        <span class="layout-menuitem-text"> item.label </span>
        <i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
      </a>
      <a
        (click)="itemClick($event)"
        (mouseenter)="onMouseEnter()"
        *ngIf="item.routerLink && !item.items"
        pRipple
        [ngClass]="item.class"
        [routerLink]="item.routerLink"
        routerLinkActive="active-menuitem-routerlink"
        [routerLinkActiveOptions]=" exact: true "
        [attr.target]="item.target"
        [attr.tabindex]="0"
      >
        <i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
        <span class="layout-menuitem-text"> item.label </span>
        <i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
      </a>
      <ul
        *ngIf="item.items && (active || animating || selectedKey)"
        (@children.done)="onAnimationDone()"
        [ngStyle]=" padding: active && root ? '' : '0' "
        [@children]="
          app.isHorizontal() && root && !app.isMobile()
            ? active
              ? 'visible'
              : 'hidden'
            : active
            ? 'visibleAnimated'
            : 'hiddenAnimated'
        "
      >
        <ng-template ngFor let-child let-i="index" [ngForOf]="item.items">
          <li
            app-menu-item
            [item]="child"
            [index]="i"
            [parentKey]="key"
            [class]="child.badgeClass"
          ></li>
        </ng-template>
      </ul>
    </ng-container>
  `,
  host: 
    '[class.active-menuitem]':
      '(selectedKey && app.isHorizontal()) || (active && !app.isHorizontal()) ' +
      '|| (active && !root && app.isHorizontal())',
    '[class.active-rootmenuitem]': 'active && root && app.isHorizontal()'
  ,
  animations: [
    trigger('children', [
      state(
        'void',
        style(
          height: '0px'
        )
      ),
      state(
        'hiddenAnimated',
        style(
          height: '0px'
        )
      ),
      state(
        'visibleAnimated',
        style(
          height: '*'
        )
      ),
      state(
        'visible',
        style(
          height: '*',
          'z-index': 999
        )
      ),
      state(
        'hidden',
        style(
          height: '0px',
          'z-index': '*'
        )
      ),
      transition(
        'visibleAnimated => hiddenAnimated',
        animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')
      ),
      transition(
        'hiddenAnimated => visibleAnimated',
        animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')
      ),
      transition(
        'void => visibleAnimated, visibleAnimated => void',
        animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')
      )
    ])
  ]
)
export class MenuItemComponent implements OnInit, OnDestroy 
  @Input() item: any;
  @Input() index: number;
  @Input() root: boolean;
  @Input() parentKey: string;

  animating: boolean;
  active = false;
  menuSourceSubscription: Subscription;
  menuResetSubscription: Subscription;

  key: string;

  selectedKey: boolean;

  constructor(
    public app: LayoutComponent,
    public router: Router,
    private cd: ChangeDetectorRef,
    private menuService: MenuService
  ) 
    this.menuSourceSubscription = this.menuService.menuSource$.subscribe((key) => 
      // deactivate current active menu
      if (this.active && this.key !== key && key.indexOf(this.key) !== 0) 
        this.active = false;
      
    );

    this.menuResetSubscription = this.menuService.resetSource$.subscribe(() => 
      this.active = false;
    );

    this.router.events
      .pipe(filter((event) => event instanceof NavigationEnd))
      .subscribe((params) => 
        if (this.app.isHorizontal() && this.item.routerLink) 
          this.active = false;
          this.selectedKey = this.router.isActive(
            this.item.routerLink[0],
            this.item.items ? false : true
          );
         else 
          if (this.item.routerLink) 
            this.updateActiveStateFromRoute();
           else 
            this.active = false;
          
        
      );
  

  ngOnInit() 
    if (!this.app.isHorizontal() && this.item.routerLink) 
      this.updateActiveStateFromRoute();
    

    if (this.app.isHorizontal() && this.item.routerLink) 
      this.active = false;
      this.selectedKey = this.router.isActive(
        this.item.routerLink[0],
        this.item.items ? false : true
      );
    

    this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index);
  

  updateActiveStateFromRoute() 
    this.active = this.router.isActive(this.item.routerLink[0], this.item.items ? false : true);
  

  itemClick(event: Event) 
    // avoid processing disabled items
    if (this.item.disabled) 
      event.preventDefault();
      return;
    

    // navigate with hover in horizontal mode
    if (this.root) 
      this.app.menuHoverActive = !this.app.menuHoverActive;
    

    // notify other items
    this.menuService.onMenuStateChange(this.key);

    // execute command
    if (this.item.command) 
      this.item.command( originalEvent: event, item: this.item );
    

    // toggle active state
    if (this.item.items) 
      this.active = !this.active;
      this.animating = true;
     else 
      // activate item
      this.active = true;

      // reset horizontal menu
      if (this.app.isHorizontal()) 
        this.menuService.reset();
      

      this.app.overlayMenuActive = false;
      this.app.overlayMenuMobileActive = false;
      this.app.menuHoverActive = !this.app.menuHoverActive;
    
  

  onMouseEnter() 
    // activate item on hover
    if (this.root && this.app.menuHoverActive && this.app.isHorizontal() && this.app.isDesktop()) 
      this.menuService.onMenuStateChange(this.key);
      this.active = true;
    
  

  onAnimationDone() 
    this.animating = false;
  

  ngOnDestroy() 
    if (this.menuSourceSubscription) 
      this.menuSourceSubscription.unsubscribe();
    

    if (this.menuResetSubscription) 
      this.menuResetSubscription.unsubscribe();
    
  


【问题讨论】:

【参考方案1】:

您的主机绑定应该返回布尔值(根据组件的状态计算),并且当前您正在为其分配一个字符串值。请记住,它是 typescript 文件的一部分,在您的组件中,因此您可以访问所有属性。

所以,这样的事情应该可以工作:

@HostBinding('class.active-menuitem') get activeMenuItem(): boolean 
  return (
    (this.selectedKey && this.app.isHorizontal()) ||
    (this.active && !this.app.isHorizontal()) ||
    (this.active && !this.root && this.app.isHorizontal())
  );

请注意,我不会在类的构建过程中赋值一次。相反,我使用的是带有 getter 的属性。这意味着主机绑定将在每个更改检测周期更新。

【讨论】:

谢谢!就像i.imgur.com/Cx73RiK.png? 嗯,不知何故效果不起作用。我需要在某处指定变量吗?这是用法:&lt;li app-menu-item *ngFor="let item of model; let i = index" [item]="item" [index]="i" [root]="true"&gt;&lt;/li&gt;. 对不起,我犯了一个复制粘贴错误。删除@HostBinding 中的括号,即@HostBinding('class.active-menuitem')。我制作了一个简化的 stackblitz 示例 here - 这取决于传递的 name 值的长度。

以上是关于Angular12 - 使用 @HostBinding 或 @HostListener 而不是 `host` 元数据属性的主要内容,如果未能解决你的问题,请参考以下文章

Angular12 - 使用 @HostBinding 或 @HostListener 而不是 `host` 元数据属性

Angular 12 日期选择器

Angular 12 的 Storybook 插件 StoryShots

2021-07-28 使用BASE加密 encode 数据 在angular12上

Kendo UI Grid Angular (12)

在 Angular 12 反应形式中使用 patchValue 绑定表单数组的最佳方法