Skip to content

Angular Templates

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 'counterValue' with a default value of zero.
counterValue = input(0);
}

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

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

wrapper.html
<div>
<app-my-counter [counterValue]="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.
counterValue = 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 {
counterValue = input<number>();
// create a computed expression that reads the value input
myLabel = computed(() => `Counter's value is ${this.counterValue()}`);
}

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
counterValue = 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 {
counterLabel = 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 [counterLabel]="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 CustomSlider {
// 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 the volume signal instance, not the value of the signal.