NgRx Auto-Entity
Primary version
Primary version
  • NgRx Auto-Entity
  • Getting Started
    • Installation
    • Quick Start
    • Use your State!
      • Enhancing your Facade
      • Simplify your Component
    • From Scratch
      • App Interfaces
      • App Reducer
      • App State Module
      • Entity Model
      • Entity State
      • Update App State
      • Entity Service
      • Update App Module
  • Advanced Topics
    • Advanced Usage
      • Paradigm Changes
        • Models
        • Services
        • Service Providers
      • Taking Control
        • Integrating Custom Effects
      • Building Your Entities
        • Entity Names
        • Sort Comparers
        • Data Transforms
      • Building Your Entity States
        • The buildState() function
        • The buildFeatureState() function
        • The IEntityState Interface
        • The Selector Map
      • Generic Actions
        • Actions Now
        • Reusable Generic Actions
        • Custom Criteria
        • Loading Actions
          • Loading Entities
          • Loading Pages
          • Loading Ranges
        • Optional Loading
        • CURD Actions
        • Utility Actions
      • Correlation
      • Common Selectors
        • Exporting Selectors
      • Extra Selectors
      • Custom Selectors
        • Adding to Facades
        • Using Custom Selectors
      • Custom Effects
        • Composing Actions
        • Workflows
    • Leveraging Facades
      • Preparing Facades
      • The Interface: Selections
        • Using Facade Selections
        • Simplifying Further
      • The Interface: Activities
        • Using Facade Activities
      • So Little Code!
    • Utility Functions
      • Prototyping your Entities
        • Entity Making Performance
      • Entity Name Utilities
      • Entity Key Utilities
      • Entity Comparers
  • Examples
    • Sample Application
      • App Module
      • State
      • Models
      • Services
      • Facades
      • Container Components
      • Presentation Components
      • Modal Component
  • Documentation
    • Reference
  • Extras
    • Github Link
Powered by GitBook
On this page
  • The createAction Factory
  • Dispatched from Components
Export as PDF
  1. Advanced Topics
  2. Advanced Usage
  3. Generic Actions

Actions Now

Current Complexity with Action Definitions

A standard approach to implementing actions with @ngrx requires defining an enumeration of action types, which map a code identifier to a string, implementation of an action class that derives from Action and the concatenation of each action type into an action union that allows proper implementation of a reducer function to reduce actions and the information they contain into your state.

customer.actions.ts
export enum CustomerActionTypes {
    CREATE_CUSTOMER = '[Customer] Create',
    CREATE_CUSTOMER_SUCCESS = '[Customer] Create: Success',
    CREATE_CUSTOMER_FAILURE = '[Customer] Create: Failure',
    LOAD_ALL_CUSTOMERS = '[Customer] Load: All',
    // ... additional types ...
}

export class CreateCustomer implements Action {
    readonly type = CustomerActionTypes.CREATE_CUSTOMER;
    
    constructor(public payload: Customer) {}
}

export class CreateCustomerSuccess implements Action {
    readonly type = CustomerActionTypes.CREATE_CUSTOMER_SUCCESS;
    
    constructor(public payload: Customer) {}
}

export class CreateCustomerFailure implements Action {
    readonly type = CustomerActionTypes.CREATE_CUSTOMER_FAILURE;
    
    constructor(public payload: Error | any) {}
}

export class LoadAllCustomers implements Action {
    readonly type = CustomerActionTypes.LOAD_ALL_CUSTOMERS;
}

// ... additional actions ...

export union CustomerActions = 
      CreateCustomer
    | CreateCustomerSuccess
    | CreateCustomerFailure 
    | LoadAllCustomers
     // ... additional actions to union ...;

Lot of work!

This is a lot of work required just to add the ability to create an entity. Not only do you need the ability to handle the initial create request, but also deal with the success or failure of that request, at the very least. So each "action" generally tends to trifurcate, and thus the action types and action union code trifurcates as well.

The createAction Factory

With the release of NgRx 8, several utility functions, factory functions, were introduced and can help reduce some of the "boilerplate" nature of implementing actions, effects and reducers. These new functions are a welcome improvement over prior versions of NgRx...however, we do believe they still fall short of providing the kind of simplified, rapid development experience Auto-Entity provides for entity use cases.

For custom use cases, we are in fact huge fans of the new factory functions in NgRx. They provide a much cleaner approach to using NgRx in general, for actions, effects, etc. Even with automatic entities, applications that fully rely on NgRx for the bulk of the application logic will require many additional actions, effects, selectors, reducers, etc. We strongly encourage their use whenever you do not need boilerplate entity support!

Using the createAction factory, we can reduce the previous boilerplate to the following:

export const createCustomer = createAction(
    '[Customer] Create',
    props<{customer: Customer}>()
);

export const createCustomerSuccess = createAction(
    '[Customer] Create: Success',
    props<{customer: Customer}>()
); 

export const createCustomerFailure = createAction(
    '[Customer] Create: Failure',
    props<{error: Error | any}>()
);

export const loadAllCustomers = createAction(
    '[Customer] Load: All'
);

A definite reduction in complexity, and a small reduction in overall code volume. However, actions must still be created in order to handle entity loading/success/failure in NgRx. And, similar sets of actions must be created for each and every entity you need to use in your app.

Dispatched from Components

Once actions are defined, one may then dispatch them using the @ngrx store. This is usually done within Angular container components:

customers.component.ts
import {Customer} from 'models';
import {createCustomer, loadAllCustomers} from 'state/customer.actions';
import {allCustomers} from 'state/customer.selectors';

@Component(...)
export class CustomersComponent implements OnInit {
    customers$: Observable<Customer[]>;
    
    constructor(
        private activatedRoute: ActivatedRoute, 
        private store: Store<AppState>
    ) {}
    
    ngOnInit() {
        this.customer$ = this.store.pipe(select(allCustomers));
        this.refresh();
    }
    
    addCustomer(customer: Customer) {
        this.store.dispatch(createCustomer({customer}));
    }
    
    refresh() {
        this.store.dispatch(loadAllCustomers());
    }
    
    // ...
}

For actions to actually do anything, however, you need more. Your work does not end here. You still need effects, and reducers, to make any of these dispatched actions actually perform useful work and update state.

PreviousGeneric ActionsNextReusable Generic Actions

Last updated 4 years ago