Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Customers and Orders
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Encapsulate!
In our CustomerComponent
, there are a few methods that exhibit "class envy" which is a kind of anti-pattern. For those not familiar with the concept, class envy occurs when methods of one class are more dependent on functionality within another class. It then becomes prudent to move the method into the class of envy, and if necessary parameterize any input required from the method's previous home.
Our CustomerComponent
has two potential candidates for encapsulation within our CustomerFacade
class: hasCustomer
and onSave. We can easily move this functionality into our facade class and make these behaviors reusable in any component that may require interaction with customer entity state:
Entity facades include a considerable amount of ready-to-go functionality. Check out the advanced facade documentation here to learn more about everything our facades provide and how to extend them.
That junk don't belong here!
Once you have enhanced your facade with functionality that belongs in the facade and not the component, it's time to clean up your component. Using the new functionality we have implemented in our customer facade, our component can become reduced to a simpler form:
Leverage the power of facades
Now that you have set up state for an entity, it is time to start using it. With NgRx Auto-Entity, if you leverage our pre-fabricated facades, we have made using state about as easy as it can get. Start by creating a facade class that derives from the facade base class generated by your call to buildState
:
With your facade in hand, inject it into your component and use the facade to interact with your entities:
Note the changes here. We imported only the activated route and a facade into our component. Our component does not import any state-related types at all. No actions, no store, no app state interface, none of the usual suspects. All state interactions occur through the facade.
Getting started with NgRx Auto-Entity is easy!
If you are already familiar with NgRx, then adding Auto-Entity to your Angular application is very easy. There are four major steps required to add the module, create new entity state, and provide your entity services.
First things first, you must bring in the NgrxAutoEntityModule
into your app module and call the forRoot()
method to configure the library.
Note the use of strictActionSerializability
being set to false
here. This is an important setting with NgRx Auto-Entity. This library uses special actions that reference classes, which are types. Not instances of those classes, which would be objects (data), but the classes themselves. Classes, being types, are not serializable, which prevents auto-entity actions from being compatible with strict action serializability in NgRx.
Providing the type allows Auto-Entity to gain rich knowledge about each entity, including any metadata/config you may attach to your entities with the @Entity
and @Key
decorators.
Before you can actually create your state, you will need to create your entity models, as you would normally do. Auto-Entity requires two small changes to how you create models for NgRx.
First, your models must be classes rather than interfaces (see advanced documentation for more info.) Second, your entity identity must be decorated with the @Key
directive. For entities with composite keys, simply decorate each property that is part of the key.
You will also need to create an entity service to handle CRUD behavior for each entity. Entity services may be shared if your API uses a common pattern. Otherwise you may need to implement a service for each entity as you usually do with NgRx.
In the example above we have a simple shared entity service that supports a basic REST API where each entity conforms to a simple pattern:
/<rootUrl>/<entityName>[/<key>]
Auto-Entity provides basic entity metadata, such as the model name, in the entityInfo
parameter of each entity service method.
Once you have created an entity service or services, you will need to provide them in your app module. With Auto-Entity, services must be provided in a slightly different manner than normal, to ensure that Auto-Entity is able to find entity services dynamically.
In our example here, we are sharing a single entity service, EntityService
, for all entities. We must provide
each model and useClass
to specify the service class to use.
Finally, now that you have your models and have provided your entity services, you need to build your state for each model. Add a new state file for each model following the pattern depicted here:
Finally, include your entity states in the AppState interface, and your stub reducers in the action reducer map:
With that, you are ready to start using your automatic entity state! Continue on to the next section to learn how.
Adding NgRx & Auto-Entity to an App
If you have not used NgRx before, and need to start from scratch, this guide should get you going. Let's start by creating a state module. We recommend creating this module in a root state directory, located at src/app/state. In this directory, create a new state.module.ts file:
Import the NgRx StoreModule
and EffectsModule
as well as the Auto-Entity NgrxAutoEntityModule
. Make sure you call the .forRoot()
initializer on each of them to ensure they are properly imported. It is important that the NgrxAutoEntityModule be brought in after the EffectsModule, as this ensures all automatic effects will be properly registered.
The NgRx Auto Entity module is now imported in your application, giving you access to ready-made generic actions, automatic effects, pre-defined reducers and prefabricated facade classes to handle the bulk of your CRUD meeds.
Next, we need to set up the state of our application.
Simplifying Reactive State!
NgRx Auto-Entity is a library that simplifies reactive state in @ngrx
with reusable generic actions, automated effects & reducers and prefabricated facades.
NgRX Auto-Entity is an add-on library for @ngrx
that aims to greatly simplify use of @ngrx
and reduce the implementation load of adding @ngrx
to your application. We provide a set of ready-made, generic actions that cover most of the common use cases such as loading entities and sets of entities as well as creating, updating or replacing, and deleting entities.
Auto-Entity wires in ready-made effects to handle standard behaviors, as well as provides a core meta reducer that reduces all entity state managed by this library. For most use cases, the implementation burden for you the developer will be limited to basic initial @ngrx
setup, creation of models for each entity & creation of services for each entity. This is functionality that would need to be implemented by you the developer regardless.
Where we save you time is by eliminating the need to implement unique sets of CRUD actions for each and every entity, as well as the side effects that handle standard CRUD behavior for each and every entity. This can be a significant amount of development effort for each @ngrx
application, and we hope the savings we offer will allow you to focus on solving the critical business needs of your customers.
Further, Auto-Entity generates pre-fabricated Facades around your entity state, allowing you to extract all @ngrx
and other state related code from your components, encapsulate them in a facade layer that presents a much simpler, more logical and easier to comprehend and use API for your entities.
To get started, install the package using npm or yarn:
The following peer requirements must also be installed when using NgRx Auto Entity:
A quick note about Angular 10. While NgRx Auto-Entity has been tested with Angular 10, not every use case exhibited flawless support. There have been use cases where some uses of Auto-Entity with Angular 10 have worked just fine, and others that seemed to have issues. So we tentatively support Angular 10, but official guaranteed support is still pending. As such, we do not recommend you use Auto-Entity with Angular 10 in production environments at this time.
Further testing of Auto-Entity with Angular 10 is ongoing, and outstanding issues will be resolved as they are discovered. One of our key goals with Auto-Entity is to remain backwards compatible with prior versions of Angular as best we can. Due to certain TypeScript requirements, our minimum supported versions are Angular 8 and NgRx 8. We hope this will expand Auto-Entity viability to the broadest range of enterprise customers with established Angular projects, as well as cutting edge projects on the bleeding edge. Our compatibility goals may require more complex build, packaging and deployment in the future, and there may come a time when our minimum versions must change to keep abreast of the Angular platform.
For now, Angular 10 support is tentatively there. If you run into issues, let us know by opening a ticket in our GitHub repo:
Define the initial entity state
In our example we are building the state for the Customer
entity. As such, we've created a new customer.state.ts file located at src/app/state/customer.state.ts.
Import the buildState
function from the ngrx-auto-entity module. This function builds the initial state and selectors for each entity. Call the function by passing in the Customer entity class (note, the class must be passed in!) We use object destructuring on the return type access the initialState
, selectors
and facade
base class from the result of buildState
.
We can now further destructure the selectors object to map each type of standard selector to model-specific names for import into other modules:
selectAll
selects the Array
of entities
selectEntities
selects the Dictionary
of entities
selectIds
selects the Array
of entity identifiers (keys)
selectTotal
selects the number
of entities
Note that retrieving and exporting the selectors are optional if you extract the facade. The facade base class generated by buildState fully encapsulates all of the functionality you will need to interact with your entity state, and we generally recommend only using the facade. Demonstration of how to access selectors directly, such as in the event that you may need to create your own custom selectors, is simply for completeness here.
There are many additional selectors bundled as part of each custom entity state built by buildState
that may be mapped to state-specific names. Read more in the advanced documentation.
When the selectors object is destructured we alias the selectors with entity-friendly names to avoid naming conflicts with other exported names. This prevents the need to import entire files with an import * as fromBlah from 'blah'
syntax. Uniquely named exports are enough, and allow selective import into each area of the app.
Finally, we define the customerReducer
function. The reducer function accepts two arguments: state
, which defaults to the initialCustomerState
, and the action
.
A reducer function is necessary to configure the NgRX standard actionsReducer
we defined earlier. For most entities, you will not need to do anything other than return the state passed in, as the autoEntityMetaReducer
will handle reduction for you. If custom reduction is required for your apps, it may be handled in these reducers.
Create application state interface.
As with any NgRX application, we need to create a new app.state.ts file. We recommend creating is file in a root state directory, located at src/app/state. Import the IEntityState
interface from @briebug/ngrx-auto-entity as well, as we will be using it later.
Define the AppState
interface as normal. We will eventually define each entity state property here. For now, we've left it blank, and will fill it in later on in this quick start.
Remember to export the AppState
type so that it may be used throughout the rest of your app.
Create application state module.
Now that we have implemented our root state interface & reducer map, we need to update the state module we created in the first step:
Update the import of the StoreModule
. In the forRoot()
static method, specify the appReducer
and (if necessary) appMetaReducers
from your previously created app.reducer
as with any normal NgRX app.
If you have any custom effects you may have implemented, include those classes in the EffectsModule
import's forRoot()
call. Effects are optional with NgRx Auto-Entity, so if you have no effects just pass an empty array.
Update the application state interface.
Now that we have the standard initial implementation for NgRX and Auto-Entity in place, we need to wire our models into our state.
Note that we have added a new customer
property to the IAppState
interface of type IEntityState<Customer>
, which we imported from @briebug/ngrx-auto-entity
at the top of the file.
For most basic CRUD states, you will not need to implement any custom state interfaces, effects or reducers. This is the simplicity that NgRX Auto-Entity brings to the table!
After we create the entity reducer, we'll also need to update the appReducer
constant to include the customer
property (with the customer reducer function as the value). It's important that both properties have the same name.
Create application reducer and meta reducer.
Also like normal NgRX apps, add a reducer map to your app.state.ts file. We recommend creating this file in a root state directory, located at src/app/state.
In versions of NgRx Auto-Entity prior to v0.2, the developer was also responsible for including the autoEntityMetaReducer
in the app meta reducers collection. As of version 0.2 of the library, import of the NgrxAutoEntityModule
with the .forRoot()
call is all that is necessary to include the meta reducer.
If you are upgrading from a version prior to v0.2, you should remove the autoEntityMetaReducer
from your app meta reducers!
With NgRx 8 and up, runtime checks have replaced the need to use the storeFreeze meta reducer. As such, a standard state configuration no longer requires any meta reducers.
Create your entity models.
In a departure from classic @ngrx/entity models, each model in your application should be defined as a class (see note below). Here is an example of a Customer
model:
Next we need to import the Key
and Entity
decorators. The Key
decorator is used to specify the property in your model that is the unique identifier. Decorate the id
property, which is the unique identifier for Customer
model. Read more about entity keys in the advanced documentation.
The Entity
decorator is used to attach metadata to your entity model that the NgRx Auto-Entity library can use to perform many of its automated tasks. In version 0.5 of the library, only the modelName
must be specified. Read more about the entity decorator in the advanced documentation.
Note that the model must be a class and not an interface. This is because interfaces are a compile-time only feature of TypeScript, while classes are a native runtime construct in JavaScript. Further, the modelName
must be defined on the entity, as this is the name that the library will use at runtime for minified/uglified code (critical, read more in advanced documentation.)
Module
Minimum Required Version
Supported Versions
@angular/common
8.0.0
8.x, 9.x, 10.x*
@angular/core
8.0.0
8.x, 9.x, 10.x*
@ngrx/effects
8.0.0
8.x, 9.x, 10.x
@ngrx/store
8.0.0
8.x, 9.x, 10.x
rxjs
6.0.0
6.x
Understand changes to model implementation
With NgRX Auto-Entity, a very small change must be made to how models are implemented. Standard @ngrx models are implemented as interfaces. A normal model used with @ngrx/entity may look like this:
This simple model would then be wired into @ngrx/entity using the adapter, with a selectId handler to allow entity framework to determine what the primary key of the entity is.
Due to the dynamic nature of how NgRX Auto-Entity works, interfaces are insufficient as they are compile-time only types in TypeScript. All of their information is ultimately stripped from the transpiled and optimized JavaScript code that you will ultimately release to a web server. To combat this issue, we must convert our entities to classes, which are a native runtime feature of JavaScript.
In addition to converting our model from an interface to a class, notice that we have also imported Key
from @briebug/ngrx-auto-entity
and decorated our id
property with @Key
. This is how the Auto-Entity framework is able to determine which property of the model represents the primary key. As with @ngrx/entity, this key is used to organize and look up entities stored in state.
Note that Auto-Entity supports composite keys. Simply decorate each property that participates in a primary key with the @Key
decorator if necessary.
A simple but common example of a composite key might be something along the lines of an order line item, which references both an order and a product along with a quantity:
For more detail about how composite keys work, read the advanced usage documentation on models and keys.
You should also notice that we have decorated our entity with the Entity
decorator, imported from @briebug/ngrx-auto-entity
. This decorator allows us to associate important metadata with each of our entities. Currently, the only required metadata is the modelName
which allows us to define a minification/uglification-safe identifier for our entity models.
The Entity
decorator allows the definition of other metadata for our entities, including alternative names for specific use cases, a sort comparer, data transformations, and other things. For more detail about entity metadata, read the advanced usage documentation on models.
Learn how to make NgRX Auto-Entity for you
NgRX Auto-Entity aims to provide a seamless means of utilizing a standard set entity actions and effects with minimal repetitive code requirements, while preserving the fundamental nature of NgRX itself. This library is not a replacement for or alternative to NgRX. It works within the standard paradigm that NgRX has set forth, making use of actions, reducers & effects like any other NgRX application.
What Auto-Entity does do is provide a set of ready-made, generic actions for handling all of the standard CRUD operations for entities, so you neither have to write nor generate any of that code yourself. Auto-Entity presents a flexible framework that you may use in its entirety for all of your entity needs, or use piecemeal as necessary in order to achieve your specific goals.
High Performance and Efficiency
Auto-Entity has been implemented with high performance in mind, and is capable of reducing very large data sets. We have tested NgRx Auto-Entity with up to millions of records in state, while adding millions more, with reduction times in the fractions of a second. We do not generally recommend loading such significant volumes of data into your Angular applications, and for more common usage at much lower volumes, you should encounter no performance limitations with Auto-Entity.
Should your application need to work with millions of entities in state in the browser, NgRx Auto-Entity has been tested under high entity volumes. With tens to hundreds of thousands of entities, reduction times should be sub-second, although subject to memory limitations. For millions of entities, performance may vary with memory, and under limited memory situations reductions of very large volumes of entities may require more time. Tests with MacBook Pros with 16 gigabytes of ram, reduction of 2 million entities takes 0.5 to 2 seconds under normal usage.