218. Module Introduction
This section shifts from standalone Angular apps with in-memory data to apps connected to a backend (and potentially a database). You’ll learn how to send HTTP requests, handle backend responses, and manage loading and error states. A new demo project will be used where data is fetched from and stored on the backend instead of within the Angular app.
219. The Starting Projects - Frontend & Backend
-
The provided starter project combines two separate apps in one folder: an Angular frontend and a
backendfolder containing a standalone Node/Express Web API (no Angular code). -
The backend exposes routes (e.g., GET, PUT, DELETE) and persists dummy data in JSON files instead of a database, to simulate a real backend/data store.
-
Setup steps:
-
Run
npm installin the main project folder (Angular dependencies). -
Run
npm installinside thebackendfolder (backend dependencies).
-
-
Run both servers separately:
-
In the
backendfolder:npm startto start the API (keep it running; stop with Ctrl+C and restart as needed). -
In a separate terminal, in the main project folder:
npm startto run the Angular dev server.
-
-
After starting, visit
localhost:4200to see the app. The next goal is to fetch and display data (e.g., “available places”) from the backend, learning how Angular communicates with the API to access the stored JSON data.
220. How To Connect Angular Apps To A Backend
Angular apps run in users’ browsers, so they shouldn’t connect directly to databases or contain database credentials (it’s insecure and can cause other issues). Instead, an Angular frontend communicates with a separate backend (your own or a third-party) through a web API that exposes specific routes/URLs. The Angular app sends HTTP requests (e.g., GET to fetch data, or requests to send/store data), and the backend responds with data the app can use to update the UI. In this case, the backend provides a GET /places route that reads place data (from a JSON file here) and returns it, and the next step is to call that route from the Angular app.
222. Getting Started with Angular’s Http Client
-
The data for “available places” should be fetched in the
AvailablePlacesComponent(a service could be used later, but for now the component is fine). -
Angular sends HTTP requests via the
HttpClientservice (from@angular/common/http), which you can inject usinginject()or a constructor. -
If you inject
HttpClientwithout configuring it, you’ll get aNullInjectorErrorbecause Angular doesn’t provide it automatically. -
In a standalone setup, you typically register
HttpClientapp-wide inmain.tsby adding providers tobootstrapApplication(…), usingprovideHttpClient()(imported from@angular/common/http). -
Once
provideHttpClient()is added, Angular can injectHttpClientand the error disappears, making it ready to send requests. -
In a module-based app, you’d provide it in the root module instead (covered later).
224. Sending a GET Request To Fetch Data
-
Use the injected Angular
HttpClientto fetch initial component data inngOnInit, since it runs once when the component is ready and is a good place to start loading backend data (e.g., available places). -
Call
http.get()(notpost()) to send a GET request to an endpoint likehttp://localhost:3000/places. -
HttpClient.get()returns an Observable; the request only happens when you subscribe. Insubscribe, handle the emitted response innext(typically only one emission: the response data). Initially, log the response to the console. -
Although HttpClient observables usually complete after one value, it’s still good practice to clean up: store the subscription and unsubscribe in an
onDestroycallback using Angular’sDestroyRef. -
Improve TypeScript support by typing the response via generics, e.g.
get<{ places: Place[] }>(…), based on the backend response shape (an object with aplaceskey containing an array ofPlace). -
The backend may respond with a deliberate delay (for demo), motivating later UI work like showing a loading state. After confirming the data arrives, the next step is to render/output the places in the UI.
225. Configuring Http Requests
Angular’s HttpClient.get() can take a second argument (a config
object) to change what the subscribe callback receives via the
observe option:
-
observe: 'response'makes the callback receive the fullHttpResponse(not just the parsed data). You then access data viaresponse.body, which can benull, so you may need optional chaining (e.g.response.body?.places). The full response also includes headers, status code/status text, success info, etc. -
observe: 'events'makes the callback fire multiple times for different request lifecycle events (e.g. an initial “request sent” event, then the final response), letting you react at different stages.
After demonstrating these, the code returns to using just the response body data to render the places in the UI.
226. Transforming & Using Response Data
The text explains how to display fetched “places” data in an Angular
component by updating an existing places Signal once the HTTP
observable emits. It suggests optionally using pipe() with the RxJS
map operator to transform the response from an object like
{ places: […] } into just the array […]
before subscribe(), so the UI can be populated directly with the
places list.
After confirming the UI updates correctly on reload, it outlines the next improvements: show fallback/loading text while the data is being fetched and add error handling, before moving on to selecting places, adding favorites, and persisting them to the backend.
227. Showing a Loading Fallback
Add a loading/fallback UI while “places” data is being fetched by
introducing an isFetching Signal:
-
Initialize
isFetchingtofalse. -
In
ngOnInit, setisFetchingtotrueright before starting the HTTP/request subscription. -
Set
isFetchingback tofalsein the observable’scompletecallback (safer thannextif the observable could emit multiple values). -
In the template, conditionally render a fallback message/spinner when
isFetchingis true (e.g., via@if), and render the places list only when data exists. -
Result: “Fetching available places…” shows during the delay, then disappears once the places arrive.
228. Handling HTTP Errors
The text explains how to handle loading and error states for Angular HTTP requests, including how to simulate backend failures and how to present user-friendly error messages.
-
Simulating an error response (backend): In a Node/Express route, return an HTTP 500 response (e.g.,
res.status(500).json(…)) to mimic a server failure. Restart the backend server after code changes so they take effect. -
Observing the failure (frontend): When the Angular app reloads, the request doesn’t return data and an HTTP error appears in dev tools.
-
Managing UI state with signals: Add:
-
a loading/fetching signal (already implied) and
-
an error signal (e.g., a string, initially empty).
-
-
Handling errors in
subscribe: Use the third observer callback (error) to set the error signal when the observable errors (e.g., offline, non-2xx response). -
Template updates:
-
Show a loading fallback only when fetching is true AND no error.
-
Show an error paragraph when the error signal is truthy.
-
-
Error object vs. message: The error passed to the observer is often an object, so interpolating it yields
"[object Object]". Accesserror.messageinstead. -
User-friendly messaging: Log the detailed error for developers, but display a generic message to users like:
“Something went wrong fetching the available places. Please try again later.” -
Optional RxJS approach (
catchError): Move error transformation out ofsubscribeby usingcatchErrorinpipe():-
catchErrorreceives the original error and must return a new observable. -
Use
throwError) => new Error(customMessageto rethrow a custom Error so the subscriber’serrorcallback still runs, but with a cleaner message/type.
-
-
Restore normal behavior: Remove/comment out the forced 500 backend response, restart the backend, and the places load successfully again.
Overall, it demonstrates a common Angular pattern: track loading + error state, update the template accordingly, and optionally use RxJS operators to keep subscription callbacks cleaner.
229. Sending Data To A Backend
-
The goal is to not only fetch places but also send selected place data to the backend so the backend can store a user’s favorite places in
user-places.json. -
The backend exposes a PUT route at
PUT /user-placeswhich expects a request body containing aplaceId. -
In Angular, the
app-placescomponent emits aselectPlaceevent when a place is clicked; this event provides the full place object. -
In the
AvailablePlacesComponent, you should:-
Listen to
(selectPlace)in the template and call a new method (e.g.onSelectPlace($event)). -
Implement
onSelectPlace(selectedPlace: Place)and useHttpClient.put(…)to send a request tohttp://localhost:3000/user-placeswith a JSON body like{ placeId: selectedPlace.id }.
-
-
The PUT request won’t fire unless you
subscribe(), just like GET requests—subscribing triggers the HTTP call. -
After subscribing, clicking a place results in:
-
A network request showing the payload
{ placeId: … } -
A response containing the updated user places
-
The backend
user-places.jsonbeing updated (no longer empty)
-
-
Next steps mentioned: add loading/error handling for the PUT request, and then display the stored user places in the “favorite places” UI section.
230. More Data Fetching & Some Code Duplication
The goal is to display a user’s favorite places in the “favorite places”
box by fetching them from the backend (GET /user-places, backed by a
user-places.json file).
Steps outlined:
-
Reuse much of the data-fetching logic from the
AvailablePlacescomponent in theUserPlacescomponent:-
Copy
ngOnInitlogic (then adjust it). -
Copy supporting state:
placessignal,isFetchingsignal,errorsignal. -
Copy required injections:
HttpClientandDestroyRef. -
Implement
OnInitand add all necessary imports (signal,inject,Place,HttpClient,DestroyRef,map,catchError,throwError).
-
-
Update the request URL to
/user-placesand adjust the error message (e.g., “Fetching your favorite places failed.”). -
Replace the
UserPlacestemplate “todo” placeholder by copying theAvailablePlacestemplate structure, but:-
Don’t wire up place selection (favorites are already selected); removal may be added later.
-
Use wording like “Fetching favorite places…”.
-
-
After reloading, favorites should appear immediately; newly favorited places won’t show in the favorites list until reload (to be fixed later).
-
Notes that this introduces code duplication; suggests later refactoring via a shared component or (more likely) a service to share logic.
231. Outsourcing HTTP Request Logic Into A Service
-
Refactor HTTP logic out of components into a shared
PlacesServiceto centralize data-fetching/configuration and keep components lean. -
Create a private
fetchPlaces(url: string, errorMessage: string)method in the service by moving the existinghttpClient.get(…).pipe(map, catchError, throwError)code from a component into the service. InjectHttpClientinto the service and move required RxJS imports (map,catchError,throwError) there too. -
Build two public wrapper methods that reuse
fetchPlaces:-
loadAvailablePlaces()callsfetchPlaceswith the available-places URL and an appropriate error message. -
loadUserPlaces()callsfetchPlaceswith the user-places URL and its own error message.
-
-
Update
AvailablePlacesComponentandUserPlacesComponentto:-
Stop injecting
HttpClient; injectPlacesServiceinstead. -
Call
loadAvailablePlaces()/loadUserPlaces()and subscribe in the component to manage UI state and ensure subscriptions can be cleaned up on destroy.
-
-
Also move the PUT request for adding a place to favorites from
AvailablePlacesComponentinto the service viaaddPlaceToUserPlaces(placeId: string), keeping a consistent “all HTTP config in the service” approach. -
Emphasize cleaning up component subscriptions (store and unsubscribe on component destruction).
-
End result: same functionality (data loads as before), but HTTP request setup lives in the service while components focus on subscribing, state handling, and UI updates.
232. Managing HTTP-loaded Data via a Service
The issue is that after adding a favorite place, the
UserPlacesComponent doesn’t update until a reload because it isn’t
being notified of the change.
Proposed approach:
-
Move ownership of
userPlacesstate into the Places service using an existing writable signal (userPlaces) and expose it as a read-only signal (loadedUserPlaces) for components. -
In
loadUserPlaces(), update the service’suserPlacessignal whenever places are fetched:-
Call
pipe()again on thefetchPlaces()observable. -
Use RxJS
tap(imported fromrxjs) to react to the emitted “next” value without subscribing. -
In
tap, set theuserPlacessignal to the fetched places array.
-
Component changes:
-
In
UserPlacesComponent, stop setting its own localplacessignal based on the observable’snext. -
Instead, bind
placesto the service’sloadedUserPlacesread-only signal. -
Keep loading/error/complete handling in the component.
Result so far:
-
Places are loaded and stored centrally in the service, and the component displays them from the service-managed signal.
-
Immediate UI updates when adding a new favorite still aren’t solved yet, but this sets up the structure needed to implement that next.