Hey Angular developers! 🅰️ While features like signals and standalone components get all the attention, Angular has quietly introduced many smaller (but just as impactful) improvements. These lesser-known tools can make the codebase cleaner, faster, and easier to maintain.
In this article, I’ll reveal some powerful but underused Angular features, along with the version they were introduced in.
takeUntilDestroyed Operator (v16+)
The takeUntilDestroyed
operator, as part of the @angular/core/rxjs-
package, is a utility in Angular for managing observable
interop
subscriptions to prevent memory leaks. It automatically completes an
observable when the associated Angular component, directive, service, or
pipe is destroyed, eliminating the need for a manual subscription.
import { Component } from '@angular/core';
import { interval } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-timer',
template: `<p>Timer: {{ count }}</p>`,
})
export class TimerComponent {
count = 0;
constructor() {
interval(1000)
.pipe(takeUntilDestroyed())
.subscribe(() => this.count++);
}
}
Key Benefits:
- Eliminates manual cleanup with
ngOnDestroy
. - Prevents memory leaks effortlessly.
- Works in any injection context (no
DestroyRef
needed in constructors).
Note: Use it in constructors or providers for maximum simplicity.
inject() – Dependency Injection Without a Constructor (v14+)
Gone are the days when DI was limited to constructors. With inject()
, we grab dependencies anywhere, even in utility functions.
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// No constructor needed!
const http = inject(HttpClient);
Key Benefits:
- Works outside components (e.g., in functions or services).
- It makes unit testing easier (there is no need for TestBed in some cases).
- Reduces boilerplate in standalone components.
NgOptimizedImage Directive (Angular 15+)
Images can slow down your app if not optimized. This directive automatically handles lazy loading and performance tweaks!
Although the NgOptimizedImage directive was made a stable feature in Angular version 15, it has been backported and is available as a stable feature in versions 13.4.0 and 14.3.0 as well.
<img ngSrc="hero.jpg" width="1200" height="800" priority>
Key Benefits:
- Automatic
srcset
generation - Lazy-loads images to boost performance.
- Prevents layout shifts with proper sizing.
- Supports CDN optimizations like automatic srcset.
Note: Use the priority attribute for above-the-fold images to prioritize loading.
provideHttpClient() (Angular 15+)
The provideHttpClient()
function sets up HTTP services in one line, with support for interceptors, making it a must-have for standalone apps.
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient} from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
],
};
Key Benefits:
- Eliminates HttpClientModule boilerplate.
- Simplifies HTTP setup for standalone components.
- Supports interceptors for advanced request handling (e.g., auth tokens)
input/output with Signals (v17+)
These features are Signal-based functions that replace the traditional @Input()
and @Output()
decorators
Below is a code sample of its usage:
import { Component, input, output } from '@angular/core';
// Child Component
@Component({
selector: 'app-counter',
template: `
<div class="counter">
<button (click)="decrement()">-</button>
<span>{{ count() }}</span>
<button (click)="increment()">+</button>
</div>
`,
standalone: true
})
export class CounterComponent {
count = input(0); // Equivalent to @Input()
countChanged = output<number>(); // Equivalent to @Output()
increment() {
const newValue = this.count() + 1;
this.countChanged.emit(newValue);
}
decrement() {
this.countChanged.emit(this.count() - 1);
}
}
// Parent Component
@Component({
selector: 'app-parent',
template: `
<app-counter
[count]="currentCount()"
(countChanged)="handleCountChange($event)"
/>
`,
standalone: true,
imports: [CounterComponent]
})
export class ParentComponent {
currentCount = signal(5);
handleCountChange(newValue: number) {
this.currentCount.set(newValue);
}
}
Key benefits:
- Type-safe by default
- Works seamlessly with Signals
- Simpler than traditional @Input/@Output
Required Inputs Signal (Angular 17+)
Missing component inputs can lead to runtime errors. Marking inputs as required ensures they’re always provided, catching issues early.
import { Component, input } from '@angular/core';
@Component({
selector: 'app-card',
template: `<h2>{{ title() }}</h2>`,
})
export class CardComponent {
title = input.required<string>();
}
model() Function – Reactive Two-Way Binding (Angular 17+)
It replaces the @Input()
and @Output()
decorators with model()
function for a unified, reactive API that simplifies component communication.
Below is a code sample:
import { Component, model } from '@angular/core';
// Child Component (Reusable Toggle)
@Component({
selector: 'app-toggle',
template: `
<button (click)="toggle()" [class.active]="value()">
{{ value() ? 'ON' : 'OFF' }}
</button>
`,
styles: [`
button {
padding: 0.5rem 1rem;
border: 1px solid #ccc;
}
.active {
background: #3f51b5;
color: white;
}
`],
standalone: true
})
export class ToggleComponent {
// Creates a two-way binding model
value = model<boolean>(false); // Default: false
toggle() {
this.value.set(!this.value()); // Updates parent automatically
}
}
// Parent Component
@Component({
selector: 'app-parent',
template: `
<h2>Current State: {{ isToggled() ? 'ACTIVE' : 'INACTIVE' }}</h2>
<!-- Two-way binding syntax -->
<app-toggle [(value)]="isToggled" />
<!-- Manual binding alternative -->
<app-toggle
[value]="isToggled()"
(valueChange)="isToggled.set($event)"
/>
`,
standalone: true,
imports: [ToggleComponent]
})
export class ParentComponent {
isToggled = signal(false); // Parent manages state
}
Key Benefits:
- Combines input and output for two-way binding.
- Reactive and efficient with Signals.
- Reduces component boilerplate.
Deferrable Views (v17+)
As part of the new control flow syntax, the deferrable view was introduced to lazy load template content.
Below is a code sample for its use case:
@defer (when condition; on trigger) {
<!-- Heavy content to lazy load -->
<expensive-component />
} @placeholder (optional) {
<!-- Shown while waiting to load -->
<skeleton-loader />
} @loading (optional) {
<!-- Shown during loading -->
<spinner />
} @error (optional) {
<!-- Shown if loading fails -->
<error-message />
}
Key Benefits:
- 90%+ reduction in initial bundle size
- Built-in loading/error states
- Multiple trigger conditions (viewport, idle, interaction)
Here’s a quick overview of the underutilized Angular features covered in this article:
Feature | Version | Primary Use Case |
---|---|---|
takeUntilDestroyed | 16+ | Observable cleanup |
inject() | 14+ | Flexible dependency injection |
NgOptimizedImage | 15+ | Image optimization |
provideHttpClient() | 15+ | Simplified HTTP setup |
input/output (Signals) | 17+ | Type-safe component communication |
Required Inputs | 17+ | Enforce input presence |
model() | 17+ | Reactive two-way binding |
Deferrable Views | 17+ | Lazy-loaded templates |
Conclusion
These features may not be seen in the spotlight, but very effective. Let's make use of these hidden gems, we can streamline our development process and build cleaner, faster, and more robust Angular applications with ease.
Happy coding! 🅰️
Top comments (4)
pretty cool stuff, tbh i always forget about like half of these - you ever feel like focusing on the little things makes a bigger difference over time or is it just the big features that matter most
Man this is the stuff I actually bookmark, saves me so much cleanup in my own projects.
Awesome, thanks for sharing @nevodavid
Gostei muito de como o post destacou funcionalidades do Angular que muita gente acaba deixando de lado — como o uso do
trackBy
nongFor
ou os poderes dong-template
. Às vezes a gente se prende só ao básico e esquece que a ferramenta oferece muito mais do que parece à primeira vista. Isso me lembrou como, assim como em Angular, plataformas como o Magis magistvgratiss.com/Gratis também têm funcionalidades escondidas que podem melhorar bastante a experiência do usuário, tipo opções de legendas ou controle de qualidade de vídeo que nem todo mundo explora. Qual dessas funcionalidades do Angular você acha que mais merece ser usada no dia a dia?