π΄ π‘ π’ π
Test API based on Observable, Signal (state management, effect, httpResource, input-pattern, model)
https://jsonplaceholder.typicode.com/posts
-
Get all country names and display on the page: https://restcountries.com/v3.1/independent?fields=name
-
Select a country and Show the flag: https://restcountries.com/v3.1/name/Grenada?fields=name,flags
-
Search countries by language: https://restcountries.com/v3.1/lang/spanish?fields=name
Angular CLI: 20.1.5
Node: 22.13.1
Package Manager: npm 11.0.0
OS: darwin arm64
Angular: 20.1.6
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.2001.5
@angular-devkit/build-angular 20.1.5
@angular-devkit/core 20.1.5
@angular-devkit/schematics 20.1.5
@angular/cli 20.1.5
@schematics/angular 20.1.5
rxjs 7.8.2
typescript 5.8.3
zone.js 0.15.0β
@NgModule β Standalone component
β
*ngFor, *ngIf β Modern control flow: @if, @for
β
HttpClient β HttpResource API for data fetching
β
ngOnInit() + subscribe() + contructor based inject β Use reactive Signals + computed()
β
contruct-based DI injection β inject(HttpClient)
β
Better Ts notation β Protected + readonly template properties for protection and mutability control
β
RxJS / reactive streams β `Signal-Based Component Architecture Pattern` (using signals, computed, and effects internally instead of Observables for local reactive state)
β
Flow / Two-Way Binding (Old Way) β `Signal-input-pattern architecture flow` (signal parent β child communication)src/
βββ app/
βββ SoC/
βββ input/output // Separation of Concern using Parent and Child, @Input()/@Output()
βββ input/output // Separation of Concern using Parent and Child, input signal/@Output()
βββ orphan-observable/ // single Component, managing API request using Observables
βββ orphan-signal/ // single Component, managing API request using Signals
βββ orphan-signal-simple/ // single Component, managing API request using Signal
βββ orphan-signal-nested/ // single Component, managing complex API request using Signal
βββ orphan-signal-httpresource/ // simple Component, managing API request using Signals with httpResouce asynchronous data fetching
βββ orphan-signal--httpresource-reactiveForm/ // Shows how the new signals approach replaces the traditional RxJS pattern
βββ orphan-signal-httpresource-signal/ // 100% fully signal-based. Using direct signal binding with [value] and (input). Simple event handler to update the signal
βββ orphan-signal-input-pattern/ // full signal-based approach: 1-way binding
βββ orphan-signal-model/ // full signal-based approach: 2-way binding
|
βββ app.component.ts
βββ auth.interceptor.ts
βββ http.interceptor.tsπ‘ Green solutions are 100% fully reactive signal-based which are Angular recommendations:
π΅ Reactive state management
All state is managed through signals in the service
π΅ Data Management:
No local component state variablesthat aren't signalsNo RxJS Observables or Subjects
π΅ HTTP Handling:
Signal with httpresource, for automatic data fetching
π΅ Template Binding:
- Replaces
NgModelis part of the older Forms API, while signals represent Angular's future All template expressions use signals(vehicleService.searchTerm(), vehicleService.isLoading(), etc.)- Uses
modern Angular control flow(@if, @else, @for)
π΅ Data Flow:
- Use
signal-input-pattern:[value] + (input) pattern:It's simply a combination of 1-way binding (Property [value]="searchSignal()" + event binding (input)="signal.set()")-- The Two Parts ofSignal-Input-Pattern:
<!-- Template - Signal-Input-Pattern -->
<input
[value]="signalService.searchTerm()" <!-- Signal β View -->
(input)="onSearch($event)" <!-- View β Signal -->
/> Signal β View ([property] binding) = [value]="searchSignal()
View β Signal (event() handler) = (input)="signal.set()"Direct Signal Control(when is read = binding, when is updated = event handler)
π΅ Event Handling:
- Input events directly update signals (this.vehicleService.searchTerm.set(value))
- No intermediate transformations using RxJS operators
π΅ Service Implementation:
- Uses httpResource for HTTP requests (instead HttpClient) This provides:
- Automatically fetches data when the component initializes.
- Handles loading, success, and error states without extra code.
- Provides a .value() method to access the latest data.
- Supports reloading with .reload().
- Stays within the signals paradigm and use signals' effect() to automatically handle cleanup (instead OnInit/OnDestroy + No need for manual subscription management)
π‘ Other technical mentions
π΅ SoC
This example demonstrates the separation of concerns between the:
service (responsible for fetching data),
smart component (responsible for handling business logic and passing data to the dummy component), dummy component (responsible for rendering the UI)
π΅ Modern StandAlone Components:
I directly bootstrap the component itself, not its module. This is because standalone components have their own injectors and don't rely on a root module for dependency injection. Promotes code maintainability, reusability, and smaller application size.
π΅ Implemented TSP mechanism:
I'm using Tree Shakeable Providers in Services by using the providedIn attribute, this will provide the benefits of both tree shaking performance and dependency injection,
meaning that our services will not be included in the final bundle unless they are being used by other services or components. As a result we reduce the bundle size by removing unused code from the bundle.
π΅ RxJS
takeUntilDestroyed(this.destroyRef)to automatically unsubscribe when the component is destroyed, simplifying the cleanup process even furthershareReplay(1)because multiple components might subscribe to the same observable
π΅ Dependency Injection Pattern:
I'm using Modern Dependency Injection functions, instead traditional constructor-based dependency injectionas result I will have a more Modular, Less Complex
π΅ Implement Caching:
-- Cache API Service Calls
Caches identical HTTP requests within a single component:
I'm using shareReplay() to improve efficiency, ensuring that all subscribers receive the most recent data without triggering multiple HTTP requests.
π΅ DestroyRef & takeUntilDestroyed(): Angular 16+
I'm using provides a more declarative and efficient way to handle automatic cleanup tasks when a component or service is destroyed: takeUntilDestroyed(this.destroyRef) to automatically unsubscribe when the component is destroyed, simplifying the cleanup process even further
π΅ Function-based Interceptor (optional):
It also showcases the usage of an interceptor to log HTTP requests and responses. While not necessary for this example, it can be useful for debugging and monitoring purposes (WIP)