Skip to content

Angular 与 Angular 模板 ​

本页记录当前依赖版本和仓库配置最终产生的 28 条规则。每条规则均列出实际严重级别、生效范围、上游说明、常见报告以及错误/正确示例。

仓库显式规则(28 条) ​

@angular-eslint/contextual-lifecycle ​

生命周期方法只能出现在对应的 Angular 组件或指令上下文中,避免无效钩子。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Angular will not invoke the '{{methodName}}' lifecycle method within '@{{classDecoratorName}}()' classes
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Injectable } from "@angular/core";
@Injectable()
class UserService { ngOnInit(): void { console.log("initialized"); } }

正确示例:

ts
import { Component, OnInit } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent implements OnInit { ngOnInit(): void { console.log("initialized"); } }

@angular-eslint/no-empty-lifecycle-method ​

空生命周期方法没有行为且容易误导维护者,应直接移除。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Lifecycle methods should not be emptyRemove lifecycle method
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Component, OnInit } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent implements OnInit { ngOnInit(): void {} }

正确示例:

ts
import { Component, OnInit } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent implements OnInit { ngOnInit(): void { console.log("initialized"); } }

@angular-eslint/no-input-rename ​

Input 别名会让模板 API 与类属性名称分离,增加搜索和重构成本。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Input bindings should not be aliased (https://angular.dev/guide/components/inputs#choosing-input-names)Remove alias nameRemove alias name and use it as the original name
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Component, Input } from "@angular/core";
@Component({ selector: "app-user", template: `` })
class UserComponent { @Input("userName") name = ""; }

正确示例:

ts
import { Component, Input } from "@angular/core";
@Component({ selector: "app-user", template: `` })
class UserComponent { @Input() userName = ""; }

@angular-eslint/no-inputs-metadata-property ​

统一使用 input()@Input 声明输入,避免与 metadata 数组混用两套 API。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Use '@Input' rather than the 'inputs' metadata property
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
@Component({ selector: "app-user", inputs: ["name"], template: `` })
class UserComponent { name = ""; }

正确示例:

ts
@Component({ selector: "app-user", template: `` })
class UserComponent { @Input() name = ""; }

@angular-eslint/no-output-native ​

输出名称不得覆盖原生 DOM 事件,否则模板事件含义容易混淆。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Output bindings, including aliases, should not be named as standard DOM events
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() click = new EventEmitter<void>(); }

正确示例:

ts
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() saved = new EventEmitter<void>(); }

@angular-eslint/no-output-on-prefix &ZeroWidthSpace;

输出本身已经表达事件语义,不使用 on 前缀以保持 Angular 公共 API 约定。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Output bindings, including aliases, should not be named "on", nor prefixed with it (https://angular.dev/guide/components/outputs#choosing-event-names)
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() onSaved = new EventEmitter<void>(); }

正确示例:

ts
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() saved = new EventEmitter<void>(); }

@angular-eslint/no-output-rename &ZeroWidthSpace;

Output 别名会让模板 API 与类属性名称分离,增加搜索和重构成本。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Output bindings should not be aliased (https://angular.dev/guide/components/outputs#choosing-event-names)Remove alias nameRemove alias name and use it as the original name
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output("saved") save = new EventEmitter<void>(); }

正确示例:

ts
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() saved = new EventEmitter<void>(); }

@angular-eslint/no-outputs-metadata-property &ZeroWidthSpace;

统一使用 output()@Output 声明输出,避免与 metadata 数组混用两套 API。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Use '@Output' rather than the 'outputs' metadata property
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
@Component({ selector: "app-editor", outputs: ["saved"], template: `` })
class EditorComponent { saved = new EventEmitter<void>(); }

正确示例:

ts
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() saved = new EventEmitter<void>(); }

@angular-eslint/prefer-inject &ZeroWidthSpace;

Angular 推荐使用 inject() 统一依赖注入写法。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Prefer using the inject() function over constructor parameter injection. Use Angular's migration schematic to automatically refactor: ng generate @angular/core:inject
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
@Injectable()
class UserService { constructor(private readonly api: ApiService) {} }

正确示例:

ts
@Injectable()
class UserService { private readonly api = inject(ApiService); }

@angular-eslint/prefer-on-push-component-change-detection &ZeroWidthSpace;

Angular 22 默认采用 OnPush,禁止组件显式退回 Eager 或旧的 Default 策略。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Components should not opt out of the default 'ChangeDetectionStrategy.OnPush' change detection strategyRemove 'changeDetection' to use the default ('ChangeDetectionStrategy.OnPush')'changeDetection: ChangeDetectionStrategy.OnPush' is redundant because 'ChangeDetectionStrategy.OnPush' is the default change detection strategy
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { ChangeDetectionStrategy, Component } from "@angular/core";
@Component({ selector: "app-user", template: ``, changeDetection: ChangeDetectionStrategy.Eager })
class UserComponent {}

正确示例:

ts
import { Component } from "@angular/core";
@Component({ selector: "app-user", template: `` })
class UserComponent {}

@angular-eslint/prefer-standalone &ZeroWidthSpace;

独立组件是现代 Angular 的默认模型。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Components, Directives and Pipes should not opt out of standalone. Following this guide is highly recommended: https://angular.dev/reference/migrations/standaloneQuickly remove 'standalone: false'. NOTE - Following this guide is highly recommended: https://angular.dev/reference/migrations/standalone
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
@Component({ selector: "app-user", standalone: false, template: `` })
class UserComponent {}

正确示例:

ts
@Component({ selector: "app-user", standalone: true, template: `` })
class UserComponent {}

@angular-eslint/template/alt-text &ZeroWidthSpace;

图片和图像型元素需要替代文本,确保非视觉用户能获得等价信息。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:<{{element}}/> element must have a text alternative.
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<img src="user.png">

正确示例:

html
<img src="user.png" alt="User avatar">

@angular-eslint/template/banana-in-box &ZeroWidthSpace;

双向绑定必须使用 [(...)] 顺序,反写的“香蕉盒”通常是模板笔误。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Invalid binding syntax. Use [(expr)] instead
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<input ([ngModel])="name">

正确示例:

html
<input [(ngModel)]="name">

@angular-eslint/template/click-events-have-key-events &ZeroWidthSpace;

click 交互需要键盘等价入口,避免仅鼠标用户可操作。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:click must be accompanied by either keyup, keydown or keypress event for accessibility.click must be accompanied by a keyup, keydown or keypress event that specifies a key (e.g. '(keydown.enter)') for accessibility.click must be accompanied by a keyup, keydown or keypress event for one of the allowed keys ({{allowedKeyCodes}}) for accessibility.
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<div (click)="open()">Open</div>

正确示例:

html
<button type="button" (click)="open()">Open</button>

@angular-eslint/template/elements-content &ZeroWidthSpace;

需要可访问名称的元素不得为空,避免读屏软件播报无意义控件。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:<{{element}}> should have content
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<button></button>

正确示例:

html
<button>Save</button>

@angular-eslint/template/eqeqeq &ZeroWidthSpace;

模板比较使用严格等号,避免 Angular 表达式中的隐式类型转换。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Expected '{{expectedOperation}}' but received '{{actualOperation}}'Replace '{{actualOperation}}' with '{{expectedOperation}}'
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<p *ngIf="count == 0">Empty</p>

正确示例:

html
<p *ngIf="count === 0">Empty</p>

@angular-eslint/template/interactive-supports-focus &ZeroWidthSpace;

具有交互语义的元素必须可聚焦,确保键盘导航能够到达。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Elements with interaction handlers must be focusable.
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<div role="button" (click)="save()">Save</div>

正确示例:

html
<div role="button" tabindex="0" (click)="save()" (keydown.enter)="save()">Save</div>

@angular-eslint/template/label-has-associated-control &ZeroWidthSpace;

表单 label 必须关联控件,扩大可点击区域并为辅助技术提供名称。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:A label component must be associated with a form element
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<label>Name</label><input>

正确示例:

html
<label for="name">Name</label><input id="name">

@angular-eslint/template/mouse-events-have-key-events &ZeroWidthSpace;

mouseovermouseout 行为需要对应键盘焦点事件,保持输入方式等价。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:'{{mouseEvent}}' must be accompanied by '{{keyEvent}}' for accessibility (https://www.w3.org/WAI/WCAG21/Understanding/keyboard)
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<div (mouseover)="showDetails()">Details</div>

正确示例:

html
<div (mouseover)="showDetails()" (focus)="showDetails()" tabindex="0">Details</div>

@angular-eslint/template/no-autofocus &ZeroWidthSpace;

autofocus 会突然移动焦点并干扰读屏流程,默认禁止。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:The 'autofocus' attribute should not be used, as it reduces usability and accessibility for users
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<input autofocus>

正确示例:

html
<input>

@angular-eslint/template/no-distracting-elements &ZeroWidthSpace;

禁止 marqueeblink 等干扰性元素,避免可读性和可访问性问题。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Do not use <{{element}}> elements as they can create visual accessibility issues and are deprecated
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<marquee>News</marquee>

正确示例:

html
<p aria-live="polite">News</p>

@angular-eslint/template/no-negated-async &ZeroWidthSpace;

async pipe 结果直接取反会让初始 null 状态产生反直觉分支。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Async pipe results should not be negated. Use '(observable | async) === false', '(observable | async) === null', or '(observable | async) === undefined' to check its value insteadValues used with the async pipe should not be negated.Compare with 'false'
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<p *ngIf="!(ready$ | async)">Loading</p>

正确示例:

html
<p *ngIf="(ready$ | async) === false">Loading</p>

@angular-eslint/template/prefer-control-flow &ZeroWidthSpace;

采用现代 @if@for 控制流,与 Angular 推荐模板语法保持一致。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:Use built-in control flow instead of directive {{name}}.
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<p *ngIf="ready">Ready</p>

正确示例:

html
@if (ready) { <p>Ready</p> }

@angular-eslint/template/role-has-required-aria &ZeroWidthSpace;

ARIA role 必须提供该角色要求的属性,避免声明不完整的语义。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:The {{element}} with role="{{role}}" does not have required ARIA properties: {{missingProps}}Remove role '{{role}}'
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<div role="checkbox">Enabled</div>

正确示例:

html
<div role="checkbox" aria-checked="false" tabindex="0">Enabled</div>

@angular-eslint/template/table-scope &ZeroWidthSpace;

scope 只能用于表头 <th>,避免在普通单元格上声明无效的表头范围。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:The 'scope' attribute should only be on the '<th>' element
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<td scope="col">Name</td>

正确示例:

html
<th scope="col">Name</th>

@angular-eslint/template/valid-aria &ZeroWidthSpace;

ARIA 属性名和值必须有效,避免浏览器静默忽略错误语义。

  • 生效级别与范围:error: Angular HTML
  • 自动修复:不支持或上游未声明
  • 规则来源:官方文档
  • 常见报告:The '{{attribute}}' is an invalid ARIA attributeThe '{{attribute}}' has an invalid value. Check the valid values at https://raw.githack.com/w3c/aria/stable/#rolesRemove attribute '{{attribute}}'
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

html
<button aria-labl="Save">Save</button>

正确示例:

html
<button aria-label="Save">Save</button>

@angular-eslint/use-lifecycle-interface &ZeroWidthSpace;

生命周期接口让方法拼写和签名可由 TypeScript 验证;警告级别便于渐进补齐旧代码。

  • 生效级别与范围:warn: Angular TypeScript
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Lifecycle interface '{{interfaceName}}' should be implemented for method '{{methodName}}'. (https://angular.dev/style-guide#use-lifecycle-hook-interfaces)
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
import { Component } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent { ngOnInit(): void { console.log("initialized"); } }

正确示例:

ts
import { Component, OnInit } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent implements OnInit { ngOnInit(): void { console.log("initialized"); } }

@angular-eslint/use-pipe-transform-interface &ZeroWidthSpace;

Pipe 类实现接口后可由 TypeScript 检查 transform 签名,避免运行时才发现不一致。

  • 生效级别与范围:error: Angular TypeScript
  • 自动修复:支持;执行前仍应检查语义和差异
  • 规则来源:官方文档
  • 常见报告:Pipes should implement 'PipeTransform' interface
  • 示例类型:仓库显式规则的直接代码示例

错误示例:

ts
@Pipe({ name: "label" })
class LabelPipe { transform(value: string): string { return value.trim(); } }

正确示例:

ts
@Pipe({ name: "label" })
class LabelPipe implements PipeTransform { transform(value: string): string { return value.trim(); } }