Angular Component Development Kit providing behavior primitives and utilities for building high-quality, reusable UI components
npx @tessl/cli install tessl/npm-angular--cdk@20.2.0The Angular Component Development Kit (CDK) is a comprehensive library that provides a set of behavior primitives and utilities for building high-quality, reusable UI components. It offers foundational building blocks including accessibility helpers, layout utilities, drag-drop functionality, overlay positioning, scrolling behaviors, table utilities, platform detection, portal management, form field behaviors, stepper patterns, tree structures, and testing utilities.
npm install @angular/cdkThe CDK is organized into independent modules that can be imported separately:
// Import specific CDK modules
import { OverlayModule } from '@angular/cdk/overlay';
import { PortalModule } from '@angular/cdk/portal';
import { A11yModule } from '@angular/cdk/a11y';
// Import utilities from specific modules
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ENTER, SPACE } from '@angular/cdk/keycodes';
import { Platform } from '@angular/cdk/platform';
import { Portal, ComponentPortal } from '@angular/cdk/portal';import { Component, ElementRef, ViewChild } from '@angular/core';
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Platform } from '@angular/cdk/platform';
@Component({
selector: 'app-example',
template: '<button #button>Click me</button>'
})
export class ExampleComponent {
@ViewChild('button') buttonRef!: ElementRef;
constructor(
private focusMonitor: FocusMonitor,
private platform: Platform
) {}
ngAfterViewInit() {
// Monitor focus state
this.focusMonitor.monitor(this.buttonRef).subscribe(origin => {
console.log('Focus origin:', origin);
});
// Check platform
if (this.platform.isBrowser) {
console.log('Running in browser');
}
// Use coercion utilities
const isEnabled = coerceBooleanProperty('true'); // Returns true
}
}The Angular CDK is built around several key architectural patterns:
Comprehensive accessibility features including focus management, keyboard navigation, live announcers, and ARIA support for building accessible components.
// Focus monitoring and management
class FocusMonitor {
monitor(element: HTMLElement | ElementRef, checkChildren?: boolean): Observable<FocusOrigin>;
stopMonitoring(element: HTMLElement | ElementRef): void;
focusVia(element: HTMLElement | ElementRef, origin: FocusOrigin, options?: FocusOptions): void;
}
// ARIA description management
class AriaDescriber {
describe(hostElement: Element, message: string | HTMLElement, role?: string): void;
removeDescription(hostElement: Element, message: string | HTMLElement, role?: string): void;
}
type FocusOrigin = 'touch' | 'mouse' | 'keyboard' | 'program' | null;Utilities for responsive layout, breakpoint observation, and text direction (bidirectional) support.
// Breakpoint observation
class BreakpointObserver {
observe(value: string | string[]): Observable<BreakpointState>;
isMatched(value: string | string[]): boolean;
}
// Text direction support
class Directionality {
value: Direction;
change: Observable<Direction>;
}
type Direction = 'ltr' | 'rtl';Powerful overlay system for creating positioned floating content like tooltips, dropdowns, and modals.
// Overlay creation and management
class Overlay {
create(config?: OverlayConfig): OverlayRef;
position(): OverlayPositionBuilder;
}
// Overlay reference for managing attached content
class OverlayRef {
attach<T>(portal: Portal<T>): ComponentRef<T> | EmbeddedViewRef<T> | null;
detach(): any;
dispose(): void;
backdropClick(): Observable<MouseEvent>;
updatePosition(): void;
}Portal system for dynamically rendering content in different locations in the DOM.
// Base portal for dynamic content
abstract class Portal<T> {
attach(host: PortalOutlet): T;
detach(): void;
isAttached: boolean;
}
// Component-based portal
class ComponentPortal<T> {
component: ComponentType<T>;
viewContainerRef?: ViewContainerRef;
injector?: Injector;
}
// Template-based portal
class TemplatePortal<C = any> {
templateRef: TemplateRef<C>;
viewContainerRef: ViewContainerRef;
context?: C;
}Data management utilities including selection models, data sources, and collection viewers.
// Selection state management
class SelectionModel<T> {
selected: T[];
changed: Subject<SelectionChange<T>>;
select(...values: T[]): void;
deselect(...values: T[]): void;
toggle(value: T): void;
clear(): void;
isSelected(value: T): boolean;
}
// Base data source for components
abstract class DataSource<T> {
connect(collectionViewer: CollectionViewer): Observable<T[]>;
disconnect(collectionViewer: CollectionViewer): void;
}Comprehensive drag and drop functionality with support for sorting, transferring between lists, and custom drag previews.
// Drag and drop service
class DragDrop {
createDrag<T = any>(element: ElementRef<HTMLElement> | HTMLElement, config?: DragRefConfig): DragRef<T>;
createDropList<T = any>(element: ElementRef<HTMLElement> | HTMLElement): DropListRef<T>;
}
// Draggable element reference
class DragRef<T = any> {
data: T;
disabled: boolean;
moved: Observable<{source: DragRef; pointerPosition: {x: number; y: number}}>;
started: Observable<{source: DragRef}>;
ended: Observable<{source: DragRef; distance: {x: number; y: number}}>;
}Advanced scrolling utilities including virtual scrolling for large datasets and scroll event management.
// Virtual scroll viewport for large lists
class CdkVirtualScrollViewport {
itemSize: number;
orientation: 'horizontal' | 'vertical';
getViewportSize(): number;
scrollToIndex(index: number, behavior?: ScrollBehavior): void;
measureScrollOffset(from?: 'top' | 'left' | 'right' | 'bottom'): number;
}
// Global scroll event management
class ScrollDispatcher {
scrolled(auditTimeInMs?: number): Observable<CdkScrollable | void>;
ancestorScrolled(element: ElementRef | Element, auditTimeInMs?: number): Observable<CdkScrollable | void>;
}Flexible table component with support for sorting, filtering, sticky headers/columns, and custom cell rendering.
// Data table component
class CdkTable<T> {
dataSource: CdkTableDataSourceInput<T>;
trackBy: TrackByFunction<T>;
renderRows(): void;
addColumnDef(columnDef: CdkColumnDef): void;
addRowDef(rowDef: CdkRowDef<T>): void;
}
// Column definition
class CdkColumnDef {
name: string;
sticky: boolean;
stickyEnd: boolean;
}Higher-level component patterns including steppers, accordions, trees, and menus.
// Stepper component for multi-step workflows
class CdkStepper {
linear: boolean;
selectedIndex: number;
steps: QueryList<CdkStep>;
next(): void;
previous(): void;
reset(): void;
}
// Tree component for hierarchical data
class CdkTree<T, K = T> {
dataSource: DataSource<T>;
treeControl: TreeControl<T, K>;
trackBy: TrackByFunction<T>;
}Platform detection, type coercion utilities, key codes, and clipboard operations.
// Platform detection service
class Platform {
isBrowser: boolean;
EDGE: boolean;
WEBKIT: boolean;
IOS: boolean;
ANDROID: boolean;
SAFARI: boolean;
}
// Type coercion utilities
function coerceBooleanProperty(value: any): boolean;
function coerceNumberProperty(value: any, fallback?: number): number;
function coerceArray<T>(value: T | T[]): T[];
// Clipboard operations
class Clipboard {
copy(text: string): boolean;
}Flexible dialog system for creating modal dialogs, overlays, and popup content with positioning, focus management, and accessibility features.
// Dialog service for creating modal dialogs
class Dialog {
open<T, D = any, R = any>(
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
config?: DialogConfig<D, R>
): DialogRef<T, R>;
closeAll(): void;
getDialogById(id: string): DialogRef<any> | undefined;
}
// Dialog reference for managing opened dialogs
class DialogRef<T, R = any> {
readonly componentInstance: T | null;
readonly afterClosed: Observable<R | undefined>;
readonly backdropClick: Observable<MouseEvent>;
close(result?: R): void;
updatePosition(position?: DialogPosition): this;
updateSize(width?: string | number, height?: string | number): this;
}
// Dialog configuration interface
interface DialogConfig<D = any, R = any> {
data?: D | null;
width?: string | number;
height?: string | number;
hasBackdrop?: boolean;
disableClose?: boolean;
autoFocus?: AutoFocusTarget;
restoreFocus?: boolean;
}Comprehensive menu system with support for nested submenus, keyboard navigation, ARIA compliance, and intelligent hover behavior.
// Main menu directive for creating accessible menus
class CdkMenu {
orientation: 'horizontal' | 'vertical';
isInline: boolean;
readonly items: readonly CdkMenuItem[];
focusFirstItem(origin?: FocusOrigin): void;
focusLastItem(origin?: FocusOrigin): void;
}
// Menu item directive with keyboard navigation
class CdkMenuItem {
disabled: boolean;
typeaheadLabel: string;
trigger(): void;
focus(origin?: FocusOrigin): void;
}
// Menu trigger directive
class CdkMenuTrigger {
readonly isOpen: boolean;
open(): void;
close(): void;
toggle(): void;
}Accessible listbox components with single/multiple selection, keyboard navigation, and form control integration.
// Main listbox directive implementing ControlValueAccessor
class CdkListbox<T = unknown> implements ControlValueAccessor {
multiple: boolean;
disabled: boolean;
value: T | T[] | null;
readonly selectionChange: EventEmitter<ListboxSelectionChangeEvent<T>>;
toggle(value: T): void;
select(...values: T[]): void;
deselect(...values: T[]): void;
isSelected(value: T): boolean;
}
// Listbox option directive
class CdkOption<T = unknown> {
disabled: boolean;
value: T;
readonly selected: boolean;
select(): void;
deselect(): void;
toggle(): void;
}Text field utilities including autofill detection and automatic textarea resizing functionality.
// Service for monitoring input autofill state
class AutofillMonitor {
monitor(elementOrRef: Element | ElementRef<Element>): Observable<AutofillEvent>;
stopMonitoring(elementOrRef: Element | ElementRef<Element>): void;
}
// Directive for auto-resizing textarea elements
class CdkTextareaAutosize {
minRows: number;
maxRows: number;
enabled: boolean;
resizeToFitContent(force?: boolean): void;
reset(): void;
}
// Autofill event interface
interface AutofillEvent {
target: Element;
isAutofilled: boolean;
}Utilities for detecting content changes in DOM elements using the MutationObserver API with Angular integration.
// Service for observing DOM content changes
class ContentObserver {
observe(element: Element): Observable<MutationRecord[]>;
observe(element: Element, options: MutationObserverInit): Observable<MutationRecord[]>;
unobserve(element: Element): void;
}
// Directive for observing content changes
class CdkObserveContent {
readonly cdkObserveContent: EventEmitter<MutationRecord[]>;
disabled: boolean;
debounce: number;
}Flexible accordion component system with support for single/multiple panel expansion and accessibility features.
// Accordion container directive
class CdkAccordion {
multi: boolean;
id: string;
openAll(): void;
closeAll(): void;
}
// Individual accordion item directive
class CdkAccordionItem {
expanded: boolean;
disabled: boolean;
readonly opened: EventEmitter<void>;
readonly closed: EventEmitter<void>;
readonly expandedChange: EventEmitter<boolean>;
open(): void;
close(): void;
toggle(): void;
}Comprehensive testing utilities with component harness system for reliable component testing across different test environments.
// Base component harness for high-level component interaction
abstract class ComponentHarness {
host(): Promise<TestElement>;
locatorFor<T extends ComponentHarness>(selector: ComponentHarnessConstructor<T>): AsyncFactoryFn<T>;
locatorForAll<T extends ComponentHarness>(selector: ComponentHarnessConstructor<T>): AsyncFactoryFn<T[]>;
waitForTasksOutsideAngular(): Promise<void>;
forceStabilize(): Promise<void>;
}
// Test element interface for interacting with DOM elements
interface TestElement {
click(relativeX?: number, relativeY?: number): Promise<void>;
focus(): Promise<void>;
blur(): Promise<void>;
text(): Promise<string>;
sendKeys(...keys: (string | TestKey)[]): Promise<void>;
getAttribute(name: string): Promise<string | null>;
hasClass(name: string): Promise<boolean>;
setInputValue(value: string): Promise<void>;
getCssValue(property: string): Promise<string>;
}
// Harness predicate builder for complex component selection
class HarnessPredicate<T extends ComponentHarness> {
add(description: string, predicate: AsyncPredicate<T>): HarnessPredicate<T>;
addOption<K extends keyof T>(name: string, option: HarnessQuery<T[K]>, predicate: AsyncOptionPredicate<T, T[K]>): HarnessPredicate<T>;
}
// TestBed harness environment
class TestbedHarnessEnvironment {
static loader(fixture: ComponentFixture<unknown>): HarnessLoader;
static harnessForFixture<T extends ComponentHarness>(fixture: ComponentFixture<unknown>, harnessType: ComponentHarnessConstructor<T>): Promise<T>;
}// Common input types for coercion
type BooleanInput = string | boolean | null | undefined;
type NumberInput = string | number | null | undefined;
// Component type definition
interface ComponentType<T> {
new (...args: any[]): T;
}
// Event types
interface CdkDragDrop<T, O = T> {
previousIndex: number;
currentIndex: number;
item: CdkDragRef<T>;
container: CdkDropListRef<O>;
previousContainer: CdkDropListRef<T>;
}
// Selection change event
interface SelectionChange<T> {
source: SelectionModel<T>;
added: T[];
removed: T[];
}