π΄ π‘ π’ π
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 variables
that aren't signalsNo RxJS Observables or Subjects
π΅ HTTP Handling:
Signal with httpresource
, for automatic data fetching
π΅ Template Binding:
- Replaces
NgModel
is 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 injection
as 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)