myHotTake

Tag: Angular tips

  • How to Fix Common Angular Anti-Patterns with Simple Tips

    If you find this story helpful or entertaining, feel free to like or share it with others who might appreciate it!


    I’m hosting a potluck dinner at my house, and I’ve invited several friends to bring their favorite dishes. My house is like an Angular application, and each room represents a different component of the app. The goal is to ensure that each room, or component, functions properly and that the entire event runs smoothly.

    As I organize the potluck, I notice a few things that could potentially go wrong, much like common anti-patterns in Angular applications. The first potential issue is when someone brings a dish that requires too much setup. This is like creating components that have too many responsibilities. If I allow this, the kitchen becomes cluttered, and it takes forever to get things ready. So, I ensure that each dish is simple and self-contained, reflecting the Single Responsibility Principle.

    Next, I notice a friend who insists on checking every dish and adjusting the seasoning. This is akin to tightly coupled components in Angular. If I let this happen, it creates a bottleneck, and nothing can proceed without this friend’s input. To avoid this, I encourage everyone to trust each other’s cooking skills, promoting loose coupling and modularity.

    Then, there’s a guest who keeps going back and forth between the living room and the kitchen for every small item, like salt or napkins. This is similar to making too many HTTP requests in a service. To streamline things, I set up a small station with essentials, reducing unnecessary traffic and improving efficiency.

    Finally, one friend brings a giant, elaborate cake that can only fit in the hallway. This cake is like a large, monolithic component that doesn’t fit well into the structure of the app. To manage this, I ask them to slice it into smaller, manageable pieces that can be enjoyed in any room, emphasizing the importance of small, reusable components.

    By addressing these issues, I ensure that the potluck is a success, much like how avoiding anti-patterns leads to a well-functioning Angular application. If this analogy made you smile or think differently about Angular, feel free to like or share!


    Part 2: Tying It Back to JavaScript

    Continuing from our potluck scenario, let’s see how these concepts translate into JavaScript and Angular code.

    1. Single Responsibility Principle At the potluck, I ensured each dish was simple and self-contained. In Angular, this means creating components that focus on a single responsibility. Here’s a code example:
       // app.component.ts
       import { Component } from '@angular/core';
    
       @Component({
         selector: 'app-dish',
         template: `
           <div>
             <h2>{{ title }}</h2>
             <p>{{ description }}</p>
           </div>
         `
       })
       export class DishComponent {
         title: string = 'Spaghetti';
         description: string = 'A classic Italian pasta dish.';
       }

    This component only handles displaying a dish, keeping it simple and focused.

    1. Loose Coupling Just like trusting my friends’ cooking skills, Angular components should be loosely coupled. Use @Input() and @Output() decorators to communicate between components:
       // parent.component.html
       <app-dish [title]="dishTitle" (notify)="onNotify($event)"></app-dish>
    
       // parent.component.ts
       dishTitle = 'Spaghetti';
    
       onNotify(event: any) {
         console.log('Notification from child:', event);
       }
    
       // dish.component.ts
       import { Component, Input, Output, EventEmitter } from '@angular/core';
    
       @Component({
         selector: 'app-dish',
         template: `
           <div>
             <h2>{{ title }}</h2>
             <button (click)="notifyParent()">Notify Parent</button>
           </div>
         `
       })
       export class DishComponent {
         @Input() title: string;
         @Output() notify = new EventEmitter();
    
         notifyParent() {
           this.notify.emit('Dish is ready!');
         }
       }

    This setup allows components to communicate without being tightly coupled.

    1. Reducing Unnecessary Traffic Just like setting up a station with essentials, we should optimize data fetching in Angular:
       // data.service.ts
       import { Injectable } from '@angular/core';
       import { HttpClient } from '@angular/common/http';
       import { Observable } from 'rxjs';
    
       @Injectable({
         providedIn: 'root',
       })
       export class DataService {
         constructor(private http: HttpClient) {}
    
         fetchDishes(): Observable<any> {
           return this.http.get('api/dishes');
         }
       }

    Here, data fetching is centralized in a service, reducing redundant requests and improving efficiency.

    1. Breaking Down Monoliths Like slicing the giant cake into smaller pieces, break down large components into smaller, reusable ones:
       // large.component.ts (before refactor)
       // Contains logic for display, interaction, and data handling
    
       // After refactor, split into:
       // display.component.ts
       // interaction.component.ts
       // data-handler.service.ts

    By breaking down large components, each piece is easier to manage and reuse.

    Key Takeaways

    • Single Responsibility Principle: Keep components focused on a single task to reduce complexity.
    • Loose Coupling: Use @Input() and @Output() for flexible component communication.
    • Optimized Data Fetching: Centralize HTTP requests in services to reduce redundancy.
    • Reusable Components: Break large components into smaller, manageable pieces for better maintainability.