Angular Architecture

repository·main·Indexed 21 days ago

https://github.com/danwahlin/angular-architecture

A collection of code samples demonstrating Angular architectural patterns and best practices. This repository serves as companion material for Angular architecture training courses, featuring labs on routing guards, PreloadAllModules strategies, RxJS Subjects, shared libraries, and the use of Angular CLI for scaffolding.

Tokens
39.1K
Snippets
198
Records
232
Agent score
73%

What's inside angular-architecture

  1. Explore State Management and Additional Demos

    main

    The repository includes specialized folders for advanced topics. To run these, navigate to the specific folder and follow the standard npm install and ng serve process.

    State Management Demos

    • State - DIY Store: Located in state-management/diy-store. Starts with a simple "do it yourself" store using subjects and observables.
    • State - NgRx: Located in state-management/ngrx.
    • State - ngrx-data: Located in state-management/ngrx-data.
    • State - Observable Store: Located in state-management/observable-store using the Observable Store library.

    Other Specialized Demos

    • Cloning: Located in the cloning folder.
    • Input/Output Properties: Located in the input-output-demo project.
    • Shared Library: Located in the shared-library-example project.
  2. Understand the Standalone Lab Reference

    main

    The Standalone Lab serves as a reference for converting an Angular application from an NgModule-based architecture to a Standalone architecture.

    There are two versions of this lab:

    1. REFERENCE version: Represents the 100% NgModule state of the application. It is a near-perfect copy of the routing-guards-and-preload-strategies END state.
    2. END state: The final state of the lab after the conversion to Standalone components and configurations has been completed.
  3. Explore Angular Architecture Main Demos

    main

    The demos folder contains several core architectural examples:

    • Communication: Demonstrates using services and subjects for component communication.
    • Component Inheritance: Shows how to implement component inheritance.
    • Features Modules: Demonstrates application structure using NgModules.
    • Http Client RxJS: Shows how to use RxJS operators to combine HTTP client results.
    • Pipes and Functions: Demonstrates the use of Angular Pipes.
    • Signals: A simple demonstration of Angular Signals.
    • Structuring Components: Focuses on component presentation and change detection.
    • Subjects: Demonstrates the different types of RxJS Subjects.
    • View Models: Provides progressive examples of implementing view models.
    • Planning: Provides architectural planning tips.
  4. Understand the difference between CanActivate and preventing code chunk loading

    main

    When using PreloadAllModules, a CanActivate guard only prevents the user from navigating to a route in the UI. However, because the module was preloaded, the network chunk for that module will still be downloaded.

    To prevent the code chunk from loading entirely, you must prevent the module from being preloaded or use a different strategy that intercepts the loading process before the chunk is requested.

  5. Understand NgRx State Management Concepts

    main

    NgRx is a reactive state management library for Angular based on several core concepts that work together to manage application state:

    • Actions: Objects that represent unique events (e.g., GetCustomers, AddCustomer). They carry a type and can optionally include a payload (data).
    • Reducers: Pure functions that receive the current state and an action, then return a new state. They are responsible for updating the data store.
    • Effects: Side-effect handlers (e.g., for HTTP calls) that listen for actions, perform tasks, and then dispatch new actions (like Success or Error actions) based on the result.
    • Selectors: Functions used to retrieve and slice specific pieces of data from the store. They allow components to react to changes in the state.
    • Store: The single source of truth that holds the application state.
  6. Prevent module loading using canMatch

    main

    If you want to prevent a lazy-loaded module from being downloaded at all when a user attempts to navigate to a route, use the canMatch property instead of canActivate. While canActivate allows the chunk to load before checking permissions, canMatch allows the router to skip the route (and its associated module loading) entirely if the guard returns false.

    // In AppRoutingModule
    { path: 'villains', loadChildren: () => import('./villains/villains.module').then(m => m.VillainsModule), canMatch: [AuthGuard] }
  7. Understand RxJS Subject types

    main

    When implementing component communication via services, choose the appropriate RxJS Subject type based on the required data emission behavior:

    • Subject: Sends data to subscribed observers. Any data emitted before a subscription is made is not sent to that observer.
    • BehaviorSubject: Sends the last emitted data value to observers immediately upon subscription.
    • ReplaySubject: Sends all previously sent data to new observers (the number of buffered values can be configured).
    • AsyncSubject: Emits only the last value to observers, and only when the sequence is completed.
  8. Configure Preload Strategies in AppRoutingModule

    main

    Preload strategies allow you to load JavaScript bundles in the background before a user explicitly navigates to a route. You can configure these in your AppRoutingModule using different strategies:

    PreloadAllModules

    Loads all lazy-loaded modules immediately after the initial application load.

    PreloadSelectedModulesList

    An opt-in strategy where only specific modules are preloaded. To use this, add data: { preload: true } to the specific route configuration you wish to preload.

    NetworkAwarePreloadStrategy

    A strategy that checks the user's connection speed. It only preloads modules if the network connection meets certain criteria (e.g., faster than 3G).

    // Example: Opt-in Preload Strategy configuration
    { path: 'villains', loadChildren: () => import('./villains.module').then(m => m.VillainsModule), data: { preload: true } }
  9. Compare Subject vs BehaviorSubject behavior

    main

    When choosing between Subject and BehaviorSubject for state management or data streaming, consider how late subscribers should behave:

    • Subject: Only current subscribers receive data. If a component subscribes after a value has been emitted via .next(), it will not receive that previous value. It is useful for discrete events.
    • BehaviorSubject: Requires an initial value in its constructor (e.g., new BehaviorSubject<T>(null)). New subscribers immediately receive the last emitted value upon subscription, followed by any new values. It is ideal for representing state that components need to know immediately upon initialization.
    // Subject: No initial value, late subscribers miss previous data
    this.subject$ = new Subject<string>();
    
    // BehaviorSubject: Requires initial value, late subscribers get the last value
    this.subject$ = new BehaviorSubject<string>(null);
  10. Create and implement a CanActivate Guard

    main

    You can prevent users from accessing specific routes by implementing a CanActivate guard.

    1. Generate a service-based guard using the Angular CLI: ng generate guard AuthGuard (select CanActivate).
    2. Implement the logic within the guard to return false when access should be denied.
    3. Register the guard in your AppRoutingModule by adding it to the canActivate array of the target route and ensuring it is provided in the module configuration.
    // Example implementation logic
    export const authGuard: CanActivateFn = (route, state) => {
      return false; // Prevents navigation
    };
  11. Subscribe to an Observable Service in a component

    main

    To react to changes in a service, inject the service into your component and subscribe to its public observable in the ngOnInit lifecycle hook.

    Important: To prevent memory leaks, you must store the Subscription object and unsubscribe from it during the ngOnDestroy lifecycle hook.

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { ShoppingCartService } from '../core/shopping-cart.service';
    import { Subscription } from 'rxjs';
    
    @Component({
      selector: 'app-header',
      templateUrl: './header.component.html',
      styleUrls: ['./header.component.css'],
    })
    export class HeaderComponent implements OnInit, OnDestroy {
      cartItemsCount = 0;
      shoppingCartSub: Subscription;
    
      constructor(private shoppingCartService: ShoppingCartService) {}
    
      ngOnInit() {
        // Subscribe to the service's observable
        this.shoppingCartSub = this.shoppingCartService.shoppingCartChanged$.subscribe((val) => {
          this.cartItemsCount = val;
        });
      }
    
      ngOnDestroy() {
        // Unsubscribe to prevent memory leaks
        this.shoppingCartSub.unsubscribe();
      }
    }