Skip to content

Angular Components Input

Understanding the core of Angular

Every Angular component must have:

  • TypeScript class that describes the behaviors,
  • HTML template that controls what renders into the DOM,
  • CSS selector that defines how the component is used in HTML (optional, could also use a global).

You provide Angular-specific information for a component by adding the @Component decorator on top of the TypeScript class. The object passed to the @Component decorator is called the component’s metadata.

my-component.ts
@Component({ /* component's metadata */
selector: 'app-my-component',
template: `<img src="profile-photo.jpg" alt="Your profile photo" />`,
styles: `
img {
border-radius: 50%;
}
`,
})
export class MyComponent {}

Usually, HTML template and CSS classes are in a separate files:

my-component.ts
import {Component, input} from '@angular/core';
@Component({ /* component's metadata */
selector: 'app-my-component',
templateUrl: 'my-component.html',
styleUrl: 'my-component.css',
})
export class MyComponent {}

To use a component, directive, or pipe, you must add it to the imports array in the @Component decorator:

out-component.ts
import {Component, input} from '@angular/core';
import {MyComponent} from './my-component';
@Component({
imports: [MyComponent],
selector: 'app-out-component',
template: `
<h1>Out Component</h1>
<app-my-component><app-my-component />
`,
})
export class OutComponent {}

Import MyComponent component in order to use it in OutComponent component’s template.

Angular input properties are similar to React’s props.

A component specifies the data that it accepts by declaring inputs:

my-counter.ts
import {Component, input} from '@angular/core';
@Component({
selector: 'app-my-counter',
/*...*/
})
export class MyCounter {
// declare an input named 'myValue' with a default value of zero.
myValue = input(0);
}

In this case, myValue is of type InputSignal<number>.

This lets you bind to the property in a template of another component:

wrapper.html
<div>
<app-my-counter [myValue]="50" />
</div>

If an input has a default value, like in the example above, TypeScript infers the type from the default value. You can explicitly declare a type for the input by specifying a generic parameter to the function. If an input without a default value is not set, its value is undefined:

my-counter.ts
import {Component, input} from '@angular/core';
@Component({/*...*/})
export class MyCounter {
// produces an InputSignal<number | undefined>
// the variable may not be set.
myValue = input<number>();
}

Angular records inputs statically at compile-time. Inputs cannot be added or removed at run-time.

When extending a component class, inputs are inherited by the child class.

The input function returns an InputSignal. You can read the value by calling the signal:

my-counter.ts
@Component({/*...*/})
export class MyCounter {
myValue = input<number>();
// create a computed expression that reads the value input
myLabel = computed(() => `Counter's value is ${this.myValue()}`);
}

Signals created by the input function are read-only.

You can declare that an input is required by calling input.required instead of input:

my-counter.ts
@Component({/*...*/})
export class MyCounter {
// declare a required input named value
// returns an `InputSignal<number>` even if default value is not specified
myValue = input.required<number>();
}

Angular enforces that required inputs must be set when the component is used in a template. If you try to use a component without specifying all of its required inputs, Angular reports an error at build-time.

The input function accepts a config object as a second parameter that lets you change the way that input works. You can specify a transform function to change the value of an input when it’s set by Angular.

my-counter.ts
@Component({/*...*/})
export class MyCounter {
label = input('', {transform: trimString});
}
function trimString(value: string | undefined): string {
return value?.trim() ?? '';
}

This lets you bind to the property in a template of another component:

wrapper.html
<div>
<app-my-counter [label]="System Counter Label" />
</div>

Input transform function must be statically analyzable at build-time. You cannot set transform functions conditionally or as the result of an expression evaluation.

Input transform functions should always be pure functions. Relying on state outside the transform function can lead to unpredictable behavior.


When you specify an input transform, the type of the transform function’s parameter determines the types of values that can be set to the input in a template.

my-counter.ts
@Component({/*...*/})
export class MyCounter {
widthPx = input('', {transform: appendPx});
}
function appendPx(value: number): string {
return `${value}px`;
}

In this case, the widthPx input accepts a number while the InputSignal property returns a string because of appendPx() manipulation.


Angular includes two built-in transform functions for the two most common scenarios:

  • coercing values to boolean,
  • coercing values to numbers.
my-counter.ts
import {Component, input, booleanAttribute, numberAttribute} from '@angular/core';
@Component({/*...*/})
export class MyCounter {
disabled = input(false, {transform: booleanAttribute});
numberValue = input(0, {transform: numberAttribute});
}

booleanAttribute imitates the behavior of standard HTML boolean attributes, where the presence of the attribute indicates a "true" value. Angular’s booleanAttribute treats the literal string "false" as the boolean false.

numberAttribute attempts to parse the given value to a number, producing NaN (which stands for Not a Number) if parsing fails.

You can specify alias option to change the name of an input in templates.

my-counter.ts
@Component({/*...*/})
export class MyCounter {
maxValue = input(0, {alias: 'countMax'});
}

This lets you call the property, in a template of another component, with the alias name:

wrapper.html
<div>
<app-my-counter [countMax]="500" />
</div>

This alias does not affect usage of the property in TypeScript code.

While you should generally avoid aliasing inputs for components, this feature can be useful for renaming properties while preserving an alias for the original name or for avoiding collisions with the name of native DOM element properties.

Model inputs are a special type of input that enable a component to propagate new values back to its parent component.

Both types of input allow someone to bind a value into the property. However, model inputs allow the component author to write values into the property. If the property is bound with a two-way binding, the new value propagates to that binding.

my-slider.ts
@Component({/* ... */})
export class MySlider {
// define a model input named "value".
value = model(0);
increment() {
// update the model input with a new value, propagating the value to any bindings.
this.value.update((oldValue) => oldValue + 10);
}
}

MySlider can write values into its value model input, which then propagates those values back to the volume signal in MyMedia. This binding keeps the values of value and volume in sync. Notice that the binding passes volume signal instance, not the value of the signal.

You can bind a plain JavaScript property to a model input. In the example below, MySlider component can write values into its value model input, which then propagates those values back to volume property in MyMedia. This binding keeps the values of value and volume in sync.

my-media.ts
@Component({
/* ... */
// `value` is a model input.
// parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding
template: '<my-slider [(value)]="volume" />',
})
export class MyMedia {
protected volume = 0;
}

When you declare a model input in a component or directive, Angular automatically creates a corresponding output for that model. The output’s name is the model input’s name suffixed with ā€œChangeā€.

my-checkbox.ts
@Directive({
/* ... */
})
export class MyCheckbox {
checked = model(false);
}

The above code automatically creates an output named checkedChange that can be subscribed to using (checkedChange)="handler()" in a template.

Angular emits this change event whenever you write a new value into the model input by calling its set or update methods.


You can mark a model input as required or provide an alias in the same way as a standard input. Model inputs do not support input transforms.

Use model inputs when you want a component to support two-way binding. This is typically appropriate when a component exists to modify a value based on user interaction. Custom form controls, such as a date picker or combobox, should use model inputs for their primary value.


Avoid choosing input names that collide with properties on DOM elements like HTMLElement. Name collisions introduce confusion about whether the bound property belongs to the component or the DOM element.

Avoid adding prefixes for component inputs like you would with component selectors. Since a given element can only host one component, any custom properties can be assumed to belong to the component.

Angular team recommends using the signal-based input function for new projects, but the original decorator-based @Input API remains supported.

You can alternatively declare component inputs by adding the @Input decorator to a property:

my-slider.ts
@Component({/*...*/})
export class MySlider {
@Input() value = 0;
}

Binding to an input is the same in both signal-based and decorator-based inputs:

wrapper.html
<my-slider [value]="50" />

@Input decorator accepts a config object that lets you change the way that input works.

You can specify the required option to enforce that a given input must always have a value.

my-slider.ts
@Component({/*...*/})
export class CustomSlider {
@Input({required: true}) value = 0;
}

If you try to use a component without specifying all of its required inputs, Angular reports an error at build-time.

You can specify a transform function to change the value of an input when it’s set by Angular. This transform function works identically to transform functions for signal-based inputs described above.

my-slider.ts
@Component({/*...*/})
export class CustomSlider {
@Input({transform: trimString}) label = '';
}
function trimString(value: string | undefined) {
return value?.trim() ?? '';
}

You can specify the alias option to change the name of an input in templates.

my-slider.ts
@Component({/*...*/})
export class CustomSlider {
@Input({alias: 'sliderValue'}) value = 0;
}

This lets you call the property, in a template of another component, with the alias name:

wrapper.html
<my-slider [sliderValue]="50" />

@Input decorator also accepts the alias as its first parameter in place of the config object. Input aliases work the same way as for signal-based inputs described above.

When using decorator-based inputs, a property implemented with a getter and setter can be an input:

my-slider.ts
@Component({/*...*/})
export class MySlider {
@Input()
get value(): number {
return this.internalValue;
}
set value(newValue: number) {
this.internalValue = newValue;
}
private internalValue = 0;
}

You can also create a write-only input by defining a public setter:

my-slider.ts
@Component({/*...*/})
export class CustomSlider {
@Input()
set value(newValue: number) {
this.internalValue = newValue;
}
private internalValue = 0;
}

Use input transforms instead of getters and setters if possible. Avoid complex or costly getters and setters. Angular may invoke an input’s setter multiple times, which may negatively impact application performance if the setter performs any costly behaviors, such as DOM manipulation.

In addition to @Input decorator, you can also specify a component’s inputs with the inputs property of @Component decorator. This can be useful when a component inherits a property from a base class:

my-slider.ts
// `MySlider` inherits `disabled` property from `BaseSlider`
@Component({
/*...*/,
inputs: ['disabled'],
})
export class MySlider extends BaseSlider { }

You can specify an input alias in the inputs list by putting the alias after a colon in the string:

my-slider.ts
@Component({
/*...*/,
inputs: ['disabled: sliderDisabled'],
})
export class CustomSlider extends BaseSlider { }