Starting in Angular version 17.3, we now have a new output API. Note that the new output is NOT a signal. So why did the Angular team define a new output API as part of its signal-ification of Angular?
import { Component, EventEmitter, Output, effect, output, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-filter',
standalone: true,
imports: [FormsModule],
template: `
<div class='card'>
<div class='card-body'>
<div class='row'>
<div class='col-sm-2'>Filter by:</div>
<div class='col-sm-5'><input type='text' [(ngModel)]='filter' /></div>
</div>
</div>
</div>
`
})
export class FilterComponent {
filter = signal('');
// @Output() processFilter = new EventEmitter<string>();
processFilter = output<string>({
alias: 'filterChange'
});
filterEff = effect(() => this.processFilter.emit(this.filter()));
}
Now our parent snacks component HTML
<div class="card border-secondary">
<div class="card-header text-secondary fw-bold">
{{ pageTitle }}
</div>
<div class="card-body">
<app-filter (filterChange)="filterList($event)"/>
<div class="row">
<div class="col-md-4 col-sm-4 fw-bold">Name</div>
<div class="col-md-4 col-sm-4 fw-bold text-center">Price</div>
</div>
@for (snackItem of filteredSnacks(); track snackItem) {
<div>
<div>
<div class="row mb-2">
<div class="col-md-4 col-sm-4">
{{ snackItem.name }}
</div>
<div class="col-md-4 col-sm-4 text-center">
{{ snackItem.price | currency }}
</div>
</div>
</div>
</div>
} @empty { No snacks available that match the filter }
</div>
</div>
Ts code
import { Component, computed, inject, signal } from '@angular/core';
import { FilterComponent } from '../filter/filter.component';
import { SnackService } from './snack.service';
import { CurrencyPipe } from '@angular/common';
@Component({
selector: 'app-snack',
standalone: true,
imports: [ FilterComponent, CurrencyPipe],
templateUrl: './snack.component.html'
})
export class SnackComponent {
pageTitle = 'Snacks';
listFilter = signal('');
snackService = inject(SnackService);
snacks = this.snackService.snacks;
filteredSnacks = computed(() =>
this.snacks().filter(s => s.name.includes(this.listFilter())));
filterList(filterText: string) {
this.listFilter.set(filterText);
}
}