../

Angular cheatsheet

January 19, 2026

Typescript ·Angular

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 @empty to 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


Links

Some libraries and useful links:

Copyright © 2016-2026 boris.foo, All rights reserved.