
Angular 19 Signals with input() and output()
With Angular 16+, the Composition API offers a modern and reactive way to manage component state using signal(), input(), and output(). In Angular 19, this approach is more powerful than ever, enabling lightweight and reactive component communication.
In this guide, we'll build a Product Dashboard using:
-
input()andoutput()for reactive component I/O -
signal()for local state management - Standalone components with full signal-based communication
Folder Structure
/components
├── product-page/
│ └── product-page.component.ts
├── product-add/
│ └── product-add.component.ts
├── product-list/
│ └── product-list.component.ts
1. product-add.component.ts – Emits new product using output()
import { Component, output, signal } from '@angular/core';
export interface Product {
id: number; name: string; price: number;
}
@Component({
selector: 'product-add',
standalone: true,
template: `
Add Product
Add
`,
})
export class ProductAddComponent {
name = signal('');
price = signal(0);
productAdded = output<Product>();
addProduct() {
if (!this.name || this.price() <= 0) {
return;
}
const productAdded: Product = {
id: Math.floor(Math.random() * 1000),
name: this.name(),
price: this.price(),
}
this.productAdded.emit(productAdded);
this.resetFields();
}
resetFields() {
this.name.set('');
this.price.set(0);
}
}
2. product-list.component.ts – Consumes products with input()
import { Component, input } from '@angular/core';
import { Product } from '../product-add/product-add.component';
@Component({
selector: 'product-list',
standalone: true,
template: `
Product List
Si cargo
@for (product of products(); track product.id; let idx = $index) {
{{ product.name }} - ${{ product.price }}
}
`
})
export class ProductListComponent {
products = input.required<Product[]>();
}
3. product-page.component.ts – Orchestrates everything with signal()
import { Component, signal } from '@angular/core';
import { ProductAddComponent, Product } from '../product-add/product-add.component';
import { ProductListComponent } from '../product-list/product-list.component';
@Component({
selector: 'product-page',
standalone: true,
imports: [ProductAddComponent, ProductListComponent],
template: `
Product Dashboard
`
})
export class ProductPageComponent {
products = signal<Product[]>([]);
addProduct(newProduct: Product) {
this.products.update(p => [...p, newProduct]);
}
}What This Shows
| Feature | Demonstrated |
|---|---|
input.required |
In ProductListComponent
|
output |
In ProductAddComponent
|
signal |
In ProductPageComponent
|
| Reactive flow | Between child and parent |
| Standalone components | ✅ |