ESLint - 选择器应该用作元素

ESLint - The selector should be used as an element

我正在使用 Angular 12 和 ESLint。每当我执行 ng lint 时,ESLint 都会对下面的选择器生气。

10:13 error The selector should be used as an element (https://angular.io/guide/styleguide#style-05-03) @angular-eslint/component-selector

用法:

<li app-menu-item *ngFor="let item of model; let i = index" [item]="item" [index]="i" [root]="true"></li>

我可以从 .eslintrc.json 中删除该规则,但我宁愿修复它。我该如何解决?

import { Component, Input, OnInit, ChangeDetectorRef, OnDestroy, HostBinding } 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>
  `,
  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 {
  @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())
    );
  }

  @HostBinding('class.active-rootmenuitem') get activeRootMenuItem(): boolean {
    return this.active && this.root && this.app.isHorizontal();
  }

  @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();
    }
  }
}

组件选择器应该作为元素使用,即

<app-menu-item *ngFor="let item of model; let i = index" [item]="item" [index]="i" [root]="true"></app-menu-item>

Plus 选择器应该没有括号(使用预定义的 eslint 配置)

@Component({
  selector: 'app-menu-item',
...

或者您可以更改