Angular cheatsheet
January 19, 2026
This post is a amalgamation of knowledge and other resources, to help me remember useful tools/tricks to build complex Angular web-applications.
Note that as of writing, the latest version of Angular v21.
Templating
When checking if a signal exists, you can assign the value of the signal to a new variable, within the if statement. (documentation)
@if (signalValue(); as value) { <p>{{ value }}</p> }
When using a for-loop in a template, you can use
@emptyto provide markup for when there are no items in the list. (documentation)@for (let item of items(); track $index) { <p>{{ item }}</p> } @empty { <p>no items!</p> }
[routerOutletData]allows for passing of data to sub-routes// app.component.ts export interface OutletData { username: string; } @Component({ template: `@if (outletData(); as data) { <router-outlet [routerOutletData]="data" /> } @else { <p>Loading...</p> }` }) export class AppComponent { outletData = signal<OutletData | null>(null); }Then, the sub-route could look like this:
// home.component.ts import { type OutletData } from 'app.component.ts'; @Component({ template: `<p>Hi, {{ outlet().username }}!</p>` }) export class ProjectPage { outlet = inject(ROUTER_OUTLET_DATA) as Signal<OutletData>; }
When listening for key events, you can use
(keydown.<key>)to only listen to that specific key. (documentation)<input type="text" (keyup.enter)="submit()" /> <!-- Optional modifiers (matches shift + enter) --> <input type="text" (keyup.shift.enter)="submit()" />
Event manager plugins
Event manager plugins are a way to create custom event handlers, like this one to prevent default events from executing:
import { Injectable } from '@angular/core';
import { EventManagerPlugin } from '@angular/platform-browser';
@Injectable()
export class PreventDefaultEventPlugin extends EventManagerPlugin {
constructor() {
super(document);
}
// Define which events this plugin supports
override supports(eventName: string) {
return /preventDefault/.test(eventName);
}
// Handle the event registration
override addEventListener(
element: HTMLElement,
eventName: string,
handler: Function
) {
// Parse the event (e.g. 'submit.preventDefault')
const [event] = eventName.split('.');
const listener = (event: Event) => {
// Actually prevent the default event
event.preventDefault();
handler(event);
};
element.addEventListener(event, listener);
// Return cleanup function
return () => element.removeEventListener(event, listener);
}
} Then, don’t forget to provide it in the providers array in the bootstrapApplication function
Directives
Directives can be used for all sorts of things, but in this example I use it to create a tab component:
@Directive({ selector: '[appTabListItem]' })
export class TabListItemDirective {
public readonly templateRef = inject(TemplateRef<unknown>);
}
@Directive({ selector: '[appTabContent]' })
export class TabContentDirective {
public readonly templateRef = inject(TemplateRef<unknown>);
}
@Directive({ selector: '[appTabsLayout]' })
export class TabsLayoutDirective {
public readonly templateRef = inject(
// By adding `list` and `content` here, we can
// access them by using `let-list` in templates
TemplateRef<{
list: TemplateRef<unknown>;
content: TemplateRef<unknown>;
}>
);
}
@Component({
template: `<ng-container
*ngTemplateOutlet="
layout()!.templateRef;
context: {
list: listTemplate,
content: contentTemplate,
}
"
/>`
})
export class TabsComponent {
layout = contentChild(TabsLayoutDirective);
listItems = contentChildren(TabListItemDirective);
contents = contentChildren(TabContentDirective);
activeIndex = signal(0);
} Then to use it:
<app-tabs>
<!-- Define a template for how to align the list and content -->
<ng-template appTabsLayout let-list="list" let-content="content">
<div class="grid size-full grid-cols-4">
<div
class="col-span-1 flex flex-col gap-1 border-r border-neutral-200 p-2 transition-colors dark:border-neutral-700"
>
<ng-container *ngTemplateOutlet="list" />
</div>
<div class="col-span-3 p-4">
<ng-container *ngTemplateOutlet="content" />
</div>
</div>
</ng-template>
<!-- Then we can add as many tabs as we want -->
<ng-template appTabListItem>
<p>Tab 1</p>
</ng-template>
<ng-template appTabContent>
<p>Some content here</p>
</ng-template>
<ng-template appTabListItem>
<p>Tab 2</p>
</ng-template>
<ng-template appTabContent>
<p>Some other content here</p>
</ng-template>
</app-tabs> Random snippets
Boolean input transformer
/** * @example * import { transform } from '@core/utils/merge-classes'; * * class ExampleComponent { * option = input(false, { transform }) * } * * <example-component option /> * * @param value * @returns boolean */ export function transform(value: boolean | string): boolean { return typeof value === 'string' ? value === '' : value; }
Merge tailwind classes
import clsx, { ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function mergeClasses(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
Component with selector that matches an input field
@Component({ // note this selector is the same as the input below selector: '[appTooltip]', template: '<ng-content />', // show the original content host: { '[title]': 'tooltipContent()', }, }) export class TooltipComponent { appTooltip = input<string>(''); // you can also use an alias to make this more readable: tooltipContent = input<string>('', { alias: 'appTooltip', }); }Then you can use it like this:
@Component({ template: `<p appTooltip="tooltip content">Hover over me to see the tooltip</p>`, imports: [TooltipComponent] // importing is important }) export class ExampleComponent {}
Useful Angular
- Angular has a bunch of useful built-in pipes.
- The
model()function allows you to create two-way-binding in and out of components. - The
form()function allows for creation of forms based on signals. - Angular Aria provides some accessable base components.
- Angular Material CDK provides building blocks for new components.
$any()can be used in templates to disable type checking- common-router-tasks a list of common routing tasks
Links
Some libraries and useful links:
- Zard UI - shadcn/ui alternative for Angular
- ngx-vflow - svg flow charts
- lucide-angular - svg based lucide icons
- es-toolkit - lodash-like utility functions
- class-variance-authority - conditional classes
- ngx-datatable - handling large tables
- microfuzz - simple fuzzy searching
- fontsource - easiest way of adding fonts
- oxide.ts - rust-like types
- tailwind-intellisense-regex-list
- tailwind v4 utility directive
Copyright © 2016-2026 boris.foo, All rights reserved.