Creating Actions page

Learn how to create NgRx Actions.

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

Overview

  1. Replace generated Actions with new Login Actions.

  2. Create Logout Actions.

  3. Clean up code involving replaced Actions.

Problem 1: Create Login Actions to Represent Login Events

NgRx schematics generated Actions with the following Action Types: [Login] Load Logins, [Login] Load Logins Success and [Login] Load Logins Failure.

Our goal is to have a different set of Action instead:

  1. login - Dispatched when user submits username and password:

    • type: [Login Page] Login
    • props: { username: string, password: string }
  2. loginSuccess - Dispatched by an Effect when user has successfully logged in:

    • type: [Login API] Login Success
    • props: { userId: string; username: string; token: string }
  3. loginFailure - Dispatched by an Effect when user login attempt fails:

    • type: [Login API] Login Failure
    • props: { errorMsg: string }

P1: What you need to know

Action Types in NgRx follow a string pattern: [Source] Event. Since Actions represent a unique event, an Action Type should be a unique identifier for that event. NgRx encourages Actions to be unique in order to help with application debugging, traceability and maintainability. Here’s a great presentation about Action Best Practices from Mike Ryan, co-creator of NgRx.

createAction() can take up to two arguments: the string type and a config function that represents additional metadata, usually referred as props in Redux Pattern:

Note that many of the code examples in these sections are for instructional purposes only, and aren’t part of our main application repo or solution

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

import { createAction, props } from '@ngrx/store';

export const submit = createAction(
  '[Contact Page] Submit',// type
  props<{ emailAddress: string; fullName: string }>()// props
);

P1: Solution

src/app/store/login/login.actions.ts

// src/app/store/login/login.actions.ts

import { createAction, props } from '@ngrx/store';

export const login = createAction(
  '[Login Page] Login',
  props<{ username: string; password: string }>()
);

export const loginSuccess = createAction(
  '[Login API] Login Success',
  props<{ userId: string; username: string; token: string }>()
);

export const loginFailure = createAction(
  '[Login API] Login Failure',
  props<{ errorMsg: string }>()
);

Problem 2: Create Logout Actions to Represent Logout Events

Next, our goal is to create 3 more Actions for logout:

  1. logout - Dispatched when user clicks on a logout button
    • type: [Dashboard Page] Logout
  2. logoutSuccess - Dispatched by an Effect when the user has successfully logged out
    • type: [Login API] Logout Success
  3. logoutFailure - Dispatched by an Effect when user logout attempt fails
    • type: [Login API] Logout Failure
    • props: { errorMsg: string }

P2: Solution

src/app/store/login/login.actions.ts

// src/app/store/login/login.actions.ts

import { createAction, props } from '@ngrx/store';

export const login = createAction(
  '[Login Page] Login',
  props<{ username: string; password: string }>()
);

export const loginSuccess = createAction(
  '[Login API] Login Success',
  props<{ userId: string; username: string; token: string }>()
);

export const loginFailure = createAction(
  '[Login API] Login Failure',
  props<{ errorMsg: string }>()
);

export const logout = createAction('[Dashboard Page] Logout');

export const logoutSuccess = createAction('[Login API] Logout Success');

export const logoutFailure = createAction(
  '[Login API] Logout Failure',
  props<{ errorMsg: string }>()
);

Cleaning Up Removed Actions

Now that we’ve removed the generated Actions, we will need to update a couple of files so that our application can run:

Also note that this clean up will be required for the upcoming unit tests to pass

First, we’ll remove the generated Effects. And we will remove the unused imports from @ngrx/effects, rxjs/operators, rxjs, and ./login.actions:

src/app/store/login/login.effects.ts

// src/app/store/login/login.effects.ts

import { Injectable } from '@angular/core';
import { Actions } from '@ngrx/effects';

@Injectable()
export class LoginEffects {
  constructor(private actions$: Actions) {}
}

Last, we’ll remove the generated on() handlers in our Reducer function. And we will remove the unused imports from @ngrx/store and ./login.actions:

src/app/store/login/login.reducer.ts

// src/app/store/login/login.reducer.ts

import { createReducer } from '@ngrx/store';

export const loginFeatureKey = 'login';

export interface State {

}

export interface LoginPartialState {
  [loginFeatureKey]: State;
}

export const initialState: State = {

};

export const reducer = createReducer(
  initialState
);

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/create-actions