Angular template validation rules that cover accessibility, performance, correctness, and best practices for Angular component templates.
Prevents common two-way binding syntax mistakes (banana in a box pattern).
/**
* Ensures correct two-way binding syntax [()] instead of ([])
* Prevents common template binding mistakes
*/
export class TemplateBananaInBoxRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Usage Examples:
TSLint configuration:
{
"rules": {
"template-banana-in-box": true
}
}Correct two-way binding:
<input [(ngModel)]="value">Incorrect (banana in box):
<input ([ngModel])="value"> <!-- Wrong: parentheses inside brackets -->Prevents function calls in Angular templates for performance reasons.
/**
* Prevents function calls in templates that execute on every change detection
* Improves application performance by avoiding unnecessary computations
*/
export class TemplateNoCallExpressionRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Usage Examples:
Bad (function calls in template):
<div>{{ getFullName() }}</div>
<button (click)="calculate().then(handleResult)">Calculate</button>Good (use properties or pipes):
<div>{{ fullName }}</div>
<div>{{ userData | fullName }}</div>
<button (click)="handleCalculate()">Calculate</button>Prevents use of 'any' type in Angular templates.
/**
* Prevents use of '$any()' type assertion in templates
* Maintains type safety in Angular templates
*/
export class TemplateNoAnyRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Prevents negation of async pipe results which can cause issues.
/**
* Prevents negating async pipe results in templates
* Avoids potential issues with truthy/falsy evaluation of observables
*/
export class TemplateNoNegatedAsyncRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Usage Examples:
Bad:
<div *ngIf="!(data$ | async)">No data</div>Good:
<div *ngIf="(data$ | async) === null">No data</div>
<div *ngIf="(data$ | async)?.length === 0">No data</div>Enforces use of trackBy functions with *ngFor for performance.
/**
* Enforces trackBy functions with *ngFor directives
* Improves performance by helping Angular track list changes efficiently
*/
export class TemplateUseTrackByFunctionRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Usage Examples:
Good with trackBy:
<div *ngFor="let item of items; trackBy: trackByFn">{{ item.name }}</div>Component:
trackByFn(index: number, item: any) {
return item.id;
}Measures and limits conditional complexity in templates.
/**
* Enforces maximum conditional complexity in Angular templates
* Promotes readable templates by limiting nested conditionals
*/
export class TemplateConditionalComplexityRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Measures cyclomatic complexity in Angular templates.
/**
* Measures cyclomatic complexity in Angular templates
* Helps identify overly complex templates that should be refactored
*/
export class TemplateCyclomaticComplexityRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Enforces internationalization attributes for text content.
/**
* Enforces i18n attributes on elements with text content
* Ensures application text is properly marked for internationalization
*/
export class TemplateI18nRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Usage Examples:
Good with i18n:
<p i18n="@@welcome.message">Welcome to our application</p>
<button i18n="@@button.save">Save</button>Prevents use of autofocus attribute for accessibility reasons.
/**
* Prevents autofocus attribute usage in templates
* Improves accessibility by avoiding unexpected focus changes
*/
export class TemplateNoAutofocusRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Prevents use of distracting HTML elements like <blink> and <marquee>.
/**
* Prevents use of distracting HTML elements
* Improves accessibility and user experience
*/
export class TemplateNoDistractingElementsRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Ensures click events have corresponding keyboard events for accessibility.
/**
* Ensures click events have corresponding keyboard event handlers
* Improves accessibility by providing keyboard navigation alternatives
*/
export class TemplateClickEventsHaveKeyEventsRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}Usage Examples:
Good with keyboard events:
<div (click)="handleClick()" (keyup.enter)="handleClick()" (keyup.space)="handleClick()">
Clickable content
</div>Ensures mouse events have corresponding keyboard alternatives.
/**
* Ensures mouse events have corresponding keyboard event alternatives
* Maintains accessibility for keyboard-only users
*/
export class TemplateMouseEventsHaveKeyEventsRule extends Lint.Rules.AbstractRule {
static readonly metadata: Lint.IRuleMetadata;
static readonly FAILURE_STRING: string;
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}{
"rules": {
"template-banana-in-box": true,
"template-no-call-expression": true,
"template-no-any": true,
"template-no-negated-async": true,
"template-use-track-by-function": true,
"template-conditional-complexity": [true, 4],
"template-cyclomatic-complexity": [true, 5],
"template-i18n": [true, "check-id", "check-text"],
"template-no-autofocus": true,
"template-no-distracting-elements": true,
"template-click-events-have-key-events": true,
"template-mouse-events-have-key-events": true
}
}Template rules help identify performance bottlenecks:
Many template rules enforce accessibility best practices: