Angular's modern authoring APIs provide signal-based alternatives to traditional decorators, offering better type safety, reactivity, and integration with Angular's signal system. These APIs represent the future direction of Angular development.
Modern input declarations using signals for better reactivity and type safety.
/**
* Creates an optional input signal
* @returns InputSignal that may be undefined
*/
function input<T>(): InputSignal<T | undefined>;
/**
* Creates an input signal with initial value
* @param initialValue - Default value for the input
* @returns InputSignal with the specified type
*/
function input<T>(initialValue: T): InputSignal<T>;
/**
* Creates an input signal with options
* @param options - Configuration options for the input
* @returns InputSignal with specified configuration
*/
function input<T>(options: InputOptions<T>): InputSignal<T | undefined>;
/**
* Creates an input signal with initial value and options
* @param initialValue - Default value for the input
* @param options - Configuration options for the input
* @returns InputSignal with the specified type and configuration
*/
function input<T>(initialValue: T, options: InputOptions<T>): InputSignal<T>;
/**
* Creates a required input signal
* @returns InputSignal that must be provided
*/
input.required<T>(): InputSignal<T>;
/**
* Creates a required input signal with options
* @param options - Configuration options for the input
* @returns InputSignal that must be provided
*/
input.required<T>(options: InputOptions<T>): InputSignal<T>;
/**
* Signal representing an input property
*/
interface InputSignal<T> extends Signal<T> {
readonly [InputSignalNode]: unknown;
}
/**
* Configuration options for input signals
*/
interface InputOptions<T> {
/** Alias for the input property */
alias?: string;
/** Transform function to convert input value */
transform?: (value: any) => T;
}Model signals enable two-way data binding with parent components using signal-based APIs.
/**
* Creates an optional model signal for two-way binding
* @returns ModelSignal that may be undefined
*/
function model<T>(): ModelSignal<T | undefined>;
/**
* Creates a model signal with initial value
* @param initialValue - Default value for the model
* @returns ModelSignal with the specified type
*/
function model<T>(initialValue: T): ModelSignal<T>;
/**
* Creates a model signal with options
* @param options - Configuration options for the model
* @returns ModelSignal with specified configuration
*/
function model<T>(options: ModelOptions): ModelSignal<T | undefined>;
/**
* Creates a model signal with initial value and options
* @param initialValue - Default value for the model
* @param options - Configuration options for the model
* @returns ModelSignal with the specified type and configuration
*/
function model<T>(initialValue: T, options: ModelOptions): ModelSignal<T>;
/**
* Creates a required model signal
* @returns ModelSignal that must be provided
*/
model.required<T>(): ModelSignal<T>;
/**
* Creates a required model signal with options
* @param options - Configuration options for the model
* @returns ModelSignal that must be provided
*/
model.required<T>(options: ModelOptions): ModelSignal<T>;
/**
* Signal representing a model property with two-way binding
*/
interface ModelSignal<T> extends WritableSignal<T> {
readonly [ModelSignalNode]: unknown;
}
/**
* Configuration options for model signals
*/
interface ModelOptions {
/** Alias for the model property */
alias?: string;
}Modern output declarations using signals for event emission and communication with parent components.
/**
* Creates an output signal for event emission
* @returns OutputEmitterRef for emitting events
*/
function output<T>(): OutputEmitterRef<T>;
/**
* Creates an output signal with options
* @param options - Configuration options for the output
* @returns OutputEmitterRef with specified configuration
*/
function output<T>(options: OutputOptions): OutputEmitterRef<T>;
/**
* Reference to an output emitter for sending events to parent
*/
interface OutputEmitterRef<T> {
/** Emit an event with the specified value */
emit(value: T): void;
/** Subscribe to output events (for advanced use cases) */
subscribe(callback: (value: T) => void): OutputRefSubscription;
}
/**
* Configuration options for output signals
*/
interface OutputOptions {
/** Alias for the output property */
alias?: string;
}
/**
* Subscription to output events
*/
interface OutputRefSubscription {
/** Unsubscribe from output events */
unsubscribe(): void;
}Modern query declarations using signals for accessing child elements and components.
/**
* Query for a single view child element or component
* @param locator - Selector or component type to query
* @returns Signal containing the queried element or undefined
*/
function viewChild<T>(locator: ProviderToken<T>): Signal<T | undefined>;
/**
* Query for a single view child with options
* @param locator - Selector or component type to query
* @param options - Query configuration options
* @returns Signal containing the queried element or undefined
*/
function viewChild<T>(
locator: ProviderToken<T>,
options: ViewChildOptions
): Signal<T | undefined>;
/**
* Query for multiple view child elements or components
* @param locator - Selector or component type to query
* @returns Signal containing array of queried elements
*/
function viewChildren<T>(locator: ProviderToken<T>): Signal<ReadonlyArray<T>>;
/**
* Query for multiple view children with options
* @param locator - Selector or component type to query
* @param options - Query configuration options
* @returns Signal containing array of queried elements
*/
function viewChildren<T>(
locator: ProviderToken<T>,
options: ViewChildrenOptions
): Signal<ReadonlyArray<T>>;
/**
* Query for a single content child element or component
* @param locator - Selector or component type to query
* @returns Signal containing the queried element or undefined
*/
function contentChild<T>(locator: ProviderToken<T>): Signal<T | undefined>;
/**
* Query for a single content child with options
* @param locator - Selector or component type to query
* @param options - Query configuration options
* @returns Signal containing the queried element or undefined
*/
function contentChild<T>(
locator: ProviderToken<T>,
options: ContentChildOptions
): Signal<T | undefined>;
/**
* Query for multiple content child elements or components
* @param locator - Selector or component type to query
* @returns Signal containing array of queried elements
*/
function contentChildren<T>(locator: ProviderToken<T>): Signal<ReadonlyArray<T>>;
/**
* Query for multiple content children with options
* @param locator - Selector or component type to query
* @param options - Query configuration options
* @returns Signal containing array of queried elements
*/
function contentChildren<T>(
locator: ProviderToken<T>,
options: ContentChildrenOptions
): Signal<ReadonlyArray<T>>;
/**
* Options for view child queries
*/
interface ViewChildOptions {
/** Read a different token from the queried element */
read?: any;
}
/**
* Options for view children queries
*/
interface ViewChildrenOptions {
/** Read a different token from the queried elements */
read?: any;
}
/**
* Options for content child queries
*/
interface ContentChildOptions {
/** Read a different token from the queried element */
read?: any;
/** Whether to query descendants */
descendants?: boolean;
}
/**
* Options for content children queries
*/
interface ContentChildrenOptions {
/** Read a different token from the queried elements */
read?: any;
/** Whether to query descendants */
descendants?: boolean;
}Core utility functions for working with signals and reactive programming.
/**
* Creates a linked signal that derives its value from a source signal
* @param source - Source signal to link from
* @param computation - Function to compute the linked value
* @returns WritableSignal linked to the source
*/
function linkedSignal<T, U>(source: Signal<T>, computation: LinkedSignalComputeFn<T, U>): WritableSignal<U>;
/**
* Read signals without tracking dependencies in the current context
* @param computation - Function to execute without tracking
* @returns Result of the computation
*/
function untracked<T>(computation: () => T): T;
/**
* Type guard to check if a value is a signal
* @param value - Value to check
* @returns True if value is a signal
*/
function isSignal(value: unknown): value is Signal<unknown>;
/**
* Computation function for linked signals
*/
type LinkedSignalComputeFn<T, U> = (source: T, previous?: { value: U }) => U;Utility functions for transforming string attributes to typed values.
/**
* Transform function for converting string attributes to boolean values
* @param value - The attribute value to transform
* @returns Boolean representation of the attribute
*/
function booleanAttribute(value: unknown): boolean;
/**
* Transform function for converting string attributes to number values
* @param value - The attribute value to transform
* @returns Numeric representation of the attribute
*/
function numberAttribute(value: unknown): number;import { Component, input, output, model, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<div class="counter">
<h3>{{title()}}</h3>
<div>Count: {{displayValue()}}</div>
<div>
<button (click)="decrement()" [disabled]="isMinReached()">-</button>
<button (click)="increment()" [disabled]="isMaxReached()">+</button>
</div>
<div *ngIf="showReset()">
<button (click)="reset()">Reset</button>
</div>
</div>
`,
styles: [`
.counter {
padding: 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
margin: 0 4px;
padding: 8px 16px;
}
`]
})
export class CounterComponent {
// Inputs
title = input('Counter');
initialValue = input(0);
min = input<number | undefined>(undefined);
max = input<number | undefined>(undefined);
step = input(1);
// Model (two-way binding)
value = model.required<number>();
// Outputs
valueChange = output<number>();
minReached = output<void>();
maxReached = output<void>();
// Computed signals
displayValue = computed(() => this.value() + this.initialValue());
isMinReached = computed(() => {
const minVal = this.min();
return minVal !== undefined && this.value() <= minVal;
});
isMaxReached = computed(() => {
const maxVal = this.max();
return maxVal !== undefined && this.value() >= maxVal;
});
showReset = computed(() => this.value() !== this.initialValue());
constructor() {
// Effect to emit events when limits are reached
effect(() => {
if (this.isMinReached()) {
this.minReached.emit();
}
if (this.isMaxReached()) {
this.maxReached.emit();
}
});
}
increment(): void {
if (!this.isMaxReached()) {
const newValue = this.value() + this.step();
this.value.set(newValue);
this.valueChange.emit(newValue);
}
}
decrement(): void {
if (!this.isMinReached()) {
const newValue = this.value() - this.step();
this.value.set(newValue);
this.valueChange.emit(newValue);
}
}
reset(): void {
this.value.set(this.initialValue());
this.valueChange.emit(this.initialValue());
}
}import { Component, viewChild, viewChildren, contentChild, ElementRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-form-container',
template: `
<form #mainForm>
<ng-content></ng-content>
<div class="form-actions">
<button #submitBtn type="submit">Submit</button>
<button #cancelBtn type="button">Cancel</button>
</div>
</form>
`
})
export class FormContainerComponent implements AfterViewInit {
// Query for form element
mainForm = viewChild.required<ElementRef<HTMLFormElement>>('mainForm');
// Query for buttons
submitButton = viewChild<ElementRef<HTMLButtonElement>>('submitBtn');
cancelButton = viewChild<ElementRef<HTMLButtonElement>>('cancelBtn');
// Query for all input elements
allInputs = viewChildren<ElementRef<HTMLInputElement>>('input');
// Query for projected content
formFields = contentChildren<ElementRef>('input, select, textarea');
ngAfterViewInit(): void {
// Access queried elements
const form = this.mainForm().nativeElement;
console.log('Form element:', form);
// Subscribe to changes in query results
effect(() => {
const inputs = this.allInputs();
console.log(`Found ${inputs.length} input elements`);
});
}
}import { Component, input, booleanAttribute, numberAttribute } from '@angular/core';
@Component({
selector: 'app-config-panel',
template: `
<div [class.enabled]="enabled()">
<h3>Configuration Panel</h3>
<p>Max items: {{maxItems()}}</p>
<p>Auto save: {{autoSave() ? 'On' : 'Off'}}</p>
</div>
`
})
export class ConfigPanelComponent {
// Boolean attribute input with transform
enabled = input(false, {
transform: booleanAttribute
});
// Number attribute input with transform
maxItems = input(10, {
transform: numberAttribute
});
// Boolean input with alias and transform
autoSave = input(true, {
alias: 'auto-save',
transform: booleanAttribute
});
}// Parent component
@Component({
selector: 'app-parent',
template: `
<app-slider [(value)]="volume" [min]="0" [max]="100"></app-slider>
<p>Current volume: {{volume}}</p>
`
})
export class ParentComponent {
volume = 50;
}
// Child component with model
@Component({
selector: 'app-slider',
template: `
<div class="slider">
<input
type="range"
[value]="value()"
[min]="min()"
[max]="max()"
(input)="onSliderChange($event)"
>
<span>{{value()}}</span>
</div>
`
})
export class SliderComponent {
// Two-way binding model
value = model.required<number>();
// Configuration inputs
min = input(0);
max = input(100);
step = input(1);
onSliderChange(event: Event): void {
const target = event.target as HTMLInputElement;
this.value.set(Number(target.value));
}
}