Streamlining Communication: New Signals API in Angular 17.3
Sonu Kapoor
Posted on March 25, 2024
Introduction
Angular 17.3 introduces a developer preview of a new Signals API, a feature unveiled at the ngConf 2024 keynote by Jeremy Elbourn and Minko Gechev.
This API promises to simplify component communication and data management in your Angular applications. This article explores the four key features of this API -- Signal Queries, Signal Inputs, Signal Outputs (with a twist), and Model Inputs -- highlighting the potential benefits they bring.
1. Effortless Reference Retrieval with Signal Queries
Traditionally, Angular components have relied on decorators like @ViewChild
and @ContentChildren
to access template references. While functional, these decorators can add some verbosity to your code.
The Signals API offers a cleaner alternative. It allows you to directly declare references as signals within your component class. This not only improves readability but also introduces a new feature - the ability to mark a signal as required using .required
. With this addition, Angular will throw an error if the query result is missing, helping you catch potential issues early in development.
Here's a before-and-after example to illustrate the change:
// Before (using decorators)
@Component({/* ... */})
export class Menu {
@ViewChild('trigger') trigger: ElementRef;
@ContentChildren(MenuItem)
items: QueryList<MenuItem> | undefined;
}
// After (using signals)
@Component({/* ... */})
export class Menu {
trigger = viewChild('trigger');
// New: mark as required
items = contentChildren(MenuItem).required;
}
2. Signal Inputs: Powering Computed Properties and Effects
The @Input
decorator has been a cornerstone for passing data into components. However, it can feel limiting when working with computed properties or effects within your component.
The Signals API introduces signal-based inputs that seamlessly integrate with these functionalities. These signal inputs can be used within computed expressions and effects, providing a more reactive approach to data handling.
Here's an example showcasing the difference:
// Before (using @Input)
@Component({/* ... */})
export class Checkbox {
@Input() disabled = false;
@Input({required: true}) checked!: boolean;
}
// After (using signal inputs)
@Component({/* ... */})
export class Checkbox {
disabled = input(false);
checked = input.required<boolean>();
}
3. Outputs: Maintaining Consistency with Signal-Based Style
While the Signals API introduces a new approach for inputs, outputs still leverage the familiar @Output
decorator for consistency. That means they are not signal-based. This ensures a clear distinction between receiving data (inputs) and emitting events (outputs).
Here's a comparison highlighting the separation:
// Before (mixed styles)
@Component({/* ... */})
export class Checkbox {
disabled = input(false);
@Output() toggled = new EventEmitter<boolean>();
}
// After (consistent style)
@Component({/* ... */})
export class Checkbox {
disabled = input(false);
// Signal-based style for outputs (proposed)
toggled = output<boolean>();
}
Emitting events remain unchanged and continue to use the emit
method.
4. Model Inputs: Simplifying Two-Way Data Binding
Two-way data binding, a staple in Angular development, often involves setting up dedicated @Input
and @Output
properties with specific naming conventions.
The Signals API streamlines this process by introducing model inputs. These inputs allow you to directly define a signal within your component, eliminating the need for separate input and output properties. This not only reduces boilerplate code but also clarifies the intent of the data being bound.
Here's an example showcasing the simplification:
// Before (using @Input and @Output)
@Component({
// ...
template: '<cool-checkbox [(checked)]="isAdmin" />'
})
export class Profile {
isAdmin = false;
}
// After (using model input)
@Component({/* ... */})
export class CoolCheckbox {
checked = model(false);
}
// After (using model input, with signal
// change in Profile component)
@Component({/* ... */})
export class Profile {
// Create a signal for isAdmin property
isAdmin = signal(false);
}
This new approach provides a writable signal within your component, allowing you to directly update the bound value and propagate changes seamlessly.
Conclusion
By incorporating these Signals API features, you can enhance the readability, maintainability, and overall reactivity of your Angular applications. Remember, this API is currently in developer preview, so stay tuned for further updates and potential changes as it evolves.
Posted on March 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.