Transitioning to Angular Signal-Based Inputs
Learn how to effectively migrate from traditional @Input decorators to Angular's new signal-based inputs.
With the introduction of Angular v17, the Angular team has brought significant improvements to the framework, focusing on reactivity and type safety. One of the key changes is the shift from the traditional @Input
decorator to the new signal-based inputs. This article will guide you through the transition from the old approach to the new, showcasing how to use signal-based inputs effectively in your Angular components.
We'll cover various use cases, such as aliasing, transforming inputs, handling required and optional inputs, and more. By the end of this article, you'll be able to easily adopt these new patterns in your projects.
Common Usage Example
Before
@Component({
selector: 'app-user-profile',
template: `<p>User: {{ name }}</p>`
})
export class UserProfileComponent {
@Input() name: string;
}
After
@Component({
selector: 'app-user-profile',
template: `<p>User: {{ name() }}</p>`
})
export class UserProfileComponent {
name = input<string>();
}
In this simple example, the name
input is used to pass a value from the parent component to the UserProfileComponent
. The old approach uses the @Input
decorator, while the new approach uses the input
function, exposing name
as a signal that can be accessed by calling name()
in the template.
Inputs with Default Value
Before
import { Input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
@Input() age: number = 30;
}
After
import { input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
age = input<number>(30);
}
Aliasing an Input
Before
import { Input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
@Input('userName') name: string = "";
}
After
import { input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
name = input<string>("", { alias: 'userName' });
}
Transforming an Input
Before
import { booleanAttribute, Input } from '@angular/core';
@Component({..})
export class ToggleComponent {
@Input({ transform: booleanAttribute }) disabled: boolean = false;
}
After
import { booleanAttribute, input } from '@angular/core';
@Component({..})
export class ToggleComponent {
disabled = input<boolean>(false, { transform: booleanAttribute });
}
Required Inputs
Before
import { Input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
@Input({ required: true }) lastName: string;
}
After
import { input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
lastName = input.required<string>();
}
Monitoring Input Changes
Before
import { Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({..})
export class UserProfileComponent implements OnChange {
@Input() firstName: string;
ngOnChanges(changes: SimpleChanges): void {
console.log(changes);
}
}
After
import { effect, input } from '@angular/core';
@Component({..})
export class UserProfileComponent {
firstName = input<string>();
constructor() {
effect(() => {
console.log(this.firstName());
});
}
}
Conclusion
As we've explored in this article, transitioning to Angular's signal-based inputs brings a new level of reactivity and type safety to your components. This shift aligns with Angular's ongoing efforts to improve performance and developer experience.
By adopting signal-based inputs, you can:
- Simplify your component code
- Improved change detection
- Enhance reactivity in your applications
- Reduce the need for lifecycle hooks like
ngOnChanges
While the transition might require some initial effort, especially in larger projects, the benefits in terms of code clarity and maintainability are significant.
Keep exploring, keep learning, and happy coding!