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
中删除该规则,但我宁愿修复它。我该如何解决?
菜单-item.component.ts
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',
...
或者您可以更改
我正在使用 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
中删除该规则,但我宁愿修复它。我该如何解决?
菜单-item.component.ts
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',
...
或者您可以更改