Signal Inputs (and Outputs)
The input function allows you to create a signal-based input for your component.
It can allow for required inputs, or optional with a default.
In the following example, history is a required input, and headerMessage is optional.
There is an output function for creating outputs, but it is not signal based.
typescript
import { Component, input, output } from '@angular/core';
import { CounterHistoryItem } from './signals-one.component';
import { DatePipe } from '@angular/common';
@Component({
selector: 'app-list',
standalone: true,
imports: [DatePipe],
template: `
<div class="overflow-x-auto">
<h2 class="text-2xl font-bold">{{headerMessage()}}</h2>
<table class="table">
<!-- head -->
<thead>
<tr>
<th>Op</th>
<th>Before</th>
<th>After</th>
<th>When</th>
</tr>
</thead>
@for(h of history(); track h.when) {
<tr>
<td>{{ h.op }}</td>
<td>{{ h.before }}</td>
<td>{{ h.after }}</td>
<td>{{ h.when | date : 'HH:MM:SSS' }}</td>
</tr>
}
</table>
<button (click)="clearHistory.emit()" class="btn btn-secondary">
Clear History
</button>
</div>
`,
styles: ``
})
export class ListComponent {
history = input.required<CounterHistoryItem[]>();
headerMessage = input('Counter History');
clearHistory = output();
}Using this from the Counter in the previous example:
typescript
import { DatePipe, JsonPipe } from '@angular/common';
import { Component, computed, effect, signal } from '@angular/core';
import { ListComponent } from './list.component';
type CounterHistoryOp = 'increment' | 'decrement' | 'reset';
export type CounterHistoryItem = {
before: number;
after: number;
op: CounterHistoryOp;
when: string;
};
@Component({
selector: 'app-signals-one',
standalone: true,
template: `
<div>
<button class="btn btn-lg btn-ghost" (click)="decrement()">-</button>
<span>{{ current() }}</span>
<button class="btn btn-lg btn-ghost" (click)="increment()">+</button>
</div>
<div>
<button
(click)="reset()"
class="btn btn-lg btn-warning"
[disabled]="atBeginning()"
>
Reset
</button>
</div>
<div>
@if(hasHistory()) {
<app-list
[history]="history()"
headerMessage="Your History"
(clearHistory)="history.set([])" />
}
</div>
`,
styles: ``,
imports: [DatePipe, ListComponent],
})
export class SignalsOneComponent {
current = signal(0);
history = signal<CounterHistoryItem[]>([]);
constructor() {
const saved = localStorage.getItem('current');
if (saved != null) {
this.current.set(+saved);
}
effect(() => {
localStorage.setItem('current', this.current().toString());
});
}
increment() {
this.doOp('increment', (n) => n + 1);
}
decrement() {
this.doOp('decrement', (n) => n - 1);
}
reset() {
this.doOp('reset', (_) => 0);
}
atBeginning = computed(() => this.current() === 0);
hasHistory = computed(() => this.history().length > 0);
private doOp(op: CounterHistoryOp, f: (c: number) => number) {
const before = this.current();
this.current.update(f);
this.history.update((h) => [
{ before, op, after: this.current(), when: new Date().toISOString() },
...h,
]);
}
}