Ir al contenido

Inputs, Outputs & Two-Way Binding

Esta página aún no está disponible en tu idioma.

Section titled “Signal Inputs (Angular 18+ — Recommended)”

Signal inputs are the modern, type-safe, and reactive way to receive data from parent components:

import { Component, input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div class="card">
<h3>{{ name() }}</h3>
<p>{{ email() }}</p>
</div>
`
})
export class UserCardComponent {
name = input.required<string>();
email = input<string>('');
showAvatar = input<boolean>(false);
}

Usage in the parent template:

<app-user-card
[name]="'Alice Johnson'"
[email]="'alice@example.com'"
[showAvatar]="true"
/>

Key points about signal inputs:

  • input.required<T>() — The parent must provide a value.
  • input<T>(defaultValue) — Optional with a default value.
  • Accessed via function call syntax: this.name().
  • Fully reactive — works seamlessly with computed() and effect().

Apply transformations when a value is received:

import { Component, input, booleanAttribute, numberAttribute } from '@angular/core';
@Component({
selector: 'app-paginator',
template: `<span>Page {{ page() }} of {{ total() }}</span>`
})
export class PaginatorComponent {
disabled = input(false, { transform: booleanAttribute });
page = input(1, { transform: numberAttribute });
total = input.required<number>();
}

Decorator-based Inputs (Legacy — still supported)

Section titled “Decorator-based Inputs (Legacy — still supported)”
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `<h3>{{ name }}</h3>`
})
export class UserCardComponent {
@Input() name: string = '';
@Input() email: string = '';
}

Section titled “Function-based Outputs (Angular 18+ — Recommended)”
import { Component, output } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div class="card">
<h3>{{ name() }}</h3>
<button (click)="onDelete()">Delete</button>
</div>
`
})
export class UserCardComponent {
name = input.required<string>();
deleted = output<string>();
onDelete(): void {
this.deleted.emit(this.name());
}
}

In the parent:

<app-user-card
[name]="'Alice'"
(deleted)="handleDelete($event)"
/>
handleDelete(userName: string): void {
console.log(`Deleting user: ${userName}`);
}

Decorator-based Outputs (Legacy — still supported)

Section titled “Decorator-based Outputs (Legacy — still supported)”
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `<button (click)="sendMessage()">Click</button>`
})
export class UserCardComponent {
@Output() messageSent = new EventEmitter<string>();
sendMessage(): void {
this.messageSent.emit('Hello from child!');
}
}

Model Inputs — Two-Way Binding (Angular 18+)

Section titled “Model Inputs — Two-Way Binding (Angular 18+)”

For two-way data binding between parent and child, use model():

import { Component, model } from '@angular/core';
@Component({
selector: 'app-toggle',
template: `
<button (click)="toggle()">
{{ isActive() ? 'ON' : 'OFF' }}
</button>
`
})
export class ToggleComponent {
isActive = model<boolean>(false);
toggle(): void {
this.isActive.update(value => !value);
}
}

In the parent — use the banana-in-a-box syntax:

<app-toggle [(isActive)]="showPanel" />

FeatureLegacy SyntaxModern Syntax (Angular 18+)
Inputs@Input() name: stringname = input<string>()
Required inputs@Input({ required: true })name = input.required<string>()
Outputs@Output() event = new EventEmitter()event = output<T>()
Two-way binding@Input() + @Output() pairvalue = model<T>()