Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Inputs: input()

An input is a component field whose value is set by the parent. This way one component can be used many times with different data: for example, a single-task component is rendered for every task in the list.

Creating the task component

ng generate component todo-item

The folder src/app/todo-item appears with todo-item.ts, todo-item.html, todo-item.css and a test.

Declaring an input

The input() function creates an input. It works like a read-only signal: you read the value by calling it with parentheses.

// src/app/todo-item/todo-item.ts
import { Component, input } from '@angular/core';
 
@Component({
  selector: 'app-todo-item',
  templateUrl: './todo-item.html',
  styleUrl: './todo-item.css',
})
export class TodoItem {
  title = input('');
}
<!-- src/app/todo-item/todo-item.html -->
<span>{{ title() }}</span>

The argument of input('') is the default value: until the parent passes anything, an empty string is shown.

Passing a value from the parent

The parent adds the component to imports and passes the value with property binding:

// src/app/todo-list/todo-list.ts
import { Component, signal } from '@angular/core';
import { TodoItem } from '../todo-item/todo-item';
import { Todo } from '../todo';
 
@Component({
  selector: 'app-todo-list',
  imports: [TodoItem],
  templateUrl: './todo-list.html',
})
export class TodoList {
  todos = signal<Todo[]>([
    { id: 1, title: 'Buy bread', done: true },
    { id: 2, title: 'Call Oleg', done: false },
  ]);
}
<!-- src/app/todo-list/todo-list.html -->
<ul>
  @for (todo of todos(); track todo.id) {
    <li><app-todo-item [title]="todo.title" /></li>
  }
</ul>

The page shows two items: "Buy bread" and "Call Oleg". A constant string can be passed without brackets: <app-todo-item title="Buy bread" />.

A required input

The task component needs the whole object, not just the title. Without a task the component makes no sense, so we make the input required:

// src/app/todo-item/todo-item.ts
import { Component, input } from '@angular/core';
import { Todo } from '../todo';
 
@Component({
  selector: 'app-todo-item',
  templateUrl: './todo-item.html',
  styleUrl: './todo-item.css',
})
export class TodoItem {
  todo = input.required<Todo>();
}
<!-- src/app/todo-item/todo-item.html -->
<span [class.done]="todo().done">{{ todo().title }}</span>

The parent's template now says <app-todo-item [todo]="todo" />. If you forget to pass todo, the build reports an error before the app even runs.

Deriving values from an input

An input is a signal, so you can build a computed from it:

// src/app/todo-item/todo-item.ts
label = computed(() => (this.todo().done ? 'done' : 'in progress'));

label() recalculates by itself when the parent passes another task. Remember to import computed from @angular/core.

Common mistake

Trying to change an input from inside: this.todo.set(...). An input has no set method — the parent owns the data. If the child wants to change something, it tells the parent through an output, which is the topic of the next lesson.

Thanks for reading!

Donate to the site — we work hard for you.