<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manish Boge</title>
    <description>The latest articles on DEV Community by Manish Boge (@manish-boge).</description>
    <link>https://dev.to/manish-boge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3380938%2Fd571497a-48db-4d79-a13d-f36ddc3b051f.jpg</url>
      <title>DEV Community: Manish Boge</title>
      <link>https://dev.to/manish-boge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manish-boge"/>
    <language>en</language>
    <item>
      <title>OOP Applied in Angular: From Theory to Practice</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Sun, 08 Feb 2026 17:19:41 +0000</pubDate>
      <link>https://dev.to/manish-boge/oop-applied-in-angular-from-theory-to-practice-3md8</link>
      <guid>https://dev.to/manish-boge/oop-applied-in-angular-from-theory-to-practice-3md8</guid>
      <description>&lt;p&gt;Beyond the Basics: Building Scalable Angular Architecture&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7vytecynb45if03b2yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7vytecynb45if03b2yb.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/manish-boge/beyond-classes-the-oop-fundamentals-every-developer-must-know-2h62-temp-slug-853228"&gt;&lt;strong&gt;Part 1: OOP Fundamentals&lt;/strong&gt;&lt;/a&gt;, we explored Object-Oriented Programming (OOP) as a &lt;em&gt;design mindset,&lt;/em&gt; not just a set of keywords. We looked at how Abstraction, Encapsulation, Inheritance, Polymorphism, and Composition help software survive change over time using TypeScript as Programming Language.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;this part&lt;/strong&gt; , we bring those ideas to life by applying them directly to &lt;strong&gt;Angular applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Angular is deeply aligned with OOP principles — especially when you design your architecture intentionally.&lt;/p&gt;

&lt;p&gt;Today, we move past “basic classes” and look at how to use modern Angular features to enforce professional design patterns.&lt;/p&gt;

&lt;p&gt;Let us dive in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why OOP Matters in Angular Applications
&lt;/h3&gt;

&lt;p&gt;Angular applications don’t stay small for long. As features multiply and teams grow, codebases naturally move toward entropy. Without strong design boundaries, you end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;God Services:&lt;/strong&gt; Single files managing 5,000 lines of unrelated logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaky State:&lt;/strong&gt; Components reaching into services to mutate data directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fragile Hierarchies:&lt;/strong&gt; Deep inheritance trees where a change in the base class breaks ten children.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So OOP helps Angular apps by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Isolating responsibilities&lt;/li&gt;
&lt;li&gt;Protecting state&lt;/li&gt;
&lt;li&gt;Enforcing contracts&lt;/li&gt;
&lt;li&gt;Enabling extensibility through DI&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;OOP isn’t about writing more code; it’s about&lt;/em&gt; &lt;strong&gt;&lt;em&gt;isolating responsibilities.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let us now understand applying OOP principles one by one in Angular.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Abstraction: Contracts Over Concretes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Concept
&lt;/h4&gt;

&lt;p&gt;Abstraction in Angular means &lt;strong&gt;depending on contracts, not concrete implementations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In simple terms, Abstraction means depending on &lt;em&gt;what&lt;/em&gt; a service does, not &lt;em&gt;how&lt;/em&gt; it does it.&lt;/p&gt;

&lt;p&gt;In Angular, abstraction is most commonly achieved using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interfaces&lt;/li&gt;
&lt;li&gt;Abstract classes&lt;/li&gt;
&lt;li&gt;Dependency Injection (DI)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Problem: Tightly Coupled Services
&lt;/h4&gt;

&lt;p&gt;Many developers try to use TypeScript interfaces for DI. However, interfaces are kind of "ghosts"—they disappear at &lt;strong&gt;compile-time&lt;/strong&gt; , leaving Angular with no runtime token to inject.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In TypeScript,&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Interfaces&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;are metadata. They are used for type-checking during development but are completely stripped away (erased) during the transpilation process to JavaScript.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
export class OrderService {
  placeOrder(amount: number) {
    console.log('Paying via GooglePay');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business logic depends on a concrete payment provider&lt;/li&gt;
&lt;li&gt;Hard to replace or mock&lt;/li&gt;
&lt;li&gt;Hard to test&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Solution: Abstract the Behavior
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface PaymentGateway {
  charge(amount: number): void;
}

@Injectable()
export class GooglePay implements PaymentGateway {
  charge(amount: number): void {
    console.log('Charging via GooglePay');
  }
}

@Injectable()
export class OrderService {
  constructor(private paymentGateway: PaymentGateway) {}

  placeOrder(amount: number) {
    this.paymentGateway.charge(amount);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Angular DI Wiring
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;providers: [
  { provide: PaymentGateway, useClass: GooglePay}
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait … Here we made a mistake. The above code throws error at RunTime.&lt;br&gt;&lt;br&gt;
It will not find the token for the provider &lt;strong&gt;PaymentGateway&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But Why? — We have defined an interface right. Yes. But there is a problem with interface that we defined.&lt;/p&gt;

&lt;p&gt;Let us understand more about it and solve this issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; In TypeScript, &lt;strong&gt;Interfaces&lt;/strong&gt; are metadata. They are used for type-checking during development but are completely stripped away (erased) during the transpilation process to JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; When Angular’s Dependency Injection (DI) engine tries to run your code in the browser, it looks for a token named PaymentGateway. Because it was an interface, that token &lt;strong&gt;does not exist&lt;/strong&gt; in the JavaScript bundle.&lt;/p&gt;

&lt;p&gt;Angular will throw a NullInjectorError or an error stating it cannot find a provider for PaymentGateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution: Using Abstract Classes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike interfaces, abstract classes exist in the JavaScript runtime and can serve as unique DI tokens.&lt;/p&gt;

&lt;p&gt;So, we define an &lt;strong&gt;abstract class&lt;/strong&gt;. It acts as both the interface (contract) and the runtime DI token. Derived classes can &lt;strong&gt;extend&lt;/strong&gt;  it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 1. Use an Abstract Class instead of an Interface
export abstract class PaymentGateway {
  abstract charge(amount: number): void;
}

@Injectable({ providedIn: 'root' })
export class GooglePay extends PaymentGateway { // Use 'extends'
  charge(amount: number): void {
    console.log('Charging via GooglePay');
  }
}

@Injectable({ providedIn: 'root' })
export class OrderService {
  // 2. Modern Angular 'inject' function is preferred over constructor injection
  private paymentGateway = inject(PaymentGateway);

  placeOrder(amount: number) {
    this.paymentGateway.charge(amount);
  }
}

// 3. DI Wiring (in AppModule or AppConfig)
// Now PaymentGateway is a valid runtime token!
providers: [
  { provide: PaymentGateway, useClass: GooglePay }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What we achieved:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OrderService depends on &lt;em&gt;what&lt;/em&gt; a payment gateway does&lt;/li&gt;
&lt;li&gt;Not &lt;em&gt;how&lt;/em&gt; it does it&lt;/li&gt;
&lt;li&gt;Switching providers requires zero business logic change&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Abstraction defines&lt;/em&gt; &lt;strong&gt;&lt;em&gt;capability&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;, not the implementation.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Interface vs Abstract Classes
&lt;/h4&gt;

&lt;p&gt;We are extending the &lt;strong&gt;PaymentGateway. B&lt;/strong&gt; ut isn’t extends bad ? Like if we change &lt;strong&gt;PaymentGateway&lt;/strong&gt; class, will it not force child classes to change ?&lt;/p&gt;

&lt;p&gt;Let us understand these aspects to clear the difference between abstract class with and a standard class in the context of inheritance.&lt;/p&gt;

&lt;p&gt;When you use a standard class inheritance, you are inheriting &lt;strong&gt;behavior&lt;/strong&gt;  (code).&lt;/p&gt;

&lt;p&gt;When you use an abstract class with abstract methods, you are only inheriting a &lt;strong&gt;contract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it force children to change?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you add a new abstract method to the PaymentGateway base class, &lt;strong&gt;yes&lt;/strong&gt; , every child (GooglePay, Stripe, PayPal) class will “ &lt;strong&gt;break”&lt;/strong&gt; and force you to &lt;strong&gt;implement&lt;/strong&gt; that method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However, this is actually a good thing.&lt;/strong&gt; Abstraction is a contract. If your “Payment” contract changes to require a refund() method, we &lt;em&gt;want&lt;/em&gt; the compiler to tell us that GooglePay is now missing that capability. It prevents runtime errors where the OrderService tries to call a method that doesn't exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not just use an&lt;/strong&gt;  &lt;strong&gt;interface?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An interface is the purest form of a contract. But as we discussed, &lt;strong&gt;TypeScript interfaces don't exist in JavaScript.&lt;/strong&gt; If we strictly want to avoid the extends keyword but still want the safety of DI, you use an &lt;strong&gt;InjectionToken&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No worries — we will cover more about Injection Token in another article.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Encapsulation: Protecting State
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Concept
&lt;/h4&gt;

&lt;p&gt;Encapsulation protects an object’s internal state and ensures rules are enforced.&lt;/p&gt;

&lt;p&gt;In Angular, encapsulation applies strongly to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;State management logic&lt;/li&gt;
&lt;li&gt;Domain models&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Problem: Leaky State
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
export class CartService {
  products: Product[] = [];
}

this.cartService.products.push({ price: -100 }); // Invalid state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing prevents misuse. Any componet which consumes &lt;strong&gt;CartService&lt;/strong&gt; can modify the *&lt;em&gt;products *&lt;/em&gt; array.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Solution: Encapsulated State
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
export class CartService {
  private items: CartItem[] = [];

  addItem(item: CartItem) {
    if (item.price &amp;lt;= 0) {
      throw new Error('Invalid price');
    }
    this.items.push(item);
  }

  getItems(): readonly CartItem[] {
    return this.items;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Matters in Angular
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prevents accidental mutations&lt;/li&gt;
&lt;li&gt;Enforces business rules centrally&lt;/li&gt;
&lt;li&gt;Makes debugging predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Encapsulation keeps Angular apps &lt;strong&gt;stable under change&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Inheritance in Angular (Use Carefully)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Concept
&lt;/h4&gt;

&lt;p&gt;Inheritance models an &lt;strong&gt;Is-A&lt;/strong&gt; relationship.&lt;/p&gt;

&lt;p&gt;Angular supports inheritance well but it’s also where many designs go wrong.&lt;/p&gt;

&lt;p&gt;Let us understand now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Valid Use Case: Base Components
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export abstract class BaseComponent {
  isLoading = false;
  protected startLoading() {
    this.isLoading = true;
  }
  protected stopLoading() {
    this.isLoading = false;
  }
}

@Component({ /* ... */ })
export class UserComponent extends BaseComponent {
  loadUsers() {
    this.startLoading();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When Inheritance Hurts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deep component hierarchies&lt;/li&gt;
&lt;li&gt;Hidden side effects&lt;/li&gt;
&lt;li&gt;Tight coupling to base class changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inheritance increases coupling — hence hard to manage the derived calss in accordance with base class changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Use inheritance only when the relationship is undeniably&lt;/em&gt; Is-A_._&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Polymorphism in Angular
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Concept
&lt;/h4&gt;

&lt;p&gt;Polymorphism allows different implementations to be used interchangeably.&lt;/p&gt;

&lt;p&gt;Angular’s DI system is a &lt;strong&gt;polymorphism engine&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Problem: Conditional Services
&lt;/h4&gt;

&lt;p&gt;Let us understand a scenario where we need to use the Console for logs in development mode only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (env === 'dev') {
  this.logger.logToConsole();
} else {
  this.logger.logToServer();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If more environments like stage, uat , qa then it will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard to modify and read&lt;/li&gt;
&lt;li&gt;Easy to break&lt;/li&gt;
&lt;li&gt;We loose code extendability&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Polymorphic Solution
&lt;/h4&gt;

&lt;p&gt;We create a contract &lt;strong&gt;&lt;em&gt;Logger&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt; &lt;strong&gt;Implement&lt;/strong&gt; the Contract based on the requirement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export abstract class Logger {
  abstract log(message: string): void;
}

@Injectable()
export class ConsoleLogger extends Logger {
  log(message: string) {
    console.log(message);
  }
}

@Injectable()
export class ServerLogger extends Logger {
  log(message: string) {
    // send to API
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Environment-Based Injection
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;providers: [
  {
    provide: Logger,
    useClass: environment.production ? ServerLogger : ConsoleLogger
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No conditionals&lt;/li&gt;
&lt;li&gt;Behavior changes at runtime&lt;/li&gt;
&lt;li&gt;Open for extension, closed for modification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Polymorphism eliminates branching logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Abstraction + Polymorphism in Angular DI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Service
@Injectable()
export class ConsoleLogger implements Logger {
  log(message: string) {
    console.log(message);
  }
}

// In a component
constructor(private logger: Logger) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logger&lt;/strong&gt; → Abstraction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ConsoleLogger / ServerLogger&lt;/strong&gt; → Polymorphism&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Angular resolves the concrete class at  &lt;strong&gt;runtime&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Composition Over Inheritance in Angular
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Problem with Inheritance-Based Reuse
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class LoggingComponent {
  log(msg: string) {}
}

export class OrderComponent extends LoggingComponent {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Components now inherit behavior they may not always need.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution: Composition with Services
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
export class LoggerService {
  log(message: string) {
    console.log(message);
  }
}

@Component({ /* ... */ })
export class OrderComponent {
  constructor(private logger: LoggerService) {} // You can use inject function as well.

  createOrder() {
    this.logger.log('Order created');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why Angular Loves Composition
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;DI makes composition effortless&lt;/li&gt;
&lt;li&gt;Services are swappable&lt;/li&gt;
&lt;li&gt;Testing becomes trivial&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Composition creates&lt;/em&gt; &lt;strong&gt;&lt;em&gt;horizontal flexibility&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Mapping OOP Principles to Angular Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Abstraction → Interfaces, DI Tokens&lt;/li&gt;
&lt;li&gt;Encapsulation → Services, Private State&lt;/li&gt;
&lt;li&gt;Inheritance → Base Components / Classes&lt;/li&gt;
&lt;li&gt;Polymorphism → Dependency Injection&lt;/li&gt;
&lt;li&gt;Composition → Service Injection&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Angular is not anti-OOP it’s OOP done pragmatically.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;OOP in Angular is not about &lt;strong&gt;classes&lt;/strong&gt;. It’s about &lt;strong&gt;designing systems that evolve safely&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Depend on abstractions&lt;/strong&gt; to keep your services swappable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protect state aggressively&lt;/strong&gt; using access modifiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer composition&lt;/strong&gt; over deep inheritance trees.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Check List:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvmoblyg56egsaseq9aa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvmoblyg56egsaseq9aa.png" alt="Summary" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Should Do Next
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Refactor one fat component using services&lt;/li&gt;
&lt;li&gt;Replace conditionals with polymorphic providers&lt;/li&gt;
&lt;li&gt;Use abstract classes over interfaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you start &lt;em&gt;seeing&lt;/em&gt; OOP in Angular, you can’t unsee it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source Code &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;If you’d like to dive deeper or try the examples yourself, all source code including those discussed in this series is available on my GitHub repository: &lt;a href="https://github.com/Manishh09/oop-from-theory-to-angular/tree/master/part-2-angular-oop" rel="noopener noreferrer"&gt;&lt;strong&gt;Oop-Applied-Angular&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t forget to give this&lt;/strong&gt; &lt;a href="https://github.com/Manishh09/oop-from-theory-to-angular" rel="noopener noreferrer"&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;a star ⭐&lt;/strong&gt;  — not only does it help you stay updated when code changes, but it also shows appreciation for the content and motivates continued development.&lt;/p&gt;

</description>
      <category>designthinking</category>
      <category>architecture</category>
      <category>angular</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Beyond Classes: The OOP Fundamentals Every Developer Must Know</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Thu, 22 Jan 2026 04:32:58 +0000</pubDate>
      <link>https://dev.to/manish-boge/beyond-classes-the-oop-fundamentals-every-developer-must-know-19d9</link>
      <guid>https://dev.to/manish-boge/beyond-classes-the-oop-fundamentals-every-developer-must-know-19d9</guid>
      <description>&lt;p&gt;&lt;em&gt;Moving beyond syntax: A design-first guide to the four pillars of OOP in TypeScript&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrbbawqoxcvxdlucu18p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrbbawqoxcvxdlucu18p.png" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;OOP Fundamentals&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Object-Oriented Programming (OOP) is more than a set of keywords; it is a strategy for managing software complexity. While most developers can define its pillars, a few apply them to build systems that survive change over time.&lt;/p&gt;

&lt;p&gt;This guide explores the four pillars of OOP — and the essential rule of Composition — as architectural solutions to real-world problems.&lt;/p&gt;

&lt;p&gt;We’ll use &lt;strong&gt;TypeScript&lt;/strong&gt; programming language as code examples for clarity, but the ideas apply to any &lt;strong&gt;programming language&lt;/strong&gt; or &lt;strong&gt;framework&lt;/strong&gt; that you work with.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;Part 1/2&lt;/strong&gt; of the series.&lt;br&gt;&lt;br&gt;
In &lt;strong&gt;Part 2,&lt;/strong&gt; we will focus on &lt;em&gt;applying these concepts in&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Angular&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;applications&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, let’s understand the essentials of OOP.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Do We Need Object-Oriented Programming?
&lt;/h3&gt;

&lt;p&gt;The real-world problem in software engineering is &lt;strong&gt;complexity over time&lt;/strong&gt;. Software fails when it becomes too tangled to change.&lt;/p&gt;

&lt;p&gt;OOP addresses this by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolating changes:&lt;/strong&gt; Fix a bug in one place without breaking the whole system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reducing side effects:&lt;/strong&gt; Preventing data corruption through controlled access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modeling intent:&lt;/strong&gt; Mapping code logic directly to business requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;OOP&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is not about writing classes.&lt;br&gt;&lt;br&gt;
It’s about designing code that can change safely.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  The Four Pillars of OOP
&lt;/h3&gt;

&lt;p&gt;Abstraction, Encapsulation, Inheritance, and Polymorphism.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Abstraction
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Concept:&lt;/strong&gt; Abstraction is about focusing on &lt;em&gt;what&lt;/em&gt; an object does instead of &lt;em&gt;how&lt;/em&gt; it does it. It provides a simple “interface” to a complex internal process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; You shouldn’t need to understand bank API protocols just to process a payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Create a “contract” (Interface) that hides the internal details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Abstraction exposes intent while hiding complexity.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s see the code examples&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Abstraction:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Order {

  processOrder(amount: number) {
    console.log("Connecting to payment gateway...");
    console.log(`Charging ${amount} via Razorpay`);
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems with above code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business logic depends on payment details&lt;/li&gt;
&lt;li&gt;Switching payment providers is hard&lt;/li&gt;
&lt;li&gt;Testing becomes difficult&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;With Abstraction:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The Abstraction (The Contract)
interface PaymentGateway {
  charge(amount: number): void;
}

// The Implementation (The Detail)
class RazorpayGateway implements PaymentGateway {
  charge(amount: number): void {
    console.log(`Charging ${amount} via Razorpay`);
  }
}

class Order {
  // We depend on the abstraction, not the specific tool.
  constructor(private paymentGateway: PaymentGateway) {}

  processOrder(amount: number) {
    // business logic delegated respectively
    this.paymentGateway.charge(amount);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order class depends on &lt;strong&gt;behavior&lt;/strong&gt; , not implementation&lt;/li&gt;
&lt;li&gt;Payment logic can change independently&lt;/li&gt;
&lt;li&gt;Code becomes loosely coupled hence testable and flexible&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Encapsulation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Concept:&lt;/strong&gt; Encapsulation keeps an object’s data (state) private and only allows access through strictly defined methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; If any piece of code can change a balance variable directly, you will eventually end up with negative balances or corrupted data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Protect the data so the object can enforce its own rules.&lt;/p&gt;

&lt;p&gt;Let us see with an example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Poor Encapsulation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BankAccount {
  balance: number = 0;
}

const account = new BankAccount();
account.balance = -500; // mutable and incorrect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing prevents incorrect usage / modification in the above code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proper Encapsulation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BankAccount {
  private _balance: number = 0;

  deposit(amount: number): void {
    if (amount &amp;lt;= 0) throw new Error("Deposit must be positive");
    this._balance += amount;
  }

  withdraw(amount: number): void {
    if (amount &amp;gt; this._balance) throw new Error("Insufficient funds");
    this._balance -= amount;
  }
}

// usage
const account = new BankAccount();
account.balance = 500; // Not Allowed, throws error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Encapsulation is to protect the state of the Object.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  3. Inheritance
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Concept:&lt;/strong&gt; Inheritance allows a class to derive features from another class. It models an &lt;strong&gt;“Is-A”&lt;/strong&gt; relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Writing the same login() or logout() logic for Admin, Customer, and Guest leads to code duplication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Move common logic to a base "Parent" class.&lt;/p&gt;

&lt;p&gt;Code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User {
  constructor(public email: string) {}

  logout() {
    console.log(`${this.email} logged out safely.`);
  }
}

class Admin extends User {
  deleteUser(userId: string) {
    console.log(`Admin deleted user ${userId}`);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Admin &lt;em&gt;is a&lt;/em&gt; User— this relationship makes sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inheritance Pitfalls:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deep inheritance trees&lt;/li&gt;
&lt;li&gt;Overriding base behavior unexpectedly&lt;/li&gt;
&lt;li&gt;Changes in parent classes breaking children&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Inheritance increases coupling. Use it intentionally, not by default.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  4. Polymorphism
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The Concept:&lt;/strong&gt; Polymorphism allows you to treat different objects as the same type. It allows a single function to work with different classes interchangeably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; Massive if/else blocks that check types (e.g., if (type === 'sms') ... else if (type === 'email')).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Use a common interface so the code doesn't care about the specific type.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Polymorphism replaces conditionals with interchangeable behavior.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Conditional Logic:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function sendNotification(type: string, message: string) {
  if (type === "email") {
    console.log("Sending Email:", message);
  } else if (type === "sms") {
    console.log("Sending SMS:", message);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Proper Polymorphism usage:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Notification {
  send(message: string): void;
}

class EmailNotification implements Notification {
  send(message: string) {
    console.log("Sending Email:", message);
  }
}

class SmsNotification implements Notification {
  send(message: string) {
    console.log("Sending SMS:", message);
  }
}

// This function doesn't care WHAT service it is, as long as it can call .send()
function notifyAll(services: Notification[], message: string) {
  services.forEach(s =&amp;gt; s.send(message));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No conditionals&lt;/li&gt;
&lt;li&gt;New behaviors don’t modify existing code&lt;/li&gt;
&lt;li&gt;The system stays extensible&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Polymorphism removes conditional complexity.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Abstraction vs Polymorphism (A Commonly Confused Pair)
&lt;/h3&gt;

&lt;p&gt;Abstraction and Polymorphism are often explained together — and that’s exactly why many of us may confuse the two.&lt;/p&gt;

&lt;p&gt;They &lt;strong&gt;work together&lt;/strong&gt; , but they &lt;strong&gt;solve different problems&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Abstraction&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;defines&lt;/em&gt; &lt;strong&gt;&lt;em&gt;what a system can do , its like a contract&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Polymorphism&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;defines how that behavior can vary at _ **_runtime&lt;/em&gt;** &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s clarify this using a &lt;strong&gt;real-world payment example&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Abstraction:
&lt;/h4&gt;

&lt;p&gt;In a payment system, the business doesn’t care &lt;em&gt;how&lt;/em&gt; a payment is processed.&lt;br&gt;&lt;br&gt;
It only cares &lt;em&gt;that&lt;/em&gt; a payment can be processed or refunded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface PaymentGateway {
  processPayment(amount: number): boolean;
  refundPayment(transactionId: string): boolean;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this tells us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;These operations exist&lt;/li&gt;
&lt;li&gt;Consumers can rely on them&lt;/li&gt;
&lt;li&gt;No implementation details are exposed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No Razorpay logic&lt;/li&gt;
&lt;li&gt;No Stripe logic&lt;/li&gt;
&lt;li&gt;Only &lt;strong&gt;business capability&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This&lt;/em&gt; &lt;strong&gt;&lt;em&gt;interface&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is pure abstraction.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Polymorphism:&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Polymorphism allows &lt;strong&gt;different implementations&lt;/strong&gt; of the same abstraction to be used &lt;strong&gt;interchangeably&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each payment provider follows the same contract — but behaves differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class RazorPay implements PaymentGateway {
  processPayment(amount: number): boolean {
    // RazorPay-specific logic
    return true;
  }

  refundPayment(transactionId: string): boolean {
    // RazorPay-specific logic
    return true;
  }
}

class Stripe implements PaymentGateway {
  processPayment(amount: number): boolean {
    // Stripe-specific logic
    return true;
  }

  refundPayment(transactionId: string): boolean {
    // Stripe-specific logic
    return true;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here both classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the same &lt;strong&gt;abstraction&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Provide &lt;strong&gt;different internal behavior&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Can be swapped &lt;strong&gt;without changing usage code&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is polymorphism in action.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Where Abstraction and Polymorphism Meet
&lt;/h4&gt;

&lt;p&gt;Now look at this line carefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const paymentGateway: PaymentGateway = new RazorPay();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what is happening here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The variable type is PaymentGateway → &lt;strong&gt;Abstraction&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The actual object is RazorPay → &lt;strong&gt;Polymorphism&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system calls the RazorPay implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Switching providers becomes trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const paymentGateway: PaymentGateway = new Stripe();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The&lt;/em&gt; &lt;strong&gt;&lt;em&gt;calling code&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;doesn’t change — only the&lt;/em&gt; &lt;strong&gt;_behavior _&lt;/strong&gt; &lt;em&gt;does.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Why This Design Matters in Real Systems?
&lt;/h4&gt;

&lt;p&gt;This approach gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loose coupling&lt;/li&gt;
&lt;li&gt;Easy extensibility&lt;/li&gt;
&lt;li&gt;Cleaner code&lt;/li&gt;
&lt;li&gt;Better testability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adding a new provider will be easy in future:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class PayPal implements PaymentGateway {
  processPayment(amount: number): boolean {
    return true;
  }

  refundPayment(transactionId: string): boolean {
    return true;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Composition Over Inheritance (A Modern OOP Rule)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt; Composition is the practice of combining simple objects to create complex ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inheritance for Code Reuse:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Logger {
  log(message: string) {
    console.log(message);
  }
}

class Order extends Logger {
  createOrder() {
    this.log("Order created");
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, Logger and Order classes are tightly coupled.&lt;/p&gt;

&lt;p&gt;Let us see the code example how we can solve this issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composition:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Logger {
  log(message: string) {
    console.log(message);
  }
}

class Order {
  // inject dependency
  constructor(private logger: Logger) {}

  createOrder() {
    this.logger.log("Order created");
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces coupling&lt;/li&gt;
&lt;li&gt;Improves testability&lt;/li&gt;
&lt;li&gt;Increases flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Inheritance&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is a “Vertical” relationship (Is-A).&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Composition&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is a “Horizontal” relationship (Has-A). In modern engineering, we prefer Composition because it is easier to swap parts at runtime.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction&lt;/strong&gt; hides implementation details&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation&lt;/strong&gt; protects state and rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inheritance&lt;/strong&gt; models true hierarchies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polymorphism&lt;/strong&gt; removes conditional logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition&lt;/strong&gt; enables flexible design&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;OOP is about designing systems that can change without breaking.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What’s Next?
&lt;/h3&gt;

&lt;p&gt;This article focused on &lt;strong&gt;learning OOP as a design mindset&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt; , we’ll apply these concepts directly to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular components&lt;/li&gt;
&lt;li&gt;Services and Dependency Injection&lt;/li&gt;
&lt;li&gt;Real Angular architecture patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Source Code &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;If you’d like to dive deeper or try the examples yourself, all source code including those discussed in this series is available on my GitHub repository: &lt;a href="https://github.com/Manishh09/oop-from-theory-to-angular/tree/master/part-1-core-oop" rel="noopener noreferrer"&gt;&lt;strong&gt;Oop-Typescript-Angular&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t forget to give this&lt;/strong&gt; &lt;a href="https://github.com/Manishh09/oop-from-theory-to-angular" rel="noopener noreferrer"&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;a star ⭐&lt;/strong&gt;  — not only does it help you stay updated when code changes, but it also shows appreciation for the content and motivates continued development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect with Me!
&lt;/h3&gt;

&lt;p&gt;Hi there! I’m &lt;strong&gt;Manish&lt;/strong&gt; , a Senior Engineer, passionate about building robust web applications and exploring the ever-evolving world of tech. I believe in learning and growing together.&lt;/p&gt;

&lt;p&gt;If this article sparked your interest in modern Angular, software architecture, or just a tech discussion, I’d love to connect with you!&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Let’s connect on&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/manishboge/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;for more tech insights and discussions.&lt;br&gt;&lt;br&gt;
📧 &lt;strong&gt;Follow me on&lt;/strong&gt; &lt;a href="https://medium.com/@manishboge" rel="noopener noreferrer"&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/a&gt;to catch Part 2 when it drops!&lt;br&gt;&lt;br&gt;
💻 *&lt;em&gt;Explore my work on *&lt;/em&gt; &lt;a href="https://github.com/Manishh09" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>cleanarchitecture</category>
      <category>architecture</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Mastering Dependency Injection — Part 1: The Essentials Every Developer Must Know</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Sat, 15 Nov 2025 13:05:17 +0000</pubDate>
      <link>https://dev.to/manish-boge/mastering-dependency-injection-part-1-the-essentials-every-developer-must-know-1akk</link>
      <guid>https://dev.to/manish-boge/mastering-dependency-injection-part-1-the-essentials-every-developer-must-know-1akk</guid>
      <description>&lt;h3&gt;
  
  
  Mastering Dependency Injection — Part 1: The Essentials Every Developer Must Know
&lt;/h3&gt;

&lt;p&gt;Unlock the design principle that powers modern frameworks powerful, testable, and scalable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp4z8mpoc6jiihezg07s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp4z8mpoc6jiihezg07s.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Before diving into any framework’s DI system, whether it’s Angular, Spring, or .NET Core, it’s essential to understand the &lt;em&gt;core design principle&lt;/em&gt; that powers them all.&lt;/p&gt;

&lt;p&gt;Dependency Injection (DI) isn’t just a buzzword; it’s the architectural glue that enables loose coupling, modularity, and testability in modern applications.&lt;/p&gt;

&lt;p&gt;In this two-part series of Understanding DI Concepts, we’ll break down DI from the ground up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1 (this article):&lt;/strong&gt; The core concepts, principles, and mental models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; Practical patterns, testing strategies, and framework integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the main goal of this series is to fully understand &lt;strong&gt;Angular’s Dependency Injection system&lt;/strong&gt; , we must first build a solid foundation by understanding DI fundamentals, independent of any framework.&lt;/p&gt;

&lt;p&gt;Let us start with the essentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Dependency Injection Matters
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dependency Injection (DI)&lt;/strong&gt; is a fundamental software design pattern that improves code quality by promoting &lt;em&gt;l&lt;/em&gt; &lt;strong&gt;&lt;em&gt;oose coupling&lt;/em&gt;, &lt;em&gt;enhanced testability&lt;/em&gt;,&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;cleaner architecture&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whether you’re building a small app or a large-scale enterprise system, understanding DI is key to writing maintainable and scalable code.&lt;/p&gt;

&lt;p&gt;Every time your class creates its own dependencies, it quietly takes on responsibilities it shouldn’t have.&lt;br&gt;&lt;br&gt;
That leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rigid, tightly coupled code&lt;/li&gt;
&lt;li&gt;Hard-to-test components&lt;/li&gt;
&lt;li&gt;Painful refactors&lt;/li&gt;
&lt;li&gt;Code that breaks when business logic changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DI solves all of these problems by enforcing one simple principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Classes should not create their dependencies — they should receive them.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This unlocks flexibility, composability, and maintainability in real-world applications.&lt;/p&gt;

&lt;p&gt;This article breaks down DI basics, explaining &lt;strong&gt;what it is&lt;/strong&gt; , &lt;strong&gt;why it’s important&lt;/strong&gt; , and &lt;strong&gt;how it changes the way dependencies are managed in software&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Is Dependency Injection?
&lt;/h3&gt;

&lt;p&gt;Let us begin with a simple definition:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency Injection is a technique where a class receives the objects it depends on — its &lt;em&gt;dependencies&lt;/em&gt; — instead of creating them internally.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Technical Definition
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Dependency Injection (DI)&lt;/strong&gt; is a software design pattern in which an object’s dependencies are supplied by an external entity rather than being constructed within the object itself.&lt;/p&gt;

&lt;p&gt;This pattern is a practical implementation of the &lt;strong&gt;Inversion of Control (IoC)&lt;/strong&gt; principle, where the responsibility of creating and managing dependencies is delegated to an external system (such as a framework, a container, or even custom code).&lt;/p&gt;
&lt;h4&gt;
  
  
  A Simple Way to Think About It
&lt;/h4&gt;

&lt;p&gt;Instead of a class saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;“I’ll create everything I need.”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;“Give me what I need to do my job.”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This inversion of responsibility provides major advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better maintainability&lt;/li&gt;
&lt;li&gt;Easier testing&lt;/li&gt;
&lt;li&gt;More flexible and modular systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the important part:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don’t need a framework to understand DI.&lt;/strong&gt; The concept exists independently of Angular, Spring, .NET, or any other DI container.&lt;/p&gt;
&lt;h3&gt;
  
  
  Inversion of Control (IoC): The Principle Behind DI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Inversion of Control (IoC)&lt;/strong&gt; is the broader principle that underpins DI.&lt;br&gt;&lt;br&gt;
It means that the control of object creation and lifecycle is &lt;em&gt;inverted -&lt;/em&gt; moved away from the dependent class to an external entity (like an injector or framework).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Without IoC:&lt;/strong&gt; The class controls its dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With IoC:&lt;/strong&gt; An external system (like Angular, Spring, or your own injector) controls them.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Think of&lt;/em&gt; &lt;strong&gt;&lt;em&gt;IoC&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;as the philosophy, and&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Dependency Injection&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;as one practical way to implement it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;DI isn’t the only way to achieve IoC. Other patterns include&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Service Locator, Factory patterns,&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;and&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Event-driven&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;architectures&lt;/em&gt; &lt;strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;Each has different trade-offs in terms of explicitness and discoverability. DI is favored because it makes dependencies explicit and easier to track.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Practical Analogy
&lt;/h3&gt;

&lt;p&gt;Think of dependencies as services in our daily life.&lt;/p&gt;

&lt;p&gt;We need a ride to work. We &lt;em&gt;could&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buy a car&lt;/li&gt;
&lt;li&gt;Handle fuel&lt;/li&gt;
&lt;li&gt;Pay for maintenance&lt;/li&gt;
&lt;li&gt;Replace parts&lt;/li&gt;
&lt;li&gt;Take responsibility for everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or…&lt;/p&gt;

&lt;p&gt;We can &lt;strong&gt;use Uber&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
We &lt;strong&gt;&lt;em&gt;delegate the responsibility&lt;/em&gt;&lt;/strong&gt; to someone else, and we just &lt;strong&gt;use&lt;/strong&gt; the service.&lt;/p&gt;

&lt;p&gt;DI works the same way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Instead of creating and managing dependencies, your class simply uses what’s provided to it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So that’s how DI improves scalability and flexibility in your projects.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Problem DI Solves: Tight Coupling
&lt;/h3&gt;

&lt;p&gt;In traditional object-oriented code, classes often create their own dependencies:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Logger Class
class Logger {
  log(message: string): void {
    console.log('Log:', message);
  }
}

// User Class (tightly coupled)
class User {
  private logger = new Logger(); // hard-coded dependency

  createUser(name: string): void {
    this.logger.log(`User ${name} created.`);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Consequence:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, User is &lt;strong&gt;tightly coupled&lt;/strong&gt; to Logger.&lt;br&gt;&lt;br&gt;
 If you want to replace Logger with a new one (DatabaseLogger or FileLogger), you must edit User’s source code.&lt;/p&gt;

&lt;p&gt;This violates a core software design rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;“High-level modules should not depend on low-level modules; both should depend on abstractions.” — &lt;/em&gt;&lt;/strong&gt; &lt;em&gt;Dependency Inversion Principle, the “D” in SOLID&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Important Clarification:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not all uses of the new keyword indicates bad design. &lt;strong&gt;Value objects, DTOs, and data structures&lt;/strong&gt; should be created directly.&lt;br&gt;&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const date = new Date();
const email = new EmailAddress(userInput);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DI applies to &lt;strong&gt;behavioral dependencies&lt;/strong&gt; such as services, repositories, and components that have side effects or external interactions like logging, database access, or API calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Dependency Injection
&lt;/h3&gt;

&lt;p&gt;With DI, dependencies are &lt;strong&gt;supplied (injected)&lt;/strong&gt; from the outside — typically through the constructor:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Logger (Dependency)
class Logger {
  log(message: string): void {
    console.log('Log:', message);
  }
}

// User (Dependent)
class User {
  constructor(private logger: Logger) {} // Constructor Injection (Recommended)

  createUser(name: string): void {
    this.logger.log(`User ${name} created.`);
  }
}

// Usage
const logger = new Logger();
const user = new User(logger);
user.createUser('Alice');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now User doesn’t know &lt;em&gt;how&lt;/em&gt; logging is done — it only knows &lt;em&gt;that&lt;/em&gt; something logs.&lt;/p&gt;

&lt;p&gt;So Now,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User &lt;strong&gt;doesn’t create&lt;/strong&gt;  Logger&lt;/li&gt;
&lt;li&gt;Any logger can be provided&lt;/li&gt;
&lt;li&gt;Testing is trivial&lt;/li&gt;
&lt;li&gt;Different implementations(FileLogger, MockLogger, or DatabaseLogger without touching User) can be plugged in without touching User&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is DI in its simplest form — clean, elegant, and extensible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A&lt;/em&gt; &lt;strong&gt;&lt;em&gt;dependency&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is any object another class relies on.&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Dependency Injection&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is the technique of&lt;/em&gt; &lt;strong&gt;&lt;em&gt;supplying&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;those dependencies, instead of&lt;/em&gt; &lt;strong&gt;&lt;em&gt;creating&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;them inside the class.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using Abstractions with Interfaces
&lt;/h3&gt;

&lt;p&gt;To fully leverage the Dependency Inversion Principle, it’s best to depend on &lt;strong&gt;abstractions&lt;/strong&gt; instead of &lt;strong&gt;concrete classes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ILogger interface as abstraction
interface ILogger {
  log(message: string): void;
}

// ConsoleLogger implements ILogger
class ConsoleLogger implements ILogger {
  log(message: string): void {
    console.log('Log:', message);
  }
}

// User depends on the ILogger abstraction
class User {
  constructor(private logger: ILogger) {}

  createUser(name: string): void {
    this.logger.log(`User ${name} created.`);
  }
}

// Usage
const logger = new ConsoleLogger();
const user = new User(logger);
user.createUser('Alice');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now User depends only on the &lt;strong&gt;contract&lt;/strong&gt; , never the concrete class.&lt;/p&gt;

&lt;p&gt;Using an interface like ILogger decouples User from specific implementations, enabling seamless swapping and better scalability.&lt;/p&gt;

&lt;p&gt;This is the essence of the &lt;strong&gt;Dependency Inversion Principle&lt;/strong&gt; : both high-level and low-level modules depend on abstractions, not on each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Preview: Types of Dependency Injection
&lt;/h3&gt;

&lt;p&gt;There are several ways to inject dependencies into a class:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Constructor Injection&lt;/strong&gt;  — Dependencies passed through the constructor (most common and recommended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setter Injection&lt;/strong&gt;  — Dependencies set via setter methods (for optional dependencies)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method Injection&lt;/strong&gt;  — Dependencies passed as method parameters (for transient needs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface-Based Injection&lt;/strong&gt;  — Injection enforced through interface contracts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each pattern serves different use cases, and we’ll explore all of them in detail in &lt;strong&gt;Part 2&lt;/strong&gt; of this series.&lt;/p&gt;

&lt;p&gt;For now, just know that &lt;strong&gt;Constructor based Injection is the recommended default&lt;/strong&gt; because it makes dependencies explicit and ensures objects are fully initialized.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Dependency Injection
&lt;/h3&gt;

&lt;p&gt;DI is particularly valuable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application has &lt;strong&gt;complex dependencies&lt;/strong&gt; across components or services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unit testing&lt;/strong&gt; and &lt;strong&gt;mocking&lt;/strong&gt; are priorities&lt;/li&gt;
&lt;li&gt;You want a &lt;strong&gt;modular, scalable architecture&lt;/strong&gt; that supports future change&lt;/li&gt;
&lt;li&gt;You expect &lt;strong&gt;multiple implementations&lt;/strong&gt; of the same abstraction&lt;/li&gt;
&lt;li&gt;Your team is working on a medium-to-large codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When NOT to Use Dependency Injection
&lt;/h3&gt;

&lt;p&gt;DI isn’t always the right choice. Consider simpler alternatives when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building small scripts or utilities (&amp;lt; 3 dependencies)&lt;/li&gt;
&lt;li&gt;Writing pure functions with no external dependencies&lt;/li&gt;
&lt;li&gt;Working in performance-critical paths where indirection matters&lt;/li&gt;
&lt;li&gt;Prototyping or writing throwaway code&lt;/li&gt;
&lt;li&gt;The complexity of DI setup outweighs the benefits&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Remember:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;Every pattern has trade-offs. DI adds indirection and configuration overhead. For simple scenarios, direct instantiation is perfectly fine.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Mental Model: Thinking in Dependencies
&lt;/h3&gt;

&lt;p&gt;The key mindset shift with DI is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before DI thinking:&lt;/strong&gt; “What do I need? Let me go create it.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After DI thinking:&lt;/strong&gt; “What do I need? Let me declare it, and someone else will provide it.”&lt;/p&gt;

&lt;p&gt;This shift enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;  — Swap implementations without code changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt;  — Replace real dependencies with mocks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;  — Changes localized to one place&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;  — New features don’t break existing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you internalize this mental model, writing loosely coupled code becomes natural.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Impact: Why This Matters
&lt;/h3&gt;

&lt;p&gt;Let me share a quick example from a fintech project point of view.&lt;/p&gt;

&lt;p&gt;We needed to support multiple payment gateways (Stripe, PayPal, internal processing). Initially, payment logic was scattered across 200+ transaction-handling classes, each directly instantiating the payment gateway they needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without DI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When business requirements changed and we needed to switch providers, it would have required modifying every single class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DI solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We created an IPaymentGateway interface and used Constructor Injection. Suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching providers became a single configuration change&lt;/li&gt;
&lt;li&gt;Testing payment logic used mock gateways (no real transactions)&lt;/li&gt;
&lt;li&gt;Adding new providers didn’t touch existing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This is the power of DI:&lt;/strong&gt; change becomes configuration, not code modification.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Coming in Part 2
&lt;/h3&gt;

&lt;p&gt;Now that you understand the core philosophy of Dependency Injection, you’re ready to see it in action.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 2: Dependency Injection in Practice&lt;/strong&gt; , we’ll explore:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The 4 injection patterns&lt;/strong&gt; in detail (Constructor, Setter, Method, Interface-based)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency lifecycles&lt;/strong&gt; : Singleton vs Transient vs Scoped&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing strategies&lt;/strong&gt; with real DI examples and mocks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How frameworks automate DI&lt;/strong&gt; (Angular, Spring, .NET Core)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common pitfalls&lt;/strong&gt; that trip up even experienced developers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-world case studies&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part 2 will be published next week — follow me so you don’t miss it!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Dependency Injection isn’t just a pattern, it’s a &lt;strong&gt;mindset&lt;/strong&gt; that leads to cleaner, more robust code.&lt;/p&gt;

&lt;p&gt;Understanding the essentials of DI is the first step toward mastering frameworks like Angular, where these principles are automated and extended.&lt;/p&gt;

&lt;p&gt;These concepts form the mental model you need to understand how modern frameworks work.&lt;/p&gt;

&lt;p&gt;But we’re just getting started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s Talk DI!
&lt;/h3&gt;

&lt;p&gt;Dependency Injection isn’t just some fancy design pattern — it’s the backbone of &lt;strong&gt;clean, flexible, and testable code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now I’m curious…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What DI pattern do you usually rely on in your projects?&lt;/li&gt;
&lt;li&gt;Have you ever struggled with &lt;strong&gt;tight coupling&lt;/strong&gt; , and how did DI help you untangle it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop your thoughts in the comments — I’d love to hear how other developers approach this!&lt;/p&gt;

&lt;p&gt;And if you found these examples helpful, &lt;strong&gt;give it a clap&lt;/strong&gt; and share it with your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source Code &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;If you’d like to dive deeper or try the examples yourself, all source code including those discussed in this series is available on my GitHub repository: &lt;a href="https://github.com/Manishh09/angular-di-articles/tree/main/projects/01-di-fundamentals" rel="noopener noreferrer"&gt;&lt;strong&gt;Angular DI Series&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t forget to give this&lt;/strong&gt; &lt;a href="https://github.com/Manishh09/angular-di-articles" rel="noopener noreferrer"&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;a star ⭐&lt;/strong&gt;  — not only does it help you stay updated when code changes, but it also shows appreciation for the content and motivates continued development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect with Me!
&lt;/h3&gt;

&lt;p&gt;Hi there! I’m &lt;strong&gt;Manish&lt;/strong&gt; , a Senior Engineer, passionate about building robust web applications and exploring the ever-evolving world of tech. I believe in learning and growing together.&lt;/p&gt;

&lt;p&gt;If this article sparked your interest in modern Angular, software architecture, or just a tech discussion, I’d love to connect with you!&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Let’s connect on&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/manishboge/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;for more tech insights and discussions.&lt;br&gt;&lt;br&gt;
 📧 &lt;strong&gt;Follow me on&lt;/strong&gt; &lt;a href="https://medium.com/@manishboge" rel="noopener noreferrer"&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/a&gt;to catch Part 2 when it drops next week!&lt;br&gt;&lt;br&gt;
 💻 *&lt;em&gt;Explore my work on *&lt;/em&gt; &lt;a href="https://github.com/Manishh09" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dependencyinjection</category>
      <category>angular</category>
      <category>architecture</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Mastering Modern Angular: Functional Route Guards &amp; Interceptors Explained</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Sat, 26 Jul 2025 13:09:17 +0000</pubDate>
      <link>https://dev.to/manish-boge/mastering-modern-angular-functional-route-guards-interceptors-explained-26ld</link>
      <guid>https://dev.to/manish-boge/mastering-modern-angular-functional-route-guards-interceptors-explained-26ld</guid>
      <description>&lt;p&gt;Unlock the Power of Functional Route Guards &amp;amp; Interceptors&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faydxuyqw6bodyvmaeh8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faydxuyqw6bodyvmaeh8m.png" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Angular has evolved rapidly, continuously striving for cleaner, more composable, and less verbose code. Among the most developer-friendly advancements in recent versions is the shift from traditional, class-based APIs to more concise &lt;strong&gt;Functional APIs&lt;/strong&gt; , especially for &lt;strong&gt;Route Guards&lt;/strong&gt; and &lt;strong&gt;HTTP interceptors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article we will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Route Guards and Interceptors Matter?&lt;/li&gt;
&lt;li&gt;Traditional Angular: Class-Based APIs&lt;/li&gt;
&lt;li&gt;Modern Angular: Functional APIs&lt;/li&gt;
&lt;li&gt;Comparison Table: Traditional vs. Functional&lt;/li&gt;
&lt;li&gt;When to Choose: Class-Based vs Functional ?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; Functional route guards and interceptors were introduced in Angular v15+. Ensure your project is on v15 or later to fully benefit. While Angular is now at v20.x.x, we’ll focus on these pivotal modern patterns introduced from v15+, which are important for Modern Angular Development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Route Guards and Interceptors Matter ?
&lt;/h3&gt;

&lt;p&gt;Let’s understand with real-world examples.&lt;/p&gt;

&lt;h4&gt;
  
  
  Route Guards
&lt;/h4&gt;

&lt;p&gt;These are your application’s security checkpoints. They protect routes based on &lt;strong&gt;authentication&lt;/strong&gt; , &lt;strong&gt;roles&lt;/strong&gt; , or &lt;strong&gt;permissions&lt;/strong&gt; &lt;em&gt;before&lt;/em&gt; a route even activates.&lt;/p&gt;

&lt;p&gt;Use Case:&lt;/p&gt;

&lt;p&gt;Let’s imagine a personal &lt;strong&gt;online banking application&lt;/strong&gt; or a &lt;strong&gt;social media profile&lt;/strong&gt;. When a user tries to access a protected page like /my-account or /settings, a route guard immediately checks: "Is this user currently logged in?" If not, it instantly redirects them to the /login page.&lt;/p&gt;

&lt;h4&gt;
  
  
  HTTP Interceptors
&lt;/h4&gt;

&lt;p&gt;Think of these as a centralized control panel for all your HTTP traffic. They globally transform HTTP requests and responses — perfect for adding headers, handling network errors, logging, or showing loading indicators.&lt;/p&gt;

&lt;p&gt;Use Case:&lt;/p&gt;

&lt;p&gt;Let us imagine an Angular application needs to communicate with a &lt;strong&gt;secured backend API&lt;/strong&gt; , for instance, to fetch a user’s profile details or submit a form. Every single request to this API requires an &lt;strong&gt;authentication token&lt;/strong&gt; to prove the user’s identity. Instead of manually adding Authorization: Bearer  to &lt;em&gt;every single HTTP call&lt;/em&gt; in various services throughout your application, an HTTP Interceptor can do this &lt;strong&gt;automatically for you&lt;/strong&gt;. It's like having a dedicated assistant who makes sure your secret key is attached to every letter you send out, without you ever having to remember it. This keeps your service code clean and focused on business logic, not authentication details.&lt;/p&gt;

&lt;p&gt;Now that we understand the role of Route Guards and Interceptors , let us now see the &lt;strong&gt;Traditional(class-based)&lt;/strong&gt; and &lt;strong&gt;Functional&lt;/strong&gt; way of creating each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Angular
&lt;/h3&gt;

&lt;p&gt;Before the functional revolution, Angular developers relied on a more verbose, class-oriented approach for route guards and interceptors. While these patterns were standard for years, &lt;strong&gt;the class-based guard interfaces are now officially deprecated, and functional guards, interceptors are the recommended modern alternative.&lt;/strong&gt; Understanding this foundation helps appreciate the simplicity and power of the new functional paradigms.&lt;/p&gt;

&lt;h4&gt;
  
  
  Class-Based Route Guard
&lt;/h4&gt;

&lt;p&gt;Lets see the code snippet for &lt;strong&gt;canActivate&lt;/strong&gt; guard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | UrlTree {
    if (this.authService.isLoggedIn()) {
      return true; // User is logged in, allow access
    }
    // If User is not logged in, redirect to login page
    return this.router.createUrlTree(['/login']);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage in Router Config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  path: 'dashboard',
  component: DashboardComponent,
  canActivate: [AuthGuard] // Reference the class
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Class Based HTTP Interceptor
&lt;/h4&gt;

&lt;p&gt;Let us assume we have an AuthInterceptor that adds an authorization token to outgoing http requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(req: HttpRequest&amp;lt;any&amp;gt;, next: HttpHandler): Observable&amp;lt;HttpEvent&amp;lt;any&amp;gt;&amp;gt; {
    const token = this.authService.getToken();
    // clone request due to immutability
    const authReq = req.clone({
      setHeaders: { Authorization: `Bearer ${token}` } // Add token to headers
    });
    return next.handle(authReq);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage in Standalone Angular Application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.config.ts
providers: [
    // This is crucial for class-based interceptors in standalone apps
    provideHttpClient(withInterceptorsFromDi()),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor, // Register the class
      multi: true,
    },
  ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage in Module based Angular application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@NgModule({
  providers: [
    { 
      provide: HTTP_INTERCEPTORS, 
      useClass: AuthInterceptor, // Register the class 
      multi: true 
    } 
  ]
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Modern Angular
&lt;/h3&gt;

&lt;p&gt;Angular v15+ ushered in a new era of conciseness and expressiveness. Discover how pure functions now simplify the critical tasks of route protection and HTTP request manipulation, making your code cleaner and more efficient.&lt;/p&gt;

&lt;p&gt;Since Angular v15+ , we can leverage the Functional APIs such as Functional Route Guards, Interceptors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Functional Route Guards
&lt;/h4&gt;

&lt;p&gt;Here’s a roleGuard that checks for a specific user role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// role.guard.ts
import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './auth.service';

export const roleGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) =&amp;gt; {
  const authService = inject(AuthService); // Inject services directly (New API)
  const router = inject(Router);
  const requiredRole = route.data['requiredRole']; // Access route data

  if (authService.isLoggedIn() &amp;amp;&amp;amp; authService.getUserRole() === requiredRole) {
    return true; // User has required role, allow access
  } else {
    // User doesn't have the role or isn't logged in, redirect
    return router.createUrlTree(['/login']);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; Here we have used inject() Function API instead of a &lt;strong&gt;constructor&lt;/strong&gt; for dependency injection. The inject() function was introduced in &lt;strong&gt;Angular v14.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Usage in Module-Based Router Config ( app-routing.module.ts ) or Standalone Application Router Config( app.route.ts ):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  path: 'admin',
  component: AdminComponent,
  canActivate: [roleGuard], // Reference the function directly
  data: { requiredRole: 'admin' } // Pass required role via route data
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; There are other route guard and resolver functions like canActivateChildFn, canDeactivateFn, canLoadFn, canMatchFn, and ResolveFn. I will cover a deep dive of these in another article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Functional HTTP Interceptor
&lt;/h4&gt;

&lt;p&gt;This functional authInterceptor does the same job as its class-based counterpart, but with less code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// auth-fn.interceptor.ts
import { HttpEvent, HttpHandlerFn, HttpInterceptorFn, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';

export const authFnInterceptor: HttpInterceptorFn = (req: HttpRequest&amp;lt;unknown&amp;gt;, next: HttpHandlerFn): Observable&amp;lt;HttpEvent&amp;lt;unknown&amp;gt;&amp;gt; =&amp;gt; {
  const authService = inject(AuthService); // Inject services directly
  const token = authService.getToken();

  // Clone the request to add the authorization header
  const authReq = req.clone({
    setHeaders: { Authorization: `Bearer ${token}` }
  });

  return next(authReq); // Pass the modified request to the next handler
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage in Standalone Angular Application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.config.ts
import { authFnInterceptor } from './interceptors/functional/auth-fn.interceptor';
import { ApplicationConfig} from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';export const appConfig: ApplicationConfig = {

providers: [
    provideHttpClient(withInterceptors([authFnInterceptor])),

  ]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage in Module based Angular Application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.module.ts
import { authFnInterceptor } from './interceptors/functional/auth-fn.interceptor';
import { provideHttpClient, withInterceptors } from "@angular/common/http";

@NgModule({
  providers: [
    provideHttpClient(withInterceptors([authFnInterceptor]))
  ]
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With functional interceptors, you can chain these small, focused functions in withInterceptors([]), making your HTTP layer highly modular, readable, and maintainable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; There are certain use cases of interceptors like to catch network errors like 401, 500 and to handle loading state while API requests are ongoing. I will cover a deep dive of these like handling multiple interceptors in Angular with another article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Comparison Table: Traditional vs. Functional
&lt;/h3&gt;

&lt;p&gt;This comparison table outlines the key differences between the two styles across critical aspects such as boilerplate, dependency injection, readability, and testability. While class-based APIs have been the cornerstone of Angular since its early versions, function-based APIs bring a more concise and expressive syntax that aligns with modern frontend development trends&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd00td2fmqhmx9w50x4fv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd00td2fmqhmx9w50x4fv.png" alt="FunctionalAPIsInAngular" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Choose: Functional vs. Class-Based ?
&lt;/h3&gt;

&lt;p&gt;Given the significant benefits of functional APIs we’ve already explored, and their future-forward nature, the decision in modern Angular development is largely straightforward.&lt;/p&gt;

&lt;p&gt;Crucially, &lt;strong&gt;class-based guard interfaces (&lt;/strong&gt;&lt;strong&gt;CanActivate, **&lt;/strong&gt; CanDeactivate, etc.) &lt;strong&gt;were deprecated in&lt;/strong&gt; Angular v16. &lt;strong&gt;While they still function for backward compatibility, new development should strongly favor their functional counterparts. Similarly, for HTTP interceptors,&lt;/strong&gt; functional HTTP interceptors** are now the recommended modern approach to handle HTTP logic for modern Angular development, effectively superseding the class-based approach for new code.&lt;/p&gt;

&lt;p&gt;For any new development in Angular v15+, the choice is clear: always default to functional APIs for Route Guards and HTTP Interceptors&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Functional is the Way Forward:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less Boilerplate:&lt;/strong&gt; Write more logic, less framework code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced DX &amp;amp; Readability:&lt;/strong&gt; Concise, pure functions are easier to understand and maintain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimal Performance:&lt;/strong&gt; Better tree-shaking for smaller bundle sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Testing:&lt;/strong&gt; Isolated logic makes unit testing a breeze.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Consider Class-Based:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legacy Codebases:&lt;/strong&gt; Primarily for maintaining existing class-based implementations or when a full refactor isn’t immediately feasible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specific Compatibility:&lt;/strong&gt; Extremely rare cases involving older third-party libraries that explicitly require class-based patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, leverage functional APIs for a modern, efficient, and developer-friendly Angular experience. Class-based remains primarily for backward compatibility.&lt;/p&gt;

&lt;h4&gt;
  
  
  How Functional APIs Facilitate Tree-Shaking ?
&lt;/h4&gt;

&lt;p&gt;Functional guards and interceptors are &lt;strong&gt;pure functions&lt;/strong&gt; with &lt;strong&gt;explicit dependencies via&lt;/strong&gt; &lt;strong&gt;inject()&lt;/strong&gt;. This enables bundlers (like Webpack or Rollup) to perform more aggressive and precise dead code elimination. Unused logic (and their associated dependencies) is more easily identified and removed from production builds, leading to smaller bundle sizes and faster load times—a direct win for user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Functional APIs for route guards and interceptors mark a major leap in Angular’s Developer Experience (DX). By shedding verbose classes and embracing modern TypeScript patterns, Angular continues to evolve toward a cleaner, faster, and more testable future. This isn’t just a stylistic change; it’s a fundamental improvement that empowers you to build more robust, maintainable, and performant applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ready to modernize your Angular apps? Refactor your route guards and HTTP interceptors into functions — your future self (and your teammates) will thank you!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Source Code &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;Want to dive deeper or run these examples yourself?&lt;/p&gt;

&lt;p&gt;You can find the &lt;strong&gt;complete source code&lt;/strong&gt; , including all the examples and demos discussed in this article, on our GitHub repository: &lt;a href="https://github.com/Manishh09/angular17-insights-series" rel="noopener noreferrer"&gt;&lt;strong&gt;Angular 17 Series GitHub Repository&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Love what you see?&lt;/strong&gt; Don’t forget to give the repository a star ⭐ on &lt;a href="https://github.com/Manishh09/angular17-insights-series" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! It helps us know you appreciate the content and encourages more updates and examples. The &lt;a href="https://github.com/Manishh09/angular17-insights-series/blob/master/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; file within the repo provides all the guidance you'll need to get the project up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continue Your Modern Angular Journey
&lt;/h3&gt;

&lt;p&gt;This article is part of our &lt;strong&gt;Modern Angular Development Series&lt;/strong&gt;. Explore other articles to continue your learning journey:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://medium.com/@manishboge/modern-angular-development-the-angular-17-revolution-5fa1023dfe94" rel="noopener noreferrer"&gt;&lt;strong&gt;The Angular 17 Revolution&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@manishboge/from-ngmodules-to-standalone-apis-unlocking-the-future-of-angular-c0c8f5953efd" rel="noopener noreferrer"&gt;&lt;strong&gt;Standalone APIs&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh1hbnnjqj8dmpft03yv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh1hbnnjqj8dmpft03yv.png" width="707" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ystuowwajqxt1hbglen.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ystuowwajqxt1hbglen.gif" width="146" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect with Me!
&lt;/h3&gt;

&lt;p&gt;Hi there! I’m &lt;em&gt;Manish&lt;/em&gt;, a Senior Engineer, passionate about building robust web applications and exploring the ever-evolving world of tech. I believe in learning and growing together.&lt;/p&gt;

&lt;p&gt;If this article sparked your interest in modern Angular, software architecture, or just a tech discussion, I’d love to connect with you!&lt;/p&gt;

&lt;p&gt;🔗Follow me on &lt;a href="https://www.linkedin.com/in/manishboge/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;for more discussions or contributing or just to chat tech.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you for reading — and happy&lt;/strong&gt; 🅰️ &lt;strong&gt;ngularing!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>httpinterceptors</category>
      <category>angular</category>
      <category>routeguards</category>
    </item>
    <item>
      <title>Modern Angular Development: The Angular 17 Revolution</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Wed, 23 Jul 2025 13:43:36 +0000</pubDate>
      <link>https://dev.to/manish-boge/modern-angular-development-the-angular-17-revolution-nca</link>
      <guid>https://dev.to/manish-boge/modern-angular-development-the-angular-17-revolution-nca</guid>
      <description>&lt;h3&gt;
  
  
  Before We Begin
&lt;/h3&gt;

&lt;p&gt;Welcome to my very first article on Dev.to!&lt;/p&gt;

&lt;p&gt;As an experienced Angular developer, I’ve seen the framework evolve through many versions — but Angular 17 marks a true turning point. This release isn’t just a version bump; it redefines how we build, optimize, and think about Angular applications.&lt;/p&gt;

&lt;p&gt;With powerful features like the new built-in &lt;strong&gt;control flow&lt;/strong&gt; syntax &lt;strong&gt;@if&lt;/strong&gt;, &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/switch"&gt;@switch&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;deferrable views&lt;/strong&gt; , and a blazing-fast &lt;strong&gt;Vite + esbuild&lt;/strong&gt; build system, Angular 17 modernizes the development experience while delivering major performance gains.&lt;/p&gt;

&lt;p&gt;Whether you’re a seasoned Angular veteran or just starting out, this article will walk you through the key highlights of Angular 17 — how they simplify, accelerate, and transform the way we build web apps.&lt;/p&gt;

&lt;p&gt;And yes, Angular 20 is already out — but before jumping ahead, let’s take a moment to appreciate the solid foundation Angular 17 laid. It’s this release that sparked the momentum behind today’s Angular renaissance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s dive in!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔥The Angular Renaissance
&lt;/h3&gt;

&lt;p&gt;Angular 17 represents more than an incremental update — it fundamentally redefines how developers build Angular applications. The release focuses on three core areas: performance optimization, development simplicity, and enhanced developer experience.&lt;/p&gt;

&lt;p&gt;Performance improvements are remarkable, with up to 90% faster execution compared to previous versions. Build times have been reduced by up to 87% for hybrid rendering and 67% for client-side rendering, transforming the entire development workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✨Major Features Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Built-in Control Flow Syntax&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New @if, &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt;, and &lt;a class="mentioned-user" href="https://dev.to/switch"&gt;@switch&lt;/a&gt; syntax replacing verbose structural directives&lt;/li&gt;
&lt;li&gt;Cleaner, more readable templates with JavaScript-like syntax&lt;/li&gt;
&lt;li&gt;Better type checking and reduced bundle size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deferrable Views&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a class="mentioned-user" href="https://dev.to/defer"&gt;@defer&lt;/a&gt; blocks for intelligent component lazy loading&lt;/li&gt;
&lt;li&gt;Multiple trigger options (viewport, interaction, hover, timer)&lt;/li&gt;
&lt;li&gt;Declarative loading, error, and placeholder states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enhanced Server-Side Rendering&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplified SSR setup with @angular/ssr package&lt;/li&gt;
&lt;li&gt;Seamless hydration for better performance&lt;/li&gt;
&lt;li&gt;Improved SEO capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Developer Productivity Tools&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vite + esbuild build system for faster development&lt;/li&gt;
&lt;li&gt;New lifecycle hooks for DOM interaction&lt;/li&gt;
&lt;li&gt;Enhanced debugging with improved DevTools&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🎛️Streamlined Control Flow Syntax
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ❌The Challenge with Traditional Directives
&lt;/h4&gt;

&lt;p&gt;Previous Angular versions required verbose syntax for common template operations.&lt;/p&gt;

&lt;p&gt;The *ngIf directive necessitated external templates for else conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div *ngIf="loggedIn; else anonymousUser"&amp;gt;
  &amp;lt;p&amp;gt;The user is logged in&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;ng-template #anonymousUser&amp;gt;
  &amp;lt;p&amp;gt;The user is not logged in&amp;lt;/p&amp;gt;
&amp;lt;/ng-template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many developers reported having to constantly look up *ngFor and trackBy syntax, even after years of experience.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div *ngFor="let item of items; trackBy: trackByFn"&amp;gt;
 {{ item.name }}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;trackBy&lt;/code&gt; function in component.ts file would like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trackByFn(index: number, item: any) {
 return item.id; // or any unique identifier
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, *ngSwitch demanded complex nested syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div [ngSwitch]="accessLevel"&amp;gt; 
  &amp;lt;admin-dashboard *ngSwitchCase="'admin'"/&amp;gt;
  &amp;lt;moderator-dashboard *ngSwitchCase="'moderator'"/&amp;gt;
  &amp;lt;user-dashboard *ngSwitchDefault/&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Modern Block-Based Syntax
&lt;/h4&gt;

&lt;p&gt;Angular 17 introduces intuitive control flow that resembles JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@if (status === 'active') {
  &amp;lt;p&amp;gt;Active&amp;lt;/p&amp;gt;
} @else if (status === 'inactive') {
  &amp;lt;p&amp;gt;Inactive&amp;lt;/p&amp;gt;
} @else {
  &amp;lt;p&amp;gt;Pending&amp;lt;/p&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a class="mentioned-user" href="https://dev.to/switch"&gt;@switch&lt;/a&gt; syntax is equally straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@switch (accessLevel) {
  @case ('admin') {
    &amp;lt;admin-dashboard/&amp;gt;
  }
  @case ('moderator') {
    &amp;lt;moderator-dashboard/&amp;gt;
  }
  @default {
    &amp;lt;user-dashboard/&amp;gt;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; loop requires explicit tracking for optimal performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for (user of users; track user.id) {
  {{ user.name }}
} @empty {
  &amp;lt;p&amp;gt;No users available&amp;lt;/p&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎯Technical Benefits
&lt;/h3&gt;

&lt;p&gt;The new control flow delivers significant advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better Type Checking&lt;/strong&gt; : Improved type-narrowing in conditional branches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Bundle Size&lt;/strong&gt; : Up to 30KB reduction since control flow is compiler-built&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Performance&lt;/strong&gt; : Up to 90% faster execution in framework benchmarks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Availability&lt;/strong&gt; : No imports required, automatically available in all templates&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ⚡Deferrable Views: Intelligent Lazy Loading
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ❌Previous Lazy Loading Complexity
&lt;/h4&gt;

&lt;p&gt;Before Angular 17, component lazy loading required manual ViewContainerRef management:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ViewChild('viewContainer', { read: ViewContainerRef }) 
viewContainerRef!: ViewContainerRef;

async ngAfterViewInit() {
  const { LazyComponent } = await import('./lazy/lazy.component');
  this.viewContainerRef.createComponent(LazyComponent);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Declarative Lazy Loading with &lt;a class="mentioned-user" href="https://dev.to/defer"&gt;@defer&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;a class="mentioned-user" href="https://dev.to/defer"&gt;@defer&lt;/a&gt; block simplifies lazy loading to a single template declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@defer {
  &amp;lt;heavy-component /&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple trigger options provide precise control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@defer (on viewport; prefetch on hover) {
  &amp;lt;comments-section /&amp;gt;
} @loading (minimum 500ms) {
  &amp;lt;loading-spinner /&amp;gt;
} @placeholder {
  &amp;lt;p&amp;gt;Comments will appear here&amp;lt;/p&amp;gt;
} @error {
  &amp;lt;p&amp;gt;Failed to load comments&amp;lt;/p&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Available triggers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on idle: Loads when browser becomes idle (default)&lt;/li&gt;
&lt;li&gt;on viewport: Loads when element enters viewport&lt;/li&gt;
&lt;li&gt;on interaction: Loads after user interaction&lt;/li&gt;
&lt;li&gt;on hover: Loads on mouse hover&lt;/li&gt;
&lt;li&gt;on timer(duration): Loads after specified time&lt;/li&gt;
&lt;li&gt;when condition: Loads based on custom conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Note: The new control flow, defer features are in developer preview in Angular 17, will become stable in Angular v18.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  📡Signal APIs: Simplified Reactivity
&lt;/h3&gt;

&lt;p&gt;Angular 17 introduces Signal APIs for managing reactive state without heavy RxJS dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Creating reactive values
const count = signal(0);
const name = signal('Angular');

// Updating values
count.set(5);
count.update(current =&amp;gt; current + 1);

// Computed values
const displayText = computed(() =&amp;gt; `${name()} count: ${count()}`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key Signal APIs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;signal(): Creates reactive values&lt;/li&gt;
&lt;li&gt;set(): Sets new values&lt;/li&gt;
&lt;li&gt;update(): Updates based on current value&lt;/li&gt;
&lt;li&gt;computed(): Derives values from other signals&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Note: This article only introduces the basics of Angular Signals. I’ll be publishing a dedicated series of articles exploring these APIs in depth — with real-world examples, best practices, and migration guidelines.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🖥️Simplified Server-Side Rendering
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Streamlined SSR Setup
&lt;/h4&gt;

&lt;p&gt;Angular 17 replaces Angular Universal with the @angular/ssr package, dramatically simplifying server-side rendering setup.&lt;/p&gt;

&lt;p&gt;Create SSR-enabled projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new app-name --ssr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add SSR to existing projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @angular/ssr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Performance and SEO Benefits
&lt;/h4&gt;

&lt;p&gt;The new SSR implementation provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Up to 87% faster builds for hybrid rendering&lt;/li&gt;
&lt;li&gt;Intelligent hydration without DOM flickering&lt;/li&gt;
&lt;li&gt;Improved Core Web Vitals scores&lt;/li&gt;
&lt;li&gt;Better search engine indexing&lt;/li&gt;
&lt;li&gt;Automatic HTTP request caching&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🛠️Developer Productivity Enhancements
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🔄New Lifecycle Hooks
&lt;/h4&gt;

&lt;p&gt;Angular 17 adds browser-specific lifecycle hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor() {
  afterNextRender(() =&amp;gt; {
    this.chart = new MyChart(this.chartRef.nativeElement);
  }, {phase: AfterRenderPhase.Write});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;afterRender: Runs after each change detection cycle&lt;/li&gt;
&lt;li&gt;afterNextRender: Executes once after the next render cycle&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ⚡Vite + esbuild Build System
&lt;/h3&gt;

&lt;p&gt;The new build system delivers remarkable performance improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Over 67% faster builds in most applications&lt;/li&gt;
&lt;li&gt;Near-instant hot module replacement&lt;/li&gt;
&lt;li&gt;Component-level HMR&lt;/li&gt;
&lt;li&gt;Efficient dependency pre-bundling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enable for existing projects by updating angular.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "builder": "@angular-devkit/build-angular:browser-esbuild"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧩Standalone APIs
&lt;/h3&gt;

&lt;p&gt;Standalone components eliminate module dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'app-example',
  standalone: true,
  imports: [CommonModule, RouterModule],
  template: `&amp;lt;p&amp;gt;Standalone component&amp;lt;/p&amp;gt;`
})
export class ExampleComponent {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate standalone components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component componentName --standalone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ⬆️Migration Considerations
&lt;/h3&gt;

&lt;p&gt;When upgrading to Angular 17:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Control Flow&lt;/strong&gt; : New syntax is opt-in; existing *ngIf, *ngFor continue working&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defer Blocks&lt;/strong&gt; : Currently in developer preview, stable in Angular 18&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal APIs&lt;/strong&gt; : Core APIs are stable; input/output signals in preview&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build System&lt;/strong&gt; : Vite + esbuild enabled by default for new projects&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  📈Performance Impact
&lt;/h3&gt;

&lt;p&gt;Real-world measurements show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30% average speed improvement in Angular Material applications&lt;/li&gt;
&lt;li&gt;Significant reduction in bundle sizes&lt;/li&gt;
&lt;li&gt;Improved Core Web Vitals scores&lt;/li&gt;
&lt;li&gt;Faster development server startup times&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🎯Conclusion
&lt;/h3&gt;

&lt;p&gt;Angular 17 establishes a solid foundation for modern web development. The combination of intuitive syntax, intelligent lazy loading, simplified SSR, and powerful build tools creates a development experience that balances productivity with performance.&lt;/p&gt;

&lt;p&gt;The framework’s evolution toward declarative patterns, reduced boilerplate, and enhanced developer experience positions Angular as a leading choice for enterprise and consumer applications alike.&lt;/p&gt;

&lt;p&gt;These improvements represent Angular’s commitment to developer productivity while maintaining the robustness and scalability that enterprise applications require.&lt;/p&gt;

&lt;p&gt;I encourage you to explore these new features and discover their transformative potential firsthand. While features like Angular Standalone API, Angular Core Signals APIs ( set, update, computed, effect ), Functional Guards/Resolvers, Functional Interceptors, and Global Error Handling are production-ready, don’t miss trying the developer preview features like the new control flow and defer blocks — they showcase Angular’s innovative direction. Over the coming weeks, I’ll be publishing dedicated in-detailed articles every Week, exploring each feature which I mentioned above with step-by-step implementations and practical examples.&lt;/p&gt;




&lt;h4&gt;
  
  
  💻 Source Code &amp;amp; Resources
&lt;/h4&gt;

&lt;p&gt;🔗 &lt;strong&gt;Find the complete source code&lt;/strong&gt; for this Angular 17 series at the &lt;a href="https://github.com/Manishh09/angular17-insights-series" rel="noopener noreferrer"&gt;&lt;strong&gt;angular-17-series&lt;/strong&gt;&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;Don’t forget to ⭐ star the repo&lt;/strong&gt; to stay updated with new examples and improvements!&lt;/p&gt;

&lt;p&gt;All code examples, demos, and additional resources referenced in this article are available for hands-on practice and learning.&lt;/p&gt;

&lt;h4&gt;
  
  
  🚀 What’s Coming Next: Mastering Angular’s Standalone APIs
&lt;/h4&gt;

&lt;p&gt;In our next article, we’ll dive deep into Angular’s Standalone APIs — a modern way to build lightweight, modular applications without relying on NgModules. Learn how to create standalone components, directives, and pipes, and see how they can streamline your architecture and boost performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Stay tuned — the future of Angular is leaner, cleaner and powerful!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Thank you — and happy Angularing!&lt;/strong&gt; 🅰️&lt;/p&gt;




&lt;h3&gt;
  
  
  🤝 Let's Connect
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Follow me on &lt;a href="https://dev.to/manish-boge"&gt;Dev.to&lt;/a&gt; for deep dives, tutorials, and hands-on Angular content.&lt;/li&gt;
&lt;li&gt;Read more of my technical articles on &lt;a href="https://medium.com/@manishboge" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;, where I cover Angular, TypeScript, and modern web development.&lt;/li&gt;
&lt;li&gt;Follow or Connect with me on &lt;a href="https://www.linkedin.com/in/manishboge" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; to stay updated on my latest Angular posts or just to chat tech .&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  💬 Got any Questions or Feedback?
&lt;/h3&gt;

&lt;p&gt;I’d love to hear from you! Drop a comment below or reach out — let’s keep the conversation going.&lt;/p&gt;

</description>
      <category>angular17</category>
      <category>modernangular</category>
      <category>newfeatures</category>
      <category>wedev</category>
    </item>
    <item>
      <title>Catching Those Leaks: A Deep Dive into Angular RxJS Subscription Management Strategies</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Sat, 12 Jul 2025 17:06:44 +0000</pubDate>
      <link>https://dev.to/manish-boge/catching-those-leaks-a-deep-dive-into-angular-rxjs-subscription-management-strategies-92o</link>
      <guid>https://dev.to/manish-boge/catching-those-leaks-a-deep-dive-into-angular-rxjs-subscription-management-strategies-92o</guid>
      <description>&lt;p&gt;From Manual Cleanup to Modern Auto-Unsubscribe&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniw7o01zs4t2tavoh120.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniw7o01zs4t2tavoh120.png" alt="Cover Page" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an Angular developer, you wield the mighty power of RxJS Observables — a truly transformative tool for handling asynchronous data. But with great power comes great responsibility! Unmanaged subscriptions are notorious for causing subtle, insidious memory leaks and performance bottlenecks in your applications.&lt;/p&gt;

&lt;p&gt;Imagine a component making an API call, getting destroyed, but its subscription is still alive, trying to update a part of the DOM that no longer exists. Not pretty, right?&lt;/p&gt;

&lt;p&gt;Fear not! Today, we’ll dive deep into different strategies to gracefully manage your RxJS subscriptions, ensuring your Angular apps remain lean, fast, and robust. From the classic manual approach to the modern, elegant solutions, let’s explore how to keep those memory leaks at bay!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Foundation
&lt;/h3&gt;

&lt;p&gt;For all our examples, let’s assume we have a simple UserService and ProductServicethat provides Observables for fetching data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Our Service Code Snippets
&lt;/h4&gt;

&lt;p&gt;First, let’s define our two distinct services, one for users and one for products, using Angular’s HttpClient.&lt;/p&gt;

&lt;p&gt;Lets assume we have Models for user and product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/models/user.ts
export interface User {
  id: number;
  name: string;
  username: string;
  email: string;
  // ... other user properties
}

// src/app/models/products.ts
export interface Product {
  id: number;
  title: string;
  price: number;
  description: string;
  category: string;
  image: string;
  rating: {
    rate: number;
    count: number;
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User Service:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/services/user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user'; // Ensure this path is correct
@Injectable({
  providedIn: 'root'
})
export class UserService {
  private #apiUrl = 'https://jsonplaceholder.typicode.com/users'; // Public API for users
  constructor(private http: HttpClient) {}

  getUsers(): Observable&amp;lt;User[]&amp;gt; {
    console.log('UserService: Fetching users...');
    return this.http.get&amp;lt;User[]&amp;gt;(this.#apiUrl);
  }

  getUser(id: number): Observable&amp;lt;User | undefined&amp;gt; {
    console.log(`DataService: Fetching user with ID: ${id}...`);
    return of(this.users.find(u =&amp;gt; u.id === id)).pipe(delay(300)); // Simulate network delay
  }  

  // A long-lived observable, for demonstration purposes
  getPollingData(): Observable&amp;lt;number&amp;gt; {
    console.log('DataService: Starting polling...');
    return timer(0, 1000); // Emits every second
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Product Service:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/services/product.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Product } from '../models/product'; // Ensure this path is correct@Injectable({
  providedIn: 'root'
})
export class ProductService {
  private #apiUrl = 'https://fakestoreapi.com/products'; // Public API for products
  constructor(private http: HttpClient) {}

  getProducts(): Observable&amp;lt;Product[]&amp;gt; {
    console.log('ProductService: Fetching products...');
    return this.http.get&amp;lt;Product[]&amp;gt;(this.#apiUrl);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; using the modern private field syntax (private #fieldName) for better encapsulation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  1. The Classic Approach: Manual Subscription Management
&lt;/h4&gt;

&lt;p&gt;This is where many Angular developers start. It’s explicit, straightforward, but can quickly lead to boilerplate if not managed well. You create Subscription objects, add them together, and then unsubscribe a single "master" subscription.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it Works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You declare a Subscription instance (or an array of them) in your component. Each time you subscribe, you add the returned Subscription object to your master subscription. In ngOnDestroy(), you call unsubscribe() on the master, which then unsubscribes from all added child subscriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit and easy to understand for beginners.&lt;/li&gt;
&lt;li&gt;Native RxJS functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate:&lt;/strong&gt; Requires creating a Subscription object and manually adding each new subscription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetfulness:&lt;/strong&gt; Easy to forget to add a subscription, leading to leaks.&lt;/li&gt;
&lt;li&gt;Can get messy if you have many conditional subscriptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Code Snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/classic/user-list-classic/user-list-classic.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs'; // Import Subscription
import { User } from '../../models/user';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-user-list-classic',
  templateUrl: './user-list-classic.component.html',
  styleUrl: './user-list-classic.component'
  `
})
export class UserListClassicComponent implements OnInit, OnDestroy {
  users: User[] = [];
  pollingValue: number = 0;

  // 1. Declare a master Subscription object
  private #subscriptions = new Subscription(); // Using private field syntax

  constructor(private #dataService: UserService) {}

  ngOnInit(): void {
    // 2. Add each subscription to the master subscription
    this.#subscriptions.add(
      this.#dataService.getUsers().subscribe(users =&amp;gt; {
        this.users = users;
      })
    );

    this.#subscriptions.add(
      this.#dataService.getPollingData().subscribe(value =&amp;gt; {
        this.pollingValue = value;
      })
    );
  }

  // 3. Unsubscribe from the master subscription on destroy
  ngOnDestroy(): void {
    console.log('UserListClassicComponent: Unsubscribing all classic subscriptions.');
    this.#subscriptions.unsubscribe();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. The Organizer: Using the SubSink Package
&lt;/h4&gt;

&lt;p&gt;SubSink is a fantastic third-party library that simplifies the management of multiple subscriptions, making your ngOnDestroy() much cleaner. It's essentially a convenience wrapper around the classic Subscription.add() method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it Works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You install subsink (npm install subsink). You then create an instance of SubSink and assign your subscriptions to its sink property or use its add() method. When you call unsubscribe() on the SubSink instance, it unsubscribes all managed subscriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Significantly reduces boilerplate compared to manual Subscription.add().&lt;/li&gt;
&lt;li&gt;Provides a clean, single point of control for multiple subscriptions.&lt;/li&gt;
&lt;li&gt;Offers type safety if you use the .add() method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduces a third-party dependency.&lt;/li&gt;
&lt;li&gt;Still requires you to explicitly call unsubscribe() in ngOnDestroy().&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Code Snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/subsink/user-profile-subsink/user-profile-subsink.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { SubSink } from 'subsink'; // 1. Import SubSink
import { User } from '../../models/user';
import { UserService } from '../../services/user.service';
import { switchMap } from 'rxjs/operators';

@Component({
  selector: 'app-user-profile-subsink',
  templateUrl: './user-profile-subsink.component.html',
  styleUrl: './user-profile-subsink.component.scss'
  `
})

export class UserProfileSubsinkComponent implements OnInit, OnDestroy {
  user: User | undefined;
  pollingValue: number = 0; 

  // 2. Create a new SubSink instance
  private #subs = new SubSink();
  constructor(
    private #dataService: DataService,
    private #route: ActivatedRoute
  ) {}

  ngOnInit(): void {
    // 3. Assign subscriptions to subs.sink 
    this.#subs.sink = this.#route.paramMap.pipe(
      switchMap(params =&amp;gt; this.#dataService.getUser(Number(params.get('id'))))
    ).subscribe(user =&amp;gt; {
      this.user = user;
    });

    // or use subs.add()
    this.#subs.add(
      this.#dataService.getPollingData().subscribe(value =&amp;gt; {
        this.pollingValue = value;
      })
    );
  }

  // 4. Unsubscribe all managed subscriptions on destroy
  ngOnDestroy(): void {
    console.log('UserProfileSubsinkComponent: Unsubscribing all SubSink subscriptions.');
    this.#subs.unsubscribe();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. The Guardian: Leveraging takeUntil()
&lt;/h4&gt;

&lt;p&gt;This is where the magic of RxJS operators truly shines for subscription management. takeUntil() is an RxJS operator that allows an Observable stream to continue until another "notifier" Observable emits a value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it Works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You declare a Subject (typically named destroy$ or unSubscribe$). In ngOnDestroy(), you make this Subject emit a value (.next()) and then complete it (.complete()). You then pipe(takeUntil(this.destroy$)) before subscribing to any Observable. When destroy$ emits, takeUntil automatically completes the piped Observable, unsubscribing from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RxJS-native:&lt;/strong&gt; No external dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative:&lt;/strong&gt; Clearly expresses intent in the Observable chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elegant:&lt;/strong&gt; Eliminates manual unsubscribe() calls and Subscription objects in component logic.&lt;/li&gt;
&lt;li&gt;Highly readable once you understand the pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Still requires the boilerplate of declaring the Subject and implementing ngOnDestroy() in every component.&lt;/li&gt;
&lt;li&gt;Requires remembering to pipe takeUntil to every subscription.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Code Snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/takeuntil/user-list-takeuntil.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs'; // 1. Import Subject
import { takeUntil } from 'rxjs/operators'; // 2. Import takeUntil
import { User } from '../../models/user';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-user-list-takeuntil',
  templateUrl: './user-list-takeuntil.component.html',
  styleUrl: './user-list-takeuntil.component.scss'
})
export class UserListTakeUntilComponent implements OnInit, OnDestroy {
  users: User[] = [];
  pollingValue: number = 0;

  // 3. Declare a private Subject to act as the notifier
  private #destroy$ = new Subject&amp;lt;void&amp;gt;(); // Using # for private field

  constructor(private #dataService: DataService) {}

  ngOnInit(): void {
    this.#dataService.getUsers().pipe(
      map(users =&amp;gt; users.filter(user =&amp;gt; user.isActive)), // Example transformation
      takeUntil(this.#destroy$) // 4. Pipe takeUntil before subscribing and keep at last after other operators
    ).subscribe(users =&amp;gt; {
      this.users = users;
    });

    this.#dataService.getPollingData().pipe(
      takeUntil(this.#destroy$) // For long-lived observables, this is crucial
    ).subscribe(value =&amp;gt; {
      this.pollingValue = value;
    });
  }

  // 5. Emit a value and complete the Subject on destroy
  ngOnDestroy(): void {
    console.log('UserListTakeUntilComponent: Notifying takeUntil subscriptions.');
    this.#destroy$.next(); // Signal to all takeUntil operators
    this.#destroy$.complete(); // Complete the Subject itself
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. The Game Changer: takeUntilDestroyed() (Angular v16+)
&lt;/h4&gt;

&lt;p&gt;This is the newest, most elegant, and arguably the most idiomatic way to manage subscriptions in modern Angular applications (v16 and later). It removes almost all the boilerplate associated with subscription management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it Works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;takeUntilDestoryed() is a function from &lt;strong&gt;@angular/core/rxjs-interop&lt;/strong&gt; that leverages Angular’s internal &lt;strong&gt;DestroyRef&lt;/strong&gt;. When called, it implicitly hooks into the destruction of the current component, directive, or service. It creates an internal mechanism (similar to the Subject pattern) to signal completion to the Observable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Boilerplate:&lt;/strong&gt; No need to declare a Subject, no need to implement ngOnDestroy() yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angular-native:&lt;/strong&gt; Integrates seamlessly with Angular’s lifecycle and injection system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly Concise:&lt;/strong&gt; Makes your code incredibly clean and focused on the business logic.&lt;/li&gt;
&lt;li&gt;Robust and less prone to errors due to developer oversight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only available in &lt;strong&gt;Angular v16 and later&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Tied to Angular’s injection context; cannot be used in plain TypeScript classes not managed by Angular’s DI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Code Snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/take-untildestroyed/user-list-auto-unsubscribe/user-list-auto-unsubscribe.component.ts
import { Component, OnInit, inject, DestroyRef } from '@angular/core'; // 1. Import 'inject'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; // 2. Import takeUntilDestroyed
import { User } from '../../models/user';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-user-list-auto-unsubscribe',
  templateUrl: './user-list-auto-unsubscribe.component.html',
  styleUrl: './user-list-auto-unsubscribe.component.scss'
})

export class UserListAutoUnsubscribeComponent implements OnInit {
  users: User[] = [];
  private destroyRef = inject(DestroyRef); // 3. Inject at class field place

  // 4. Inject the UserService (can also be done in constructor)
  private #userService = inject(UserService);

  ngOnInit(): void {
    this.#userService.getUsers().pipe(
      takeUntilDestroyed(this.destroyRef) //5. Pass DestroyRef manually
    ).subscribe(users =&amp;gt; {
      this.users = users;
    });
  }
  // No ngOnDestroy() needed for subscription cleanup! Angular handles it.
  // Unless you have other non-RxJS cleanup logic.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Injection Context
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;takeUntilDestroyed() must be called within an &lt;strong&gt;injection context&lt;/strong&gt; :&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let us understand a bit about Injection context in Angular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Injection Context&lt;/strong&gt; is when Angular’s dependency injection system is “active” and can resolve dependencies using inject().&lt;/p&gt;

&lt;p&gt;For Example, A place where &lt;strong&gt;Class Fields&lt;/strong&gt; reside, &lt;strong&gt;Constructor&lt;/strong&gt; and Some &lt;strong&gt;Factory Functions&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; An in-detailed article about Injection Context with examples will be covered as a part of this series.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let us see how we can use takeUntilDestoroyed for managing Subscriptions considering Injection Context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit, inject, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

@Component({
 selector: 'app-my-component',
 templateUrl: './my-component.component.html',
 styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit {
 private destroyRef = inject(DestroyRef); // Inject at class field place

 ngOnInit() {
   this.service.getData().pipe(
     takeUntilDestroyed(this.destroyRef) // Pass DestroyRef manually
   ).subscribe()
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us now understand a few important scenarios that we should be cautious about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using takeUntilDestoryed within ngOnInit / any &lt;a href="https://angular.dev/guide/components/lifecycle" rel="noopener noreferrer"&gt;Life Cycle Hooks&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// injection context is not available)
// THIS WILL THROW AN ERROR: "inject() must be called in an injection context"
// because ngOnInit is a method, not a constructor or field initializer.
ngOnInit(): void {
  this.service.getData().pipe(
    takeUntilDestroyed()
  ).subscribe();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Using in any Method
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Won't work - called outside injection context
private setupSubscription(): void {
  this.service.getData().pipe(
    takeUntilDestroyed() // Error: Not in injection context
  ).subscribe();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; inject &lt;strong&gt;DestroyRef&lt;/strong&gt; and pass it explicitly&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private destroyRef = inject(DestroyRef);

private setupSubscription(): void {
  this.service.getData().pipe(
    takeUntilDestroyed(this.destroyRef) // Works
  ).subscribe();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  &lt;strong&gt;takeUntilDestroyed&lt;/strong&gt; () needs to be called within an injection context where it can implicitly resolve &lt;strong&gt;DestroyRef&lt;/strong&gt; , such as directly in the &lt;strong&gt;constructor&lt;/strong&gt; or a &lt;strong&gt;class field initializer&lt;/strong&gt; of the component/service, or by &lt;strong&gt;explicitly passing&lt;/strong&gt; the injected DestroyRef instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having established foundational strategies for subscription cleanup, we now turn our attention to a more advanced, yet equally important, consideration: &lt;strong&gt;handling nested subscriptions.&lt;/strong&gt; This can present a unique set of challenges, but I’m here to guide you through a comprehensive, step-by-step methodology to effectively resolve them..&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we use takeUntil / takeUntilDestroyed with Nested Subscriptions ?
&lt;/h3&gt;

&lt;p&gt;You’re right, the idea of “nested subscriptions” might initially sound like it necessitates multiple takeUntil or takeUntilDestroyed applications. However, with RxJS's powerful flattening operators, the solution is elegantly straightforward.&lt;/p&gt;

&lt;p&gt;Let’s tackle this step-by-step, focusing on a common use case: subscribing to Observables in parallel.&lt;/p&gt;

&lt;p&gt;Imagine we need to display data from two independent API endpoints — say, a list of users and a list of products. We want to present this information to the user only once &lt;em&gt;both&lt;/em&gt; API calls have successfully returned their data. This is a perfect job for RxJS’s forkJoin operator.&lt;/p&gt;

&lt;p&gt;forkJoin takes a dictionary or array of Observables and waits for all of them to complete. Once they all complete, it emits a single value containing the last emitted value from each of the source Observables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Key Insight for Cleanup:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using forkJoin (or switchMap, mergeMap, concatMap), takeUntil() or takeUntilDestroyed() is applied &lt;strong&gt;only once, on the outermost Observable pipe.&lt;/strong&gt; The flattening operator (forkJoin in this case) is responsible for managing the lifecycle of the &lt;em&gt;inner&lt;/em&gt; Observables it initiates. If the &lt;em&gt;outer&lt;/em&gt; Observable completes (due to takeUntil/takeUntilDestroyed), forkJoin will cease its operation, effectively stopping any pending inner subscriptions it manages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generic Syntax:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forkJoin(
 { 
  outer: outerObservable$,
  inner: innerObservable$
 }
).pipe(takeUntilDestroyed()) // OR takeUntil(this.destroy$)
.subscribe({ 
 next: (data) =&amp;gt; {
 // you data handling goes here
 }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  takeUntil() usage for Nested Subscriptions:
&lt;/h4&gt;

&lt;p&gt;This approach is suitable for all Angular versions and relies on the manual management of a Subject for the destroy signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component Code Snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/components/advanced/user-products-with-takeuntil.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, forkJoin} from 'rxjs'; // Import forkJoin and EMPTY
import { takeUntil} from 'rxjs/operators'; // Import operators
import { UserService } from '../../services/user.service';
import { ProductService } from '../../services/product.service';
import { User } from '../../models/users';
import { Product } from '../../models/product';
@Component({
  selector: 'app-user-products-with-takeuntil',
  templateUrl: './user-products-with-takeuntil.component.html',
  styleUrl: './user-products-with-takeuntil.component.scss'
  `
})
export class UserProductsWithTakeUntilComponent implements OnInit, OnDestroy {
  users: User[] = [];
  products: Product[] = [];

  // The Subject used for the takeUntil cleanup pattern
  private #destroy$ = new Subject&amp;lt;void&amp;gt;();

  constructor(
    private #userService: UserService,
    private #productService: ProductService
  ) {}

  ngOnInit(): void {
    // 1. Use forkJoin to combine the Observables
    forkJoin({
      users: this.#userService.getUsers(),
      products: this.#productService.getProducts()
    }).pipe(
      // 2. Apply takeUntil ONLY HERE, on the outermost forkJoin Observable
      takeUntil(this.#destroy$), 
     ).subscribe({
      next: data =&amp;gt; {
        this.users = data.users; 
        this.products = data.products;
        console.log('Combined data loaded successfully!');
      },
      error: err =&amp;gt; {         
        console.error('Subscription error in forkJoin:', err);         
      },
      complete: () =&amp;gt; {
        console.log('ForkJoin subscription completed.');
      }
    });
  }

  // 3. Signal the #destroy$ Subject on component destruction
  ngOnDestroy(): void {
    this.#destroy$.next();
    this.#destroy$.complete();
    console.log('UserProductsWithTakeUntilComponent: #destroy$ signaled for cleanup.');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  takeUntilDestroyed() usage for Nested Subscriptions:
&lt;/h4&gt;

&lt;p&gt;This modern approach (Angular v16+) eliminates the Subject and ngOnDestroy() boilerplate, making your components even cleaner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component Code Snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit, inject, DestroyRef } from '@angular/core'; // Import 'inject'
import { forkJoin, } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; // Import takeUntilDestroyed
import { UserService } from '../../services/user.service';
import { ProductService } from '../../services/product.service';
import { User } from '../../models/users';
import { Product } from '../../models/product';

@Component({
  selector: 'app-user-products-auto-unsubscribe',
   templateUrl: './user-products-auto-unsubscribe.component.html',
   styleUrl: './user-products-auto-unsubscribe.component.scss'
  `
})
export class UserProductsAutoDestroyComponent implements OnInit {
  private destroyRef = inject(DestroyRef); //1 Inject at class field place

   // 2. Inject services using the modern 'inject' function
  private #userService = inject(UserService);
  private #productService = inject(ProductService);

  ngOnInit(): void {
    forkJoin({
      users: this.#userService.getUsers(),
      products: this.#productService.getProducts()
    }).pipe(
      // 2. Apply takeUntilDestroyed ONLY HERE, on the outermost forkJoin Observable
      takeUntilDestroyed(this.destroyRef) // use destroyRef in takeUntilDestroyed
     ).subscribe({
      next: data =&amp;gt; {   
        this.users = data.users; 
        this.products = data.products;
        console.log('Combined data loaded successfully!');
      },
      error: err =&amp;gt; {
        console.error('Subscription error in forkJoin:', err);         
      },
      complete: () =&amp;gt; {
        console.log('ForkJoin subscription completed.');
      }
    });
  }

  // No ngOnDestroy() or Subject needed! takeUntilDestroyed handles it implicitly.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the elegance of RxJS operators like forkJoin combined with the power of takeUntil or takeUntilDestroyed() means you never have to manually manage nested subscriptions. The rule of "apply once, at the top" simplifies your code and dramatically reduces the risk of memory leaks in complex Observable chains.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Always keep the &lt;strong&gt;takeUntil&lt;/strong&gt; or &lt;strong&gt;takeUntilDestroyed&lt;/strong&gt; after all the operators in a RxJS pipeline to avoid early unsubscriptions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why takeUntil / &lt;strong&gt;&lt;em&gt;takeUntilDestroyed&lt;/em&gt;&lt;/strong&gt; Should Be the Last Operator in Your RxJS Pipe ?
&lt;/h3&gt;

&lt;p&gt;When working with RxJS in Angular, managing subscriptions is critical for preventing memory leaks. A common pattern for automatic unsubscription is using takeUntil or takeUntilDestroyed.&lt;/p&gt;

&lt;p&gt;But here’s a subtle (and crucial) best practice:&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Does It Matter?
&lt;/h4&gt;

&lt;p&gt;If you place takeUntil too early in the pipeline, it may unsubscribe &lt;strong&gt;before&lt;/strong&gt; other operators (like map, filter, or switchMap) get a chance to execute. This can lead to missed emissions or unexpected behavior.&lt;/p&gt;

&lt;h4&gt;
  
  
  Correct Usage
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.userService.getUsers().pipe(
  map(users =&amp;gt; users.filter(u =&amp;gt; u.active)),
  delay(500),
  takeUntilDestroyed() // Always at the end
).subscribe(users =&amp;gt; {
  this.activeUsers = users;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Incorrect Usage
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.userService.getUsers().pipe(
  takeUntilDestroyed(), // Early exit from pipeline
  map(users =&amp;gt; users.filter(u =&amp;gt; u.active)),
  delay(500)
).subscribe();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the above code snippet, we can say thatmap and delay might never receive emissions, or the stream could complete before these transformations are applied to all intended data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Placing takeUntilDestroyed() early can &lt;strong&gt;cut off&lt;/strong&gt; your stream before transformations or side-effects are complete — especially in async scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Generic Syntax Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forkJoin(
 { 
  outer: outerObservable$,
  inner: innerObservable$
 }
).pipe(
   map(resp =&amp;gt; resp.isActive) 
  map(resp =&amp;gt; resp.id &amp;gt; 20)
  takeUntilDestroyed() // &amp;lt;--- ALWAYS place at the END
)  
.subscribe({ 
 next: (data) =&amp;gt; {
 // you data handling goes here
 }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Choosing Your Champion: Which Strategy is Best?
&lt;/h3&gt;

&lt;p&gt;The “best” way depends on your Angular version and project context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For Angular v16+ projects:&lt;/strong&gt; &lt;strong&gt;takeUntilDestroyed()&lt;/strong&gt; is the clear winner for its unparalleled conciseness and native integration. It's the most recommended approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Angular projects before v16:&lt;/strong&gt; &lt;strong&gt;takeUntil()&lt;/strong&gt; with a Subject is the most robust and idiomatic RxJS solution. It offers a great balance of clarity and efficiency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SubSink:&lt;/strong&gt; A strong contender, especially for pre-v16 projects, providing a good balance of boilerplate reduction and explicit control without relying solely on RxJS operators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual&lt;/strong&gt;  &lt;strong&gt;Subscription management:&lt;/strong&gt; While fundamental, it's generally &lt;strong&gt;not recommended for most component-level subscriptions&lt;/strong&gt; due to its verbosity and potential for errors. It might still be useful for very specific, tightly controlled scenarios or when dealing with highly dynamic, programmatic subscriptions outside of component lifecycles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding and applying these strategies, you’re not just preventing memory leaks; you’re writing cleaner, more resilient Angular applications that truly leverage the power of RxJS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source Code &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;🔗 &lt;em&gt;Find the complete source code for this&lt;/em&gt; &lt;strong&gt;Angular Performance Series&lt;/strong&gt; _, including all examples and demos from this article, on &lt;em&gt;[_GitHub&lt;/em&gt;](&lt;a href="https://github.com/Manishh09/ng-performance-insights/tree/master/projects/01-subscription-management" rel="noopener noreferrer"&gt;https://github.com/Manishh09/ng-performance-insights/tree/master/projects/01-subscription-management&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;📌 &lt;em&gt;Love what you see?&lt;/em&gt; Don’t forget to give the repo a ⭐ &lt;em&gt;star&lt;/em&gt; to stay updated with new examples and improvements! The README.md file will guide you on running the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2abuefn3160qn8uz1bo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2abuefn3160qn8uz1bo.png" alt="Connect" width="707" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsihqtf8fewax8kcas8dy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsihqtf8fewax8kcas8dy.gif" alt="Clap" width="146" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect with Me!
&lt;/h3&gt;

&lt;p&gt;Hi there! I’m &lt;em&gt;Manish&lt;/em&gt;, a Senior Engineer passionate about building robust web applications and exploring the ever-evolving world of tech. I believe in learning and growing together.&lt;/p&gt;

&lt;p&gt;If this article sparked your interest in modern Angular, software architecture, or just a tech chat, I’d love to connect with you!&lt;/p&gt;

&lt;p&gt;🔗Follow me on &lt;a href="https://www.linkedin.com/in/manishboge/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;for more discussions or contributing or just chat tech&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you for reading — and happy&lt;/strong&gt; 🅰️ &lt;strong&gt;ngularing!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>bestpractices</category>
      <category>angular</category>
      <category>performanceimproveme</category>
      <category>subscriptionmanageme</category>
    </item>
    <item>
      <title>From NgModules to Standalone APIs: Unlocking the Future of Angular</title>
      <dc:creator>Manish Boge</dc:creator>
      <pubDate>Fri, 27 Jun 2025 12:27:48 +0000</pubDate>
      <link>https://dev.to/manish-boge/from-ngmodules-to-standalone-apis-unlocking-the-future-of-angular-2b7h</link>
      <guid>https://dev.to/manish-boge/from-ngmodules-to-standalone-apis-unlocking-the-future-of-angular-2b7h</guid>
      <description>&lt;p&gt;&lt;em&gt;Unlocking the Module-Free Future of Angular with Standalone APIs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kuogd1dh4hzho2oc16l.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kuogd1dh4hzho2oc16l.webp" alt="Cover" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Angular has consistently evolved, striving for simpler and more efficient ways to build robust applications. While NgModules have been the cornerstone of Angular’s architecture for years, the introduction of Standalone APIs marks a significant shift towards a more streamlined, module-free development experience.&lt;/p&gt;

&lt;p&gt;This article will guide you through understanding, implementing, and leveraging these powerful new APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Rise of Standalone APIs in Angular
&lt;/h3&gt;

&lt;p&gt;For years, &lt;strong&gt;NgModules&lt;/strong&gt; were central to Angular’s architecture, organizing components, directives, pipes, and services. While effective, they eventually presented challenges: excessive &lt;strong&gt;boilerplate&lt;/strong&gt; , hurdles for efficient &lt;strong&gt;tree-shaking&lt;/strong&gt; , and a steep &lt;strong&gt;learning curve&lt;/strong&gt; for new developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standalone APIs&lt;/strong&gt; emerged to address these issues. By allowing Angular artifacts (Components, Directives, Pipes) to be used independently of NgModules, standalone APIs offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced boilerplate&lt;/strong&gt; : Less code for declarations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved tree-shaking&lt;/strong&gt; : Clearer dependency graphs for better optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified learning&lt;/strong&gt; : Easier to grasp Angular’s core concepts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced developer experience&lt;/strong&gt; : More intuitive organization and direct imports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what we will deep dive into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standalone Components&lt;/li&gt;
&lt;li&gt;Standalone Directives&lt;/li&gt;
&lt;li&gt;Standalone Pipes&lt;/li&gt;
&lt;li&gt;Bootstrapping Standalone App&lt;/li&gt;
&lt;li&gt;Standalone App Structure&lt;/li&gt;
&lt;li&gt;Providing Services in Standalone Components&lt;/li&gt;
&lt;li&gt;Routing with Standalone Components&lt;/li&gt;
&lt;li&gt;Lazy Loading with Standalone Components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive into the world of Standalone APIs in Angular.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Standalone Components, Directives, and Pipes
&lt;/h3&gt;

&lt;p&gt;The core of Standalone APIs lies in a new property: standalone: true.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This tells Angular:&lt;/em&gt; “This component doesn’t need a module.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  📌 Standalone Components
&lt;/h4&gt;

&lt;p&gt;Standalone components are Angular components that can &lt;strong&gt;exist independently&lt;/strong&gt; without being declared in any NgModule.&lt;/p&gt;

&lt;p&gt;A typical &lt;em&gt;component.ts&lt;/em&gt; file, which is a standalone, would be like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/components/hero-card.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-hero-card',
  standalone: true,
  imports: [],
  templateUrl: './hero-card.component.html',
  styleUrl: './hero-card.component.scss'
})
export class HeroCardComponent {
  name = 'Batman'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Generate with Angular CLI:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component hero-card --standalone

# or shorthand

ng g c hero-card --standalone

# generic command

ng g c your-component-name --standalone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Importing Dependencies
&lt;/h4&gt;

&lt;p&gt;Standalone components must explicitly &lt;strong&gt;import&lt;/strong&gt; other components, directives, modules, or pipes they use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/components/hero-list.component.ts

import { Component } from '@angular/core';
import { NgIf } from '@angular/common';
import { NgFor } from '@angular/common';

@Component({
  selector: 'app-hero-list',
  standalone: true,
  template: `
    &amp;lt;section class="hero-list" aria-labelledby="hero-list-heading"&amp;gt;
      &amp;lt;h2 id="hero-list-heading"&amp;gt;Hero List&amp;lt;/h2&amp;gt;      
      &amp;lt;ul *ngIf="heros.length"&amp;gt;
          &amp;lt;li *ngFor="let hero of heroes" class="hero-item"&amp;gt;
              &amp;lt;span class="hero-name"&amp;gt;{{ hero.name }}&amp;lt;/span&amp;gt;
              &amp;lt;span class="hero-separator" aria-hidden="true"&amp;gt;-&amp;lt;/span&amp;gt;
              &amp;lt;span class="hero-power"&amp;gt;{{ hero.power }}&amp;lt;/span&amp;gt;
          &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/section&amp;gt;
  `,
  imports: [NgIf, NgFor]
})
export class HeroListComponent {

  // sample hero data
  heroes = [
    { id: 1, name: 'Superman', power: 'Flight' },
    { id: 2, name: 'Batman', power: 'Intellect' },
    { id: 3, name: 'WonderWoman', power: 'Strength' },
    { id: 4, name: 'Flash', power: 'Speed' }
  ];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will specify the dependencies explicitly using _imports_array in component’s metadata section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;imports: [NgIf, NgFor]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;💡Note&lt;/em&gt;&lt;/strong&gt; : In this example, we’ve used the *ngIf , &lt;em&gt;*ngFor&lt;/em&gt; directives to demonstrate how dependencies are imported in a &lt;strong&gt;standalone component&lt;/strong&gt;. While Angular 17 does not have stable support for the new &lt;strong&gt;control flow syntax&lt;/strong&gt; , we’ll continue with *ngIf , &lt;em&gt;*ngFor&lt;/em&gt; for now. We’ll adapt it accordingly in Angular 18. This example focuses primarily on illustrating the &lt;strong&gt;import mechanism&lt;/strong&gt; in standalone components.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways for Standalone Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;standalone: true: Marks it as a standalone component.&lt;/li&gt;
&lt;li&gt;imports: Replaces the imports array of an NgModule. You directly import other standalone components, directives, pipes, or even entire NgModules that your component needs.&lt;/li&gt;
&lt;li&gt;No declarations needed in any NgModule.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  📌 Standalone Directives
&lt;/h4&gt;

&lt;p&gt;Directives can also be created as standalone units, making it easy to encapsulate and share behavior.&lt;/p&gt;

&lt;p&gt;A typical &lt;em&gt;directive.ts&lt;/em&gt; file, which is a standalone, would be like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/directives/click-logger.directive.ts

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: '[appClickLogger]',
  standalone: true,
})
export class ClickLoggerDirective {
  @HostListener('click')
  logClick() {
    console.log('Element clicked!');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Usage in Standalone Components
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/components/hero-card.component.ts

import { Component, Input } from '@angular/core';
import { ClickLoggerDirective } from '../../directives/click-logger.directive';
import { NgIf } from '@angular/common';

@Component({
  selector: 'app-hero-card',
  standalone: true,
  imports: [NgIf, ClickLoggerDirective],
  template: `
    &amp;lt;div class="hero-card" *ngIf="hero" appClickLogger&amp;gt;
        &amp;lt;h3 class="hero-name"&amp;gt;Name: {{ hero.name}}&amp;lt;/h3&amp;gt;
        &amp;lt;p class="hero-power"&amp;gt;Power: {{ hero.power }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  `,
  styleUrl: './hero-card.component.scss'
})
export class HeroCardComponent { 
  hero = {
    id: 1, // Add comma here
    name: 'Batman',
    power: 'Intellect',
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Generate with Angular CLI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate directive click-logger --standalone

# or shorthand

ng g d click-logger --standalone

# generic

ng g d your-directive-name --standalone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🧠 Use Case: Reusable UI Behavior
&lt;/h4&gt;

&lt;p&gt;Think of tooltips, hover effects, click tracking — all perfect for sharing via standalone directives.&lt;/p&gt;

&lt;h4&gt;
  
  
  📌 Standalone Pipes
&lt;/h4&gt;

&lt;p&gt;Pipes can also be defined as standalone. This makes it easy to share &lt;strong&gt;pure formatting logic&lt;/strong&gt; without any module overhead.&lt;/p&gt;

&lt;p&gt;A typical &lt;em&gt;pipe.ts&lt;/em&gt; file, which is a standalone, would be like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/pipes/capitalize.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'capitalize',
  standalone: true,
})
export class CapitalizePipe implements PipeTransform {
  transform(value: string): string {
    return value.charAt(0).toUpperCase() + value.slice(1);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🧠 Using the Pipe in Templates
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/components/hero-card.component.ts

import { Component, Input } from '@angular/core';
import { CapitalizePipe } from "../../pipes/capitalize.pipe";
import { NgIf } from '@angular/common';

@Component({
  selector: 'app-hero-card',
  standalone: true,
  imports: [NgIf, CapitalizePipe],
  template: `
    &amp;lt;div class="hero-card" *ngIf="hero"&amp;gt;
        &amp;lt;h3 class="hero-name"&amp;gt;Name: {{ hero.name | capitalize }}&amp;lt;/h3&amp;gt;
        &amp;lt;p class="hero-power"&amp;gt;Power: {{ hero.power }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  `,
  styleUrl: './hero-card.component.scss'
})
export class HeroCardComponent { 
  hero = {
    id: 1
    name: 'Batman',
    power: 'Intellect',
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🛠 Generate with Angular CLI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate pipe capitalize --standalone

# or shorthand

ng g p capitalize --standalone

# generic

ng g p your-pipe-name --standalone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🔁 Use Case: Shared Utility Logic
&lt;/h4&gt;

&lt;p&gt;Whether it’s currency formatting, text transformation, or date manipulation — standalone pipes make code cleaner and easier to reuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Bootstrapping a Standalone App
&lt;/h3&gt;

&lt;p&gt;With standalone components, the traditional AppModule is no longer strictly necessary for bootstrapping your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/main.ts

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient() // Provide HttpClient if your app uses it
    // Add other root-level services or providers here like provideRouter
  ]
})
.catch(err =&amp;gt; console.error(err));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;✅&lt;/em&gt; &lt;em&gt;No need for&lt;/em&gt; &lt;em&gt;AppModule anymore!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  📁 Standalone App Structure
&lt;/h3&gt;

&lt;p&gt;One of the immediate benefits of embracing Standalone APIs is a significantly streamlined application structure. In a new standalone Angular project, you’ll notice the absence of traditional NgModule files, particularly the root app.module.ts.&lt;/p&gt;

&lt;p&gt;Instead, your application’s entry point (main.ts) directly bootstraps a standalone root component, leveraging app.config.ts for its root-level configurations and providers. Your features are then organized with their own standalone components, directives, pipes, and services.&lt;/p&gt;

&lt;p&gt;Here’s a typical simplified structure you might see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── main.ts # Application entry point, bootstraps the app
└── app/
    ├── app.config.ts # Central place for root-level providers and configurations (e.g., routing)
    ├── app.component.ts # Your root standalone component
    ├── app.component.html
    ├── app.component.css
    ├── app.routes.ts # If you define your application routes separately, often imported by app.config.ts
    ├── services/ # Application-wide services (often provided via app.config.ts or providedIn: 'root')
    │ └── ...
    └── features/ # Folder for your feature-specific standalone components
        ├── feature-a/
        │ ├── feature-a.component.ts
        │ └── ...
        └── shared/ # Common standalone components/directives/pipes
            └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Providing Services with Standalone APIs
&lt;/h3&gt;

&lt;p&gt;In the module-less world, how do you provide services?&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;providedIn: 'root' (recommended for singletons):&lt;/strong&gt; This remains the primary way to provide application-wide singleton services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/services/hero.service.ts
import { inject, Injectable } from '@angular/core'; 

@Injectable({
  providedIn: 'root' // This service is a singleton available throughout the app
})
export class HeroService { 
  name = 'Hero Service';
  private apiUrl = 'https://jsonplaceholder.typicode.com/users/1'; // Example API

  #http = inject(HttpClient);

  getUser(): Observable&amp;lt;any&amp;gt; {
    return this.http.get(this.apiUrl);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Component-level providers:&lt;/strong&gt; You can still provide services at the component level using the providers array in the @Component decorator, which will make the service instance unique to that component and its children.&lt;br&gt;&lt;br&gt;
Lets visualize like for a feature, you have created a feature-component and a feature-service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/some-feature/some-feature.component.ts
import { Component } from '@angular/core';
import { FeatureService } from './feature.service'; // A service specific to this feature

@Component({
  selector: 'app-some-feature',
  standalone: true,
  providers: [FeatureService], // Provided at component level
  template: `&amp;lt;p&amp;gt;Feature component works!&amp;lt;/p&amp;gt;`
})
export class SomeFeatureComponent {
  constructor(private featureService: FeatureService) {}
}

// In service file
import { inject, Injectable } from '@angular/core';

@Injectable() // No providedIn:'root'
export class SomeFeatureComponent {
  constructor(private featureService: FeatureService) {}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;💡Note&lt;/em&gt;&lt;/strong&gt; : The @Injectable() decorator itself doesn't inherently define scope unless providedIn: 'root' or providedIn: 'platform' is used. When an @Injectable() service (without providedIn: 'root') is listed in a component's providers array (e.g., FeatureComponent), that service instance will be &lt;strong&gt;scoped to that component's injector only&lt;/strong&gt;. This means a new instance of the FeatureService will be created specifically for FeatureComponent (and its children) each time FeatureComponent is instantiated, ensuring it's not a global singleton.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ &lt;strong&gt;Root-level providers via&lt;/strong&gt;  &lt;strong&gt;bootstrapApplication:&lt;/strong&gt; As seen in main.ts, you can provide services directly when bootstrapping the application(Refer Bootstrapping Standalone App section above). This is ideal for global services like HttpClient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing with Standalone Components
&lt;/h3&gt;

&lt;p&gt;Routing also adapts gracefully to standalone APIs. You use new functions like provideRouter and importProvidersFrom to configure routing without relying on RouterModule.forRoot() in a traditional NgModule.&lt;/p&gt;

&lt;p&gt;app.routes.ts structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/routes.ts

import { Routes } from '@angular/router';
import { UserProfileComponent } from './user-profile/user-profile.component';
import { HomeComponent } from './home/home.component'; // Assuming standalone
import { ContactComponent } from './contact/contact.component'; // Assuming standalone

export const APP_ROUTES: Routes = [
  { path: '', component: HomeComponent },
  { path: 'profile', component: UserProfileComponent },
  { path: 'contact', component: ContactComponent },
  { path: '**', redirectTo: '' } // Wildcard route
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;app.config.ts structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app.config.ts

import { ApplicationConfig } from '@angular/core'; // New import
import { APP_ROUTES} from './app.routes'; // Your defined routes
import { provideRouter } from '@angular/router'; // New import
import { provideHttpClient } from '@angular/common/http'; // New import

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    provideRouter(APP_ROUTES) // Configure routing directly,
    provideHttpClient()
  ]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;main.ts structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/main.ts (updated)

import { appConfig } from './app/app.config';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { provideRouter } from '@angular/router'; // New import
import { UserProfileComponent } from './app/user-profile/user-profile.component';
import { APP_ROUTES } from './app/routes'; // Your defined routes

bootstrapApplication(UserProfileComponent, appConfig)
.catch(err =&amp;gt; console.error(err));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Lazy-Loading With Standalone Components
&lt;/h4&gt;

&lt;p&gt;We used to lazy load the feature modules using &lt;em&gt;loadChildren&lt;/em&gt; in module based apps, but how can we do so in a standalone app ?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;✅&lt;/em&gt;_loadComponet_comes into rescue here.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/app.routes.ts

{
  path: 'hero-list',
  loadComponent: () =&amp;gt; import('./hero-list.component').then(c =&amp;gt; c.HeroListComponent)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;✅No&lt;/em&gt; &lt;em&gt;loadChildren, no feature modules — just pure components.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🛡️Best Practices
&lt;/h3&gt;

&lt;p&gt;To truly harness the power and benefits of Angular Standalone APIs, consider adopting these best practices:&lt;/p&gt;

&lt;h4&gt;
  
  
  📦 Prefer Granular Imports
&lt;/h4&gt;

&lt;p&gt;With Standalone Components, Directives, and Pipes, you gain fine-grained control over your dependencies. Instead of &lt;strong&gt;importing&lt;/strong&gt; entire NgModules, embrace the ability to import &lt;em&gt;only what’s specifically needed&lt;/em&gt; for a particular standalone artifact.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// In your standalone component
imports: [
  CommonModule, // For NgIf, NgFor, etc. (if needed, or import them individually)
  MyStandaloneComponent,
  MyStandaloneDirective,
  MyStandalonePipe
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This practice directly leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;em&gt;Smaller Bundles:&lt;/em&gt; Your final application package only includes the code it strictly requires.&lt;/li&gt;
&lt;li&gt;✅ &lt;em&gt;Superior Tree-Shaking:&lt;/em&gt; Build tools can more effectively identify and eliminate unused code, resulting in leaner, faster applications.&lt;/li&gt;
&lt;li&gt;✅ &lt;em&gt;Clearer Dependencies:&lt;/em&gt; It becomes immediately obvious what a component, directive, or pipe relies on, improving maintainability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Embracing granular imports is a core philosophy of the standalone approach, reducing unnecessary boilerplate and optimizing performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏁 Conclusion
&lt;/h3&gt;

&lt;p&gt;Standalone APIs are the future of Angular. They:&lt;/p&gt;

&lt;p&gt;✅ Remove unnecessary complexity&lt;br&gt;&lt;br&gt;
✅ Encourage better separation of concerns&lt;br&gt;&lt;br&gt;
✅ Make apps easier to test, lazy-load, and scale&lt;/p&gt;

&lt;p&gt;As Angular continues to modernize, &lt;strong&gt;NgModules are no longer mandatory&lt;/strong&gt;. You don’t need to refactor everything overnight — but embracing &lt;strong&gt;Standalone Components, Directives, and Pipes&lt;/strong&gt; will lead to cleaner and more maintainable codebases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✨ &lt;strong&gt;If you haven’t tried Angular’s Standalone APIs yet — now is the time.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Start with a small feature or a new component and experience the simplicity and clarity that Standalone APIs bring to Angular development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  💻 Source Code &amp;amp; Resources
&lt;/h3&gt;

&lt;p&gt;🔗 &lt;em&gt;Find the complete source code for this&lt;/em&gt; &lt;strong&gt;Angular 17 series&lt;/strong&gt; &lt;em&gt;, including all examples and demos from this article, on GitHub:&lt;/em&gt; &lt;a href="https://github.com/Manishh09/angular17-insights-series" rel="noopener noreferrer"&gt;angular-17-series repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;📌 &lt;em&gt;Love what you see?&lt;/em&gt; Don’t forget to give the repo a ⭐ &lt;em&gt;star&lt;/em&gt; to stay updated with new examples and improvements! The README.md file will guide you on running the project.&lt;/p&gt;

&lt;p&gt;All additional resources referenced in this article are readily available for hands-on practice and learning in the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Up Next: Bridging the Gap — Migrating to Standalone APIs
&lt;/h3&gt;

&lt;p&gt;We’ve learnt the ‘what’ of Standalone APIs. Now, let’s tackle the ‘how.’ The upcoming article will be an essential guide to seamlessly transitioning your existing Angular applications from NgModules to the cleaner, more efficient standalone architecture. Let’s say goodbye to NgModules and get ready to modernize your codebase!!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76d4d6c1stj7xee3w3us.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76d4d6c1stj7xee3w3us.gif" alt="Read" width="146" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fple14p1wko2iast5q1mf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fple14p1wko2iast5q1mf.png" alt="Clap" width="707" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🤝Connect with Me!
&lt;/h3&gt;

&lt;p&gt;Hi there! I’m &lt;em&gt;Manish&lt;/em&gt;, a Senior Engineer passionate about building robust web applications and exploring the ever-evolving world of tech. I believe in learning and growing together.&lt;/p&gt;

&lt;p&gt;If this article sparked your interest in modern Angular, software architecture, or just a tech chat, I’d love to connect with you!&lt;/p&gt;

&lt;p&gt;🔗Follow me on LinkedIn for more discussions: &lt;a href="https://www.linkedin.com/in/manishboge/" rel="noopener noreferrer"&gt;&lt;em&gt;Manish Boge&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you for reading — and happy&lt;/strong&gt; 🅰️ &lt;strong&gt;ngularing!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>standaloneapp</category>
      <category>learninganddevelopme</category>
      <category>angular17</category>
      <category>standalonecomponent</category>
    </item>
  </channel>
</rss>
