Skip to content

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:

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

Correct:

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 ​

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:

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

Correct:

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 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:

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

Correct:

ts
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:

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

Correct:

ts
@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:

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

Correct:

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;

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:

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

Correct:

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 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:

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

Correct:

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;

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:

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

Correct:

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

@angular-eslint/prefer-inject &ZeroWidthSpace;

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:

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

Correct:

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

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

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:

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

Correct:

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

@angular-eslint/prefer-standalone &ZeroWidthSpace;

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:

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

Correct:

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

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

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:

html
<img src="user.png">

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

html
<button></button>

Correct:

html
<button>Save</button>

@angular-eslint/template/eqeqeq &ZeroWidthSpace;

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

html
<input autofocus>

Correct:

html
<input>

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

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:

html
<marquee>News</marquee>

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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

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

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:

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

Correct:

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;

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:

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

Correct:

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