User Authentication
When the backend is ready, it's time for frontend. In this lesson you will integrate user API with Angular. You will implement the user authentication flow.
Once the application's backend and the API are ready, it's time to go forward and build the frontend Angular application.
To be able to retrieve data using the API, remember to keep your backend built and running:
npm start
You are now about to implement the user authentication flow:
The
FavoritesComponent
component will display the user's favorite products.The
AuthGuard
service will protectFavoritesComponent
from unauthorized access.In case of unauthorized access, the user will be redirected to
LoginComponent
.
To generate these components and services, type the following commands in a new terminal window:
ng g s user --skipTests
ng g s auth-guard --skipTests
ng g c login --skipTests --inlineStyle --inlineTemplate
ng g c favorites --skipTests --inlineStyle --inlineTemplate
Adjusting AppModule#
The code you are about to implement will depend on two modules that AppModule
does not import by default: HttpClientModule
and ReactiveFormsModule
.
To fix this, add two import statements to src/app/app.module.ts:
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
Then update the imports
table:
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
ReactiveFormsModule,
],
User service#
Before you start coding UserService
, you need to implement the User
model that it will be using.
Create a new folder, src/model/. Inside this folder, add a new file, user.model.ts, with the following code:
export interface User {
email: string;
favorite: string[];
}
Now you can start implementing UserService
. Start with declaring class fields, injecting services that you'll need, and checking if the user is authenticated. Add the following code to src/app/user.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {
map,
catchError,
tap,
distinctUntilChanged,
mergeMap,
filter,
take,
} from 'rxjs/operators';
import {
Observable,
of,
Subject,
BehaviorSubject,
} from 'rxjs';
import { User } from '../model/user.model';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class UserService {
private API_URL = 'http://localhost/api';
private currentUser$: Subject<User> = new BehaviorSubject(
null
);
private redirectUrl: string = '/';
private redirectParams: any = null;
constructor(
private http: HttpClient,
private router: Router
) {
this.currentUser$
.pipe(
distinctUntilChanged(),
filter((user) => !user),
mergeMap(() => this.checkCookie())
)
.subscribe();
}
private checkCookie(): Observable<void> {
return this.http
.get(`${this.API_URL}/isLoggedIn`, {
withCredentials: true,
})
.pipe(
catchError(() => of(null)),
tap((user) => this.currentUser$.next(user))
);
}
The code above introduces four variables:
API_URL
is the URL of the API that the service is going to communicate with.currentUser$
is anObservable
representing the currently authenticated user, ornull
if there is no authenticated user.redirectUrl
is a placeholder thatAuthGuard
uses to define where the user should be redirected after successful authentication.redirectParams
represents path params of the URL above.
The constructor injects two services:
HttpClient
is used to perform HTTP calls to the API.Router
is used to redirect the user to the desired URL after successful authentication.
Within the constructor, you've piped the currentUser$
observable with a set of operators that includes a call to the checkCookie()
function whenever the value of currentUser$
changes to null
.
Notice that you've called the subscribe()
method of the currentUser$
object. You should be aware of keeping opened subscriptions in your code, because doing this may lead to memory leaks.
Keeping a subscription open is only acceptable here because we're dealing with a service. Thanks to the Dependency Injection mechanism, you are guaranteed that you will only have one instance of this class (UserService
) in your application.
As to components, you should avoid opening subscriptions to them whenever you can. Components can be instantiated multiple times during your application lifecycle, which makes them difficult to manage. If you have an Observable
in a component, it is much better to pass it to the template and let an async
pipe handle subscribing to and unsubscribing from it.
This page is a preview of The newline Guide to Angular Universal