Angular and Angular templates ​
This page documents 28 rules from the current dependency versions and repository configuration. Each entry includes effective severity, scope, upstream description, common messages, and incorrect/correct examples.
Explicit repository rules (28 rules) ​
@angular-eslint/contextual-lifecycle ​
Lifecycle methods must appear in the appropriate Angular component or directive context to avoid ineffective hooks.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Angular will not invoke the '{{methodName}}' lifecycle method within '@{{classDecoratorName}}()' classes - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Injectable } from "@angular/core";
@Injectable()
class UserService { ngOnInit(): void { console.log("initialized"); } }Correct:
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 ​
Empty lifecycle methods have no behavior and can mislead maintainers; remove them.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Lifecycle methods should not be empty;Remove lifecycle method - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Component, OnInit } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent implements OnInit { ngOnInit(): void {} }Correct:
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 aliases separate template API names from class properties, increasing search and refactoring costs.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Input bindings should not be aliased (https://angular.dev/guide/components/inputs#choosing-input-names);Remove alias name;Remove alias name and use it as the original name - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Component, Input } from "@angular/core";
@Component({ selector: "app-user", template: `` })
class UserComponent { @Input("userName") name = ""; }Correct:
import { Component, Input } from "@angular/core";
@Component({ selector: "app-user", template: `` })
class UserComponent { @Input() userName = ""; }@angular-eslint/no-inputs-metadata-property ​
Declare inputs with input() or @Input; do not mix them with metadata arrays.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Use '@Input' rather than the 'inputs' metadata property - Example type: Direct code example for an explicit repository rule
Incorrect:
@Component({ selector: "app-user", inputs: ["name"], template: `` })
class UserComponent { name = ""; }Correct:
@Component({ selector: "app-user", template: `` })
class UserComponent { @Input() name = ""; }@angular-eslint/no-output-native ​
Output names must not shadow native DOM events, which would make template event semantics ambiguous.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Output bindings, including aliases, should not be named as standard DOM events - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() click = new EventEmitter<void>(); }Correct:
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 ​
Outputs already express events; omit the on prefix to follow Angular public API conventions.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Output bindings, including aliases, should not be named "on", nor prefixed with it (https://angular.dev/guide/components/outputs#choosing-event-names) - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() onSaved = new EventEmitter<void>(); }Correct:
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() saved = new EventEmitter<void>(); }@angular-eslint/no-output-rename ​
Output aliases separate template API names from class properties, increasing search and refactoring costs.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Output bindings should not be aliased (https://angular.dev/guide/components/outputs#choosing-event-names);Remove alias name;Remove alias name and use it as the original name - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Component, EventEmitter, Output } from "@angular/core";
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output("saved") save = new EventEmitter<void>(); }Correct:
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 ​
Declare outputs with output() or @Output; do not mix them with metadata arrays.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Use '@Output' rather than the 'outputs' metadata property - Example type: Direct code example for an explicit repository rule
Incorrect:
@Component({ selector: "app-editor", outputs: ["saved"], template: `` })
class EditorComponent { saved = new EventEmitter<void>(); }Correct:
@Component({ selector: "app-editor", template: `` })
class EditorComponent { @Output() saved = new EventEmitter<void>(); }@angular-eslint/prefer-inject ​
Angular recommends inject() for consistent dependency injection.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Prefer using the inject() function over constructor parameter injection. Use Angular's migration schematic to automatically refactor: ng generate @angular/core:inject - Example type: Direct code example for an explicit repository rule
Incorrect:
@Injectable()
class UserService { constructor(private readonly api: ApiService) {} }Correct:
@Injectable()
class UserService { private readonly api = inject(ApiService); }@angular-eslint/prefer-on-push-component-change-detection ​
Angular 22 defaults to OnPush; components must not explicitly fall back to Eager or legacy Default.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Components should not opt out of the default 'ChangeDetectionStrategy.OnPush' change detection strategy;Remove 'changeDetection' to use the default ('ChangeDetectionStrategy.OnPush');'changeDetection: ChangeDetectionStrategy.OnPush' is redundant because 'ChangeDetectionStrategy.OnPush' is the default change detection strategy - Example type: Direct code example for an explicit repository rule
Incorrect:
import { ChangeDetectionStrategy, Component } from "@angular/core";
@Component({ selector: "app-user", template: ``, changeDetection: ChangeDetectionStrategy.Eager })
class UserComponent {}Correct:
import { Component } from "@angular/core";
@Component({ selector: "app-user", template: `` })
class UserComponent {}@angular-eslint/prefer-standalone ​
Standalone components are the modern Angular default.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Components, Directives and Pipes should not opt out of standalone. Following this guide is highly recommended: https://angular.dev/reference/migrations/standalone;Quickly remove 'standalone: false'. NOTE - Following this guide is highly recommended: https://angular.dev/reference/migrations/standalone - Example type: Direct code example for an explicit repository rule
Incorrect:
@Component({ selector: "app-user", standalone: false, template: `` })
class UserComponent {}Correct:
@Component({ selector: "app-user", standalone: true, template: `` })
class UserComponent {}@angular-eslint/template/alt-text ​
Images need alternative text conveying equivalent information to nonvisual users.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
<{{element}}/> element must have a text alternative. - Example type: Direct code example for an explicit repository rule
Incorrect:
<img src="user.png">Correct:
<img src="user.png" alt="User avatar">@angular-eslint/template/banana-in-box ​
Two-way bindings must use [(...)]; reversed syntax is generally a template typo.
- Effective severity and scope: error: Angular HTML
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Invalid binding syntax. Use [(expr)] instead - Example type: Direct code example for an explicit repository rule
Incorrect:
<input ([ngModel])="name">Correct:
<input [(ngModel)]="name">@angular-eslint/template/click-events-have-key-events ​
Click interactions need keyboard equivalents.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
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. - Example type: Direct code example for an explicit repository rule
Incorrect:
<div (click)="open()">Open</div>Correct:
<button type="button" (click)="open()">Open</button>@angular-eslint/template/elements-content ​
Elements requiring accessible names must not be empty.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
<{{element}}> should have content - Example type: Direct code example for an explicit repository rule
Incorrect:
<button></button>Correct:
<button>Save</button>@angular-eslint/template/eqeqeq ​
Use strict equality in templates to avoid implicit conversions.
- Effective severity and scope: error: Angular HTML
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Expected '{{expectedOperation}}' but received '{{actualOperation}}';Replace '{{actualOperation}}' with '{{expectedOperation}}' - Example type: Direct code example for an explicit repository rule
Incorrect:
<p *ngIf="count == 0">Empty</p>Correct:
<p *ngIf="count === 0">Empty</p>@angular-eslint/template/interactive-supports-focus ​
Interactive elements must be focusable for keyboard navigation.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Elements with interaction handlers must be focusable. - Example type: Direct code example for an explicit repository rule
Incorrect:
<div role="button" (click)="save()">Save</div>Correct:
<div role="button" tabindex="0" (click)="save()" (keydown.enter)="save()">Save</div>@angular-eslint/template/label-has-associated-control ​
Form labels must be associated with controls for accessible naming and a larger click target.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
A label component must be associated with a form element - Example type: Direct code example for an explicit repository rule
Incorrect:
<label>Name</label><input>Correct:
<label for="name">Name</label><input id="name">@angular-eslint/template/mouse-events-have-key-events ​
Mouseover and mouseout behavior needs corresponding keyboard-focus events.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
'{{mouseEvent}}' must be accompanied by '{{keyEvent}}' for accessibility (https://www.w3.org/WAI/WCAG21/Understanding/keyboard) - Example type: Direct code example for an explicit repository rule
Incorrect:
<div (mouseover)="showDetails()">Details</div>Correct:
<div (mouseover)="showDetails()" (focus)="showDetails()" tabindex="0">Details</div>@angular-eslint/template/no-autofocus ​
Autofocus can unexpectedly move focus and disrupt screen readers, so it is prohibited by default.
- Effective severity and scope: error: Angular HTML
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
The 'autofocus' attribute should not be used, as it reduces usability and accessibility for users - Example type: Direct code example for an explicit repository rule
Incorrect:
<input autofocus>Correct:
<input>@angular-eslint/template/no-distracting-elements ​
Disallows distracting elements such as marquee and blink.
- Effective severity and scope: error: Angular HTML
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Do not use <{{element}}> elements as they can create visual accessibility issues and are deprecated - Example type: Direct code example for an explicit repository rule
Incorrect:
<marquee>News</marquee>Correct:
<p aria-live="polite">News</p>@angular-eslint/template/no-negated-async ​
Negating an async-pipe result can produce counterintuitive branches during its initial null state.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Async pipe results should not be negated. Use '(observable | async) === false', '(observable | async) === null', or '(observable | async) === undefined' to check its value instead;Values used with the async pipe should not be negated.;Compare with 'false' - Example type: Direct code example for an explicit repository rule
Incorrect:
<p *ngIf="!(ready$ | async)">Loading</p>Correct:
<p *ngIf="(ready$ | async) === false">Loading</p>@angular-eslint/template/prefer-control-flow ​
Use modern @if and @for control flow.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
Use built-in control flow instead of directive {{name}}. - Example type: Direct code example for an explicit repository rule
Incorrect:
<p *ngIf="ready">Ready</p>Correct:
@if (ready) { <p>Ready</p> }@angular-eslint/template/role-has-required-aria ​
ARIA roles must supply their required attributes.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
The {{element}} with role="{{role}}" does not have required ARIA properties: {{missingProps}};Remove role '{{role}}' - Example type: Direct code example for an explicit repository rule
Incorrect:
<div role="checkbox">Enabled</div>Correct:
<div role="checkbox" aria-checked="false" tabindex="0">Enabled</div>@angular-eslint/template/table-scope ​
Use scope only on table headers (<th>).
- Effective severity and scope: error: Angular HTML
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
The 'scope' attribute should only be on the '<th>' element - Example type: Direct code example for an explicit repository rule
Incorrect:
<td scope="col">Name</td>Correct:
<th scope="col">Name</th>@angular-eslint/template/valid-aria ​
ARIA property names and values must be valid.
- Effective severity and scope: error: Angular HTML
- Autofix: Unsupported or not declared upstream
- Rule source: Official documentation
- Common messages:
The '{{attribute}}' is an invalid ARIA attribute;The '{{attribute}}' has an invalid value. Check the valid values at https://raw.githack.com/w3c/aria/stable/#roles;Remove attribute '{{attribute}}' - Example type: Direct code example for an explicit repository rule
Incorrect:
<button aria-labl="Save">Save</button>Correct:
<button aria-label="Save">Save</button>@angular-eslint/use-lifecycle-interface ​
Lifecycle interfaces let TypeScript check method names and signatures; warning severity supports gradual adoption.
- Effective severity and scope: warn: Angular TypeScript
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Lifecycle interface '{{interfaceName}}' should be implemented for method '{{methodName}}'. (https://angular.dev/style-guide#use-lifecycle-hook-interfaces) - Example type: Direct code example for an explicit repository rule
Incorrect:
import { Component } from "@angular/core";
@Component({ selector: "app-page", template: `` })
class PageComponent { ngOnInit(): void { console.log("initialized"); } }Correct:
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 ​
Implementing the Pipe interface lets TypeScript check the transform signature.
- Effective severity and scope: error: Angular TypeScript
- Autofix: Supported; review semantics and the diff before applying
- Rule source: Official documentation
- Common messages:
Pipes should implement 'PipeTransform' interface - Example type: Direct code example for an explicit repository rule
Incorrect:
@Pipe({ name: "label" })
class LabelPipe { transform(value: string): string { return value.trim(); } }Correct:
@Pipe({ name: "label" })
class LabelPipe implements PipeTransform { transform(value: string): string { return value.trim(); } }