Adopting the latest Angular features with a novel mindset for writing cleaner, more efficient code.

emoji_objects emoji_objects emoji_objects
Kevin Kreuzer

Kevin Kreuzer

@nivekcode

Dec 2, 2024

7 min read

My new Angular Coding Style
share

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.

My new Angular Coding Style - Angular Experts — figure 3

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 inject function preserves the proper type, ensuring type-safe dependency injection.
  • The inject function 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 super in the constructor.

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 inject could 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 of TestBedModule to 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

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

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

Angular Signals Mastercalss eBook

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

Angular Signal Forms: Hands-On Masterclass

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.

Win win deal illustration

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!

Emails might occasionally feature extra promotional offers; for further information, check out our Privacy policy.

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

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.

emoji_objects emoji_objects emoji_objects
Kevin Kreuzer

Kevin Kreuzer

@nivekcode

Aug 12, 2026

7 min read

Angular Signal Forms: The Missing Create/Edit Pattern

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.

emoji_objects emoji_objects emoji_objects
Kevin Kreuzer

Kevin Kreuzer

@nivekcode

Aug 1, 2026

6 min read

Angular Signal Forms Essentials

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.

emoji_objects emoji_objects emoji_objects
Kevin Kreuzer

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