Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
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.
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.
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:
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.
Once actions are defined, one may then dispatch them using the @ngrx store. This is usually done within Angular container components:
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.
NgRx Auto-Entity offers many ways to load data. The most common of these will usually be the single, all and many loads. A single entity load is pretty strait forward, requiring that you provide a model type and entity key to the Load
action. This will retrieve the single entity uniquely identified by the specified key and merge it into the state for that entity.
There are several terms I will use throughout this documentation when referring to what kind of changes are performed on state as a result of an action.
Merged: State that is merged will add or update existing entities in state with entities of the action being reduced
Replaced: State that is replaced will drop any existing state in favor of the entities of the action being reduced
Concatenated: State where the entities of the action are simply added to whatever is already in state
Loading all entities of a given type is even more strait forward, as you simply provide the model type to the LoadAll
action. This will retrieve every entity of a given type and replace it in the state for that entity.
The third common way of loading entities is to load many entities. This is also strait forward and relies on providing at least a model type to the LoadMany
action. This will retrieve entities and merge them into the state for that entity.
Note that when the behaviors of actions are described here, this is the expected behavior for a given action. Since implementation of Entity services is still the responsibility of the developer, it is up to the developer top ensure they conform to the expectations of each they are handling in their Entity services.
For many loads, you may need to specify criteria other than a primary or uniquely identifying key. All NgRx Auto-Entity actions support optional custom criteria that may be used for this purpose.
One,All, Many, Pages, Ranges...
The generic actions in the NgRx Auto-Entity library cover all the CRUD bases, as well as a range of loading options, plus some extra useful "utility" actions to cover other common state-related entity tasks. First up, we provide several options for loading data:
Load: one entity at a time
LoadAll: every entity all at once
LoadMany: lots of entities in arbitrary bunches
LoadPage: entities in discrete bunches
LoadRange: entities in sequential bunches
Each load action is imbued with unique semantics and behavioral implications. For example, dispatching a LoadAll
action implies that you wish to replace any previously existing entity state for the given model with whatever set of entities is retrieved by the entity service for that model.
Dispatching LoadMany
on the other hand implies that you wish to keep any previously existing entity state for the given model, and merge in whatever set of entities is retrieved. Similarly, Load
will also merge the retrieved entity into any previously existing state.
Further, pages are semantically different than ranges. A page is a discrete slice of a set of entities with a distinct beginning and end. A range on the other hand is a sequential slice of a set of entities that ultimately form a continuous range. When dispatching LoadPage
the implication is that you wish to replace any previously existing state for the given model.
The explicit use case here is when the entire set of all entities for a given model is simply too large to fit in memory in a browser (i.e. you may have tens of thousands...millions...billions of records in a database.) Standard case for tables with paging controls.
When dispatching LoadRange
on the other hand, the implication is that you wish to join newly loaded ranges of entities onto existing state for the given model. The primary use case here is infinite scrolling, where additional entities are added to previously loaded entities in response to continued vertical scrolling or horizontal panning by a user.
Radical Simplification with Reusable Action Libraries
NgRx Auto-Entity provides a relatively simple solution to the action triplet trifurcation conundrum. Make commonly-implemented actions reusable! If there ever was a use case for generics, CRUD entity actions would seem to be as sublimely perfect a case as ever possible. By making actions generic, this allows a simple library of standard CRUD actions to be implemented that could then be easily reused by any number of unique entities.
Generic actions are the primary interface with which an application interacts with NgRx Auto-Entity. No need to implement the same pattern of three actions again and again for each and every entity! We have provided all the necessary actions for you. You simply need to dispatch them!
Aside from no longer having to implement your own actions, everything else should be business as usual. Your controllers, when dispatching generic actions from Auto-Entity directly, should look quite familiar.
In future versions of NgRx Auto-Entity, legacy-style action classes (i.e. new LoadAll(...)
) will be supplemented by NgRx 8+ style factory functions. Instead of newing up an action, you'll be able to use an action factory pre-typed specifically to your entities:
loadAllCustomers()
createOrder(order)
selectUserByKey(userId)
This stylistic alignment should simplify NgRx Auto-Entity usage and allow deeper integration of auto-entity generated functionality with standard NgRx code, such as reducers created with createReducer()
. (These changes are currently slated for version 0.6)
Each generic action's constructors require a standard set of parameters, and some actions may require unique parameters to perform the necessary function. Most action constructors are as follows: