Using Selectors page

Learn how to use NgRx Selectors in a Component to access data from a store.

Quick Start: You can checkout this branch to get your codebase ready to work on this section.

Overview

  1. Update DashboardComponent’s username$ and userId$ members to use Login Selectors.

  2. Update AuthenticationGuard’s canActivate() method to use Login Selector.

Problem 1: Update username$ and userId$ to use Login Selectors on DashboardComponent

DashboardComponent should use the LoginSelectors.selectUsername and LoginSelectors.selectUserId Selectors for its username$ and userId$ members.

P1: What you need to know

Now that we have our Selectors defined, we can inject Store into our Components and use the select() method to obtain slices of state using our Selectors:

// Note: This example code is not part of our application repo or solution

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import * as ContactSelectors from '../store/contact/contact.selectors';

@Component({
  selector: 'app-contact',
  templateUrl: './contact.component.html',
  styleUrls: ['./contact.component.scss'],
})
export class ContactComponent {
  readonly emailAddress$ = this.store.select(ContactSelectors.emailAddress);

  constructor(private store: Store) {}
}

In the DashboardComponent, there is are TODO’s where the Login Selectors should be used.

P1: Solution

src/app/dashboard/dashboard.component.ts

// src/app/dashboard/dashboard.component.ts

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import * as LoginActions from '../store/login/login.actions';
import * as LoginSelectors from '../store/login/login.selectors';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent {
  // Select username from store
  readonly username$ = this.store.select(LoginSelectors.selectUsername);

  // Select user ID from store
  readonly userId$ = this.store.select(LoginSelectors.selectUserId);

  constructor(private store: Store) {}

  logout(): void {
    this.store.dispatch(LoginActions.logout());
  }
}

Problem 2: Update canActivate() to use Login Selector on AuthenticationGuard

AuthenticationGuard’s canActivate() method should use the LoginSelectors.selectToken Selector.

P2: What you need to know

In the AuthenticationGuard, there is a TODO where the Login Selector should be used.

P2: Solution

src/app/guards/authentication.guard.ts

// src/app/guards/authentication.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, CanLoad, Router, UrlTree } from '@angular/router';
import { Store } from '@ngrx/store';
import { map, Observable } from 'rxjs';
import * as LoginSelectors from '../store/login/login.selectors';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationGuard implements CanActivate, CanLoad {
  constructor(private router: Router, private store: Store) {}

  canActivate(): Observable<UrlTree | boolean> {
    return this.store.select(LoginSelectors.selectToken).pipe(
      map(token => {
        // Allow navigation since there is a login token
        if (token) {
          return true;
        }

        // Redirect back to login page
        return this.router.createUrlTree(['']);
      })
    );
  }

  canLoad(): Observable<UrlTree | boolean> {
    return this.canActivate();
  }
}

Wrap-up: By the end of this section, your code should match this branch. You can also compare the code changes for our solution to this section on GitHub or you can use the following command in your terminal:

git diff origin/use-selectors