Adopting the latest Angular features with a novel mindset for writing cleaner, more efficient code.
Kevin Kreuzer
@nivekcode
Dec 2, 2024
7 min read
Hello, Angular community!
If you’ve kept up with the latest Angular releases, you’re likely as excited as I am about Signals! 🚀 The possibilities they unlock are truly impressive.
Want to understand Signals and how they can change the way you build Angular apps? Then grab a copy of our new eBook on Angular Signals. It’s full of practical advice and insights to help you use Signals for faster, more responsive applications.
Recently, I got the opportunity to dig deep and refactor a substantial codebase with Signals and Signal-based APIs. It was quite an adventure!🏄♂
As I worked through it, fresh coding styles and patterns came into focus, prompting us to tweak our coding guidelines along the way. And that’s why I decided to share our updated coding style with you.
Before we dive in
Quick heads-up before we jump in: this article blends best practices with personal tastes. As you go, feel free to pick what works for you. Some tips are widely endorsed; others are simply our way of working. In the end, what matters is what fits best for you and your team.
To make things clearer, I’ve sorted every section into one of two categories:
best practices: steps that are broadly smart to follow and recommended.
personal preferences: My own tastes—feel free to adopt them, but know they’re not universal best practices, just what I prefer.
Standalone leads the way
Best Practice Alert! 🚨
Ever since standalone components hit the scene, we’ve leaned on them more and more. They’ve quickly become the default choice in modern Angular work.
And with Angular 19 arriving, the framework pushes this further by making standalone components the standard right out of the box.
// Standalone component < Angular 19
@Component({
standalone: true,
selector: 'my-component',
template: `Hello World!`
})
// Standalone component in Angular 19
@Component({
// no standalone flag needed anymore
selector: 'my-component',
template: `Hello World!`
})
This shift solidifies standalone components as the fundamental building block for contemporary Angular applications.
At this point, the advantages of standalone components are widely understood: a clearer conceptual framework, enhanced developer satisfaction, and superior optimization through tree-shaking. Angular 19, however, takes additional strides toward enforcing uniformity and modern practices within your codebase.
Expert advice! Angular 19 includes a compiler option that raises an error whenever it finds a component, directive, or pipe lacking the standalone flag.
{
"angularCompilerOptions": {
"strictStandalone": true
}
}
Going All-In on Signals
Best Practice Alert! 🚨
Signals represent the next step for Angular, as noted earlier. The new signal APIs have made it simpler and more straightforward than ever to craft clean, reactive applications.
To get the most out of this fresh approach, we highly recommend adopting every signal-related API at your disposal:
- Signals
- Computed Signals
- Signal Inputs
- Signal Queries
Using the complete set of signal APIs isn’t just about boosting reactivity and performance—it’s also about making your codebase ready for what’s coming next.
Signals will play a pivotal role in Angular's shift toward Zoneless applications and will be indispensable once Signal components become a reality.
We’re not going to explore this topic any further here, as signals are so extensive that they could fill an entire book on their own.
And coincidentally, that’s precisely what we’ve written! 😅
Embracing a Zoneless Future
Best Practice Alert! 🚨
For years, Zone.js has been a persistent frustration for Angular developers, but Angular v19 finally lets us say goodbye to it. This is a major win for performance, cutting down on unnecessary change detection runs and enabling much more precise detection of updates.
That said, dropping Zone.js isn’t without its challenges—it might introduce regressions into your application. This is where signals really shine. To make your codebase ready for a zoneless world, be sure to adopt signals thoroughly and implement OnPush change detection.
An app that combines signals with OnPush in a Zone-based setup is already primed for this transition. Such an application can operate smoothly without Zone.js, delivering both performance and longevity. So, begin applying these practices today to get your app zoneless-ready!
A Shift Away from Conventional Lifecycle Hooks
Best Practice Alert! 🚨
Time to talk lifecycle hooks—or better yet, let’s discuss how to get rid of them! With Signals, computed values, and effects in the mix, many of the older Angular lifecycle hooks are no longer necessary. Wave farewell to ngOnInit and ngAfterViewInit, and welcome a more efficient approach!
Developing Angular without lifecycle hooks, what would that even look like? The following diagram illustrates just that.

I’ve used a color-coded approach here: green signals that the old API is no longer needed, while orange highlights cases where lifecycle hooks might still be relevant, though an alternative is often preferable.
And to be clear, afterRender and afterNextRender are indeed lifecycle hooks—they’re just the newer additions. When we talk about “without traditional lifecycle hooks,” we’re referring to the ones that have long existed.
Still not convinced this is a big deal? Let’s look at a basic example. Picture a simple component that receives a number via input and tells you if it’s even or odd. Here’s how it works.
@Component({
standalone: true,
selector: 'is-even',
template: `<h1>Is Even: {{ isEven }}</h1>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IsEvenComponent implements OnChanges {
isEven: boolean | undefined;
@Input({ required: true }) counter!: number;
ngOnChanges(changes: SimpleChanges): void {
if (changes['counter']) {
this.isEven = changes['counter'].currentValue % 2 === 0;
}
}
}
If the counter value changes and we want the view to reflect that, the ngOnChanges life cycle hook is the required tool. But with Signals, we can drop that hook entirely and rebuild the same component using a "no lifecycle hooks" strategy.
@Component({
standalone: true,
selector: 'is-even',
template: `<h1>Is Even: {{ isEven() }}</h1>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IsEvenComponent {
counter = input.required<number>();
isEven = computed(() => this.counter() % 2 === 0);
}
It’s simple and refined.
Prefer Inject Over Constructor Injection
Yet Another Top Tip! 💡
In Angular, dependency injection acts like the magic ingredient that elevates the whole experience. 🍔 With modern Angular, you have two main routes to pull in a dependency: the classic constructor-based approach or the inject function.
Constructor injection has been the traditional path, but here’s why I’ve switched my allegiance to inject.
- When paired with injection tokens, the
injectfunction preserves the proper type, ensuring type-safe dependency injection. - The
injectfunction works inside any function, which opens up the possibility of crafting composable functions that tap into services. - With class inheritance, you skip the chore of forwarding injected services to the parent class via
superin theconstructor.
The constructor approach depends on TypeScript’s useDefineForClassFields flag, which must be manually turned off. In contrast, inject stands out as sturdier and built to last, making it a reliable pick. And let’s face it, a little less boilerplate is always a win.
One warning, though — adopting
injectcould disrupt some of your unit tests. That’s especially true if you’ve been sticking with plain vanilla unit tests and haven’t leveraged the power ofTestBedModuleto mock dependencies (a frequent setup in service tests). So, get ready to tweak your testing approach to keep everything running smoothly.
Embracing a Constructor-Free Style
Venturing Into Bold Territory! 💡
Now we’re entering a contentious space — this is where opinions get strong.
In a recent massive codebase rewrite using Signal APIs, we discovered something surprising: constructors were almost never required. Yep, you read that right. Ditching constructors entirely might seem extreme, but it’s far more practical than you’d think!
By swapping constructor injection for the inject function, constructors become unnecessary for dependency injection. But what about logic that initializes values? Thanks to Signals, initialization happens naturally at the point of field declaration, since Signals always require a starting value. Constructors are truly needed only in scarce, rare cases.
But wait, how about effects or the modern lifecycle hooks like afterRender or afterNextRender? Usually, those get wired up inside a constructor, right?
@Component({ /* ... */ })
export class MyComponent {
constructor() {
effect(() => { /* ... */
});
afterRender(() => { /* ... */
});
afterNextRender(() => { /* ... */
});
}
}
You’re free to store these directly on class properties. Here’s how it looks:
@Component({ /* ... */ })
export class MyComponent {
#logProductChanges = effect(() => { /* ... */
});
#afterNextRenderRef = afterRender(() => { /* ... */
});
#afterNextRender = afterNextRender(() => { /* ... */
});
}
Heads up: your editor may complain about unused variables here, since they aren't technically invoked within the class body.
This pattern tidies up your codebase and improves structure. Much like the named versus anonymous function debate, there's no single correct choice—it comes down to preference and what fits your project.
Say Goodbye to Async Pipes: A Fresh Take
Pro Tip! 💡
If you've fully embraced Signals (and why wouldn't you?), it's time to move on from the async pipe. The shift is toward relying purely on Signals. Don't worry—RxJS isn't being thrown out the window.
RxJS remains your tool for managing HTTP calls, but once the data arrives, you'll transform it with toSignal from the interop package before it touches the template. The result? A smoother, more uniform reactive view, while RxJS keeps handling the heavy lifting for complex streams. Pretty cool, right?
@Component({
template: `{{ someValue() }}`
})
export class MyComponent {
someValue = toSignal(someStream$);
}
Replacing Private with
Heavily Subjective Ahead! 💡
Diving deeper into the details, many of us use private to mark class members in TypeScript, but there are actually two distinct syntaxes: the private keyword or the # prefix.
This choice seems purely aesthetic, yet it creates a real functional separation:
private: A TypeScript-only decorator that limits accessibility within the same class hierarchy. Its enforcement stops at the transpilation stage — once the code becomes plain JavaScript, every member is exposed and mutable.#: A native JavaScript implementation called private class fields, which stays strong during execution. Any variable marked with#remains shielded from outside intervention, even after TS-to-JS conversion. That’s far more solid, isn’t it?
For ages, I stuck with the private keyword — I mean, who didn’t? But during a lunch chat, just before tackling a major restructuring, a teammate suggested we adopt # instead. Initially, I doubted it (isn’t resistance to change universal?), yet after reflecting on it — # guarantees runtime safety, is fundamentally sturdier, and benefits from being shorter to write.
So why not experiment?
Within a single day, I was completely won over! It just made sense, and my old habits vanished. Sometimes you must set aside your pride, embrace novelty, and discover what actually works.
@Component({
// ...
})
export class MyComponent {
#userService = inject(UserService);
}
That wraps up our look at Signals and how they can reshape your Angular projects — leaner, sharper, and arguably a lot more enjoyable to build. Ultimately, the key is discovering the approach that clicks for you and your crew.
So dive in, try things out, and don't shy away from a few mishaps along the way. After all, you can't whip up a masterpiece without cracking a few shells! 🍳
Enjoying the vibe of the code preview? Check out our brand-new theme plugin
Skol - the ultimate IDE theme
Aurora-level aesthetics, delivered right into your editor. This dark theme keeps things minimal, looks polished, and is easy on the eyes.
Create smarter interfaces with Angular and AI
Angular + AI Video Course
A practical workshop for adding AI capabilities to Angular applications, leveraging Hash Brown for building responsive, smart interfaces.
Explore real-time streamed chat, function invocation, dynamic UI generation, structured data output, and beyond—all in a guided progression.
Future-proof your Angular skills and take command of Signals with this in-depth resource!
The Angular Signals Masterclass Guide
Find out why Angular Signals are indispensable, take a deep dive into their comprehensive API, and reveal the mysteries of their internal mechanics.
Boost your expertise and position yourself for Angular's future. Stay ahead of the curve!
Enjoy the read and want to become an expert in Angular's latest Signal Forms?
Angular Signal Forms: A Practical Workshop
Twelve progressive chapters combine theoretical insights with practical labs to help you dominate Angular's new Signal-Forms.
Explore core form concepts, validation logic, bespoke controls, nested forms, conversion tactics, and beyond.
Get notified
about new blog posts
Subscribe to Angular Experts Content Updates & News, and we will notify you every time we publish a fresh article on Angular, Ngrx, RxJs, or other compelling Frontend subjects!
Your email address remains confidential, and you are free to cancel your subscription at any moment!
Responses & comments
Feel free to ask anything and share your personal insights and views on the subject
You might also like
Explore additional blog posts from Angular Experts to deepen your knowledge on similar themes, including Modern Angular !

Angular Signal Forms: Custom Controls Without ControlValueAccessor
Build reusable Angular custom controls with FormValueControl, model(), touch events, and schema-driven validation—without writing a ControlValueAccessor.

Kevin Kreuzer
@nivekcode
Aug 12, 2026
7 min read

Angular Signal Forms: The Missing Create/Edit Pattern
Learn a practical Angular Signal Forms pattern for create and edit flows, with route-based mode, edit data loading, linkedSignal prefilling, submit branching, and validation context.

Kevin Kreuzer
@nivekcode
Aug 1, 2026
6 min read

Angular Signal Forms Essentials
Understand the core concepts behind modern Angular Forms. Learn how to create Signal Forms, wire them up in templates, use built-in and custom validators, handle cross-field validation, submit forms, and more.

Kevin Kreuzer
@nivekcode
Feb 14, 2026
12 min read
Empower your team with our extensive experience
Angular Experts have spent many years consulting with enterprises and startups alike, leading workshops and tutorials, and maintaining rich open source resources. We take great pride in our experience in modern front-end and would be thrilled to help your business boom
