
Angular Directives
Copy-paste Angular directive patterns for DOM behavior, observers, drag-and-drop, and permission-gated UI without reinventing attribute directives.
Overview
angular-directives is an agent skill for the Build phase that supplies copy-ready Angular directive patterns for DOM control, observers, drag-and-drop, and permission-gated templates.
Install
npx skills add https://github.com/analogjs/angular-skills --skill angular-directivesWhat is this skill?
- Six documented pattern groups: DOM manipulation, form directives, intersection observer, resize observer, drag-and-drop,
- Modern Angular APIs: inject, input, afterNextRender, booleanAttribute, host listeners
- Ready-made AutoFocus and SelectAll directives with usage snippets
- Intersection and resize observer directives for lazy-load and responsive layout hooks
- Permission directive pattern for feature-flag or RBAC-style template gating
- 6 documented pattern groups in the skill table of contents
Adoption & trust: 4.6k installs on skills.sh; 592 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are building Angular UI and keep rewriting the same focus, observer, and interaction directives instead of using a consistent pattern library.
Who is it for?
Solo or indie developers on Angular 17+ who want directive recipes aligned with inject/input and host-binding style.
Skip if: Teams not using Angular, or builders who only need generic CSS/HTML patterns without TypeScript directives.
When should I use this skill?
Implementing or refactoring Angular attribute directives for DOM, forms, observers, drag-and-drop, or permissions.
What do I get? / Deliverables
You drop in tested directive snippets and wire them in templates with clear selectors and inputs, speeding UI polish without scattered one-off code.
- Directive TypeScript implementations pasted into your app
- Template usage examples for each pattern
Recommended Skills
Journey fit
How it compares
Use as a pattern cookbook beside Angular component docs, not as a full UI framework or design system generator.
Common Questions / FAQ
Who is angular-directives for?
Angular frontend developers and solo builders who implement custom behaviors on templates and want vetted directive examples instead of trial-and-error.
When should I use angular-directives?
During Build → frontend when adding autofocus, observers, DnD, or permission checks to components, or when refactoring duplicated directive logic across features.
Is angular-directives safe to install?
It is documentation and code patterns only; review the Security Audits panel on this Prism page before installing any skill from the catalog.
SKILL.md
READMESKILL.md - Angular Directives
# Angular Directive Patterns ## Table of Contents - [DOM Manipulation](#dom-manipulation) - [Form Directives](#form-directives) - [Intersection Observer](#intersection-observer) - [Resize Observer](#resize-observer) - [Drag and Drop](#drag-and-drop) - [Permission Directive](#permission-directive) ## DOM Manipulation ### Auto-Focus Directive ```typescript @Directive({ selector: '[appAutoFocus]', }) export class AutoFocus { private el = inject(ElementRef<HTMLElement>); enabled = input(true, { alias: 'appAutoFocus', transform: booleanAttribute }); delay = input(0); constructor() { afterNextRender(() => { if (this.enabled()) { setTimeout(() => { this.el.nativeElement.focus(); }, this.delay()); } }); } } // Usage: <input appAutoFocus /> // Usage: <input [appAutoFocus]="shouldFocus()" [delay]="100" /> ``` ### Text Selection Directive ```typescript @Directive({ selector: '[appSelectAll]', host: { '(focus)': 'onFocus()', '(click)': 'onClick($event)', }, }) export class SelectAll { private el = inject(ElementRef<HTMLInputElement>); onFocus() { // Delay to ensure value is set setTimeout(() => this.el.nativeElement.select(), 0); } onClick(event: MouseEvent) { // Select all on first click if not already focused if (document.activeElement !== this.el.nativeElement) { this.el.nativeElement.select(); } } } // Usage: <input appSelectAll value="Select me on focus" /> ``` ### Copy to Clipboard ```typescript @Directive({ selector: '[appCopyToClipboard]', host: { '(click)': 'copy()', '[style.cursor]': '"pointer"', }, }) export class CopyToClipboard { text = input.required<string>({ alias: 'appCopyToClipboard' }); copied = output<void>(); error = output<Error>(); async copy() { try { await navigator.clipboard.writeText(this.text()); this.copied.emit(); } catch (err) { this.error.emit(err as Error); } } } // Usage: // <button [appCopyToClipboard]="textToCopy" (copied)="showToast('Copied!')"> // Copy // </button> ``` ## Form Directives ### Trim Input ```typescript @Directive({ selector: 'input[appTrim], textarea[appTrim]', host: { '(blur)': 'onBlur()', }, }) export class Trim { private el = inject(ElementRef<HTMLInputElement | HTMLTextAreaElement>); private ngControl = inject(NgControl, { optional: true, self: true }); onBlur() { const value = this.el.nativeElement.value; const trimmed = value.trim(); if (value !== trimmed) { this.el.nativeElement.value = trimmed; this.ngControl?.control?.setValue(trimmed); } } } // Usage: <input appTrim formControlName="name" /> ``` ### Input Mask ```typescript @Directive({ selector: '[appMask]', host: { '(input)': 'onInput($event)', '(keydown)': 'onKeydown($event)', }, }) export class Mask { private el = inject(ElementRef<HTMLInputElement>); // Mask pattern: 9 = digit, A = letter, * = any mask = input.required<string>({ alias: 'appMask' }); onInput(event: InputEvent) { const input = this.el.nativeElement; const value = input.value; const masked = this.applyMask(value); if (value !== masked) { input.value = masked; } } onKeydown(event: KeyboardEvent) { // Allow navigation keys if (['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(event.key)) { return; } const input = this.el.nativeElement; const position = input.selectionStart ?? 0; const maskChar = this.mask()[position]; if (!maskChar) { event.preventDefault(); return; } if (!this.isValidChar(event.key, maskChar)) { event.preventDefault(); } } private applyMask(value: string): string { const mask = this.mask(); let result = ''; let valueIndex = 0; for (let i = 0; i < mask.length && valueIndex < value.length; i++) { const m