Skip to content

Latest commit

 

History

History
148 lines (101 loc) · 7.32 KB

entity-collection-service.md

File metadata and controls

148 lines (101 loc) · 7.32 KB

EntityCollectionService

An EntityCollectionService<T> is a facade over the ngrx-data dispatcher and selectors$ that manages an entity T collection cached in the ngrx store.

The Dispatcher features command methods that dispatch entity actions to the ngrx store. These commands either update the entity collection directly or trigger HTTP requests to a server. When the server responds, the ngrx-data library dispatches new actions with the response data and these actions update the entity collection.

The EntityCommands interface lists all the commands and what they do.

Your application calls these command methods to update the cached entity collection in the ngrx store.

Selectors$ are properties returning selector observables. Each observable watches for a specific change in the cached entity collection and emits the changed value.

The EntitySelectors$ interface lists all of the pre-defined selector observable properties and explains which collection properties they observe.

Your application subscribes to selector observables in order to process and display entities in the collection.

Examples from the demo app

Here are simplified excerpts from the demo app's HeroesComponent showing the component calling command methods and subscribing to selector observables.

constructor(EntityCollectionServiceFactory: EntityCollectionServiceFactory) {
  this.heroService = EntityCollectionServiceFactory.create<Hero>('Hero');
  this.filteredHeroes$ = this.heroService.filteredEntities$;
  this.loading$ = this.heroService.loading$;
}

getHeroes() { this.heroService.getAll(); }
add(hero: Hero) { this.heroService.add(hero); }
deleteHero(hero: Hero) { this.heroService.delete(hero.id); }
update(hero: Hero) { this.heroService.update(hero); }

Create the EntityCollectionService with a factory

The component injects the ngrx-data EntityCollectionServiceFactory and creates an EntityCollectionService for Hero entities.

We'll go inside the factory later in this guide.

Create the EntityCollectionService as a class

Alternatively, you could have created a single HeroEntityService elsewhere, perhaps in the AppModule, and injected it into the component's constructor.

There are two basic ways to create the service class.

  1. Derive from EntityCollectionServiceBase<T>
  2. Write a HeroEntityService with just the API you need.

When HeroEntityService derives from EntityCollectionServiceBase<T> it must inject the EntityCollectionServiceFactory into its constructor. There are examples of this approach in the demo app.

When defining an HeroEntityService with a limited API, you may also inject EntityCollectionServiceFactory as a source of the functionality that you choose to expose.

Let your preferred style and app needs determine which creation technique you choose.

Set component selector$ properties

The component sets two of its properties to two of the EntityCollectionService selector observables: filteredEntities$ and loading$.

The filteredEntities$ observable produces an array of the currently cached Hero entities that satisfy the user's filter criteria. This observable produces a new array of heroes if the user changes the filter or if some action changes the heroes in the cached collection.

The loading$ observable produces true while the data service is waiting for heroes from the server. It produces false when the server responds. The demo app subscribes to loading$ so that it can turn a visual loading indicator on and off.

Note that these component and EntityCollectionService selector property names end in '$', a common convention for a property that returns an Observable.

All selector observable properties of an EntityCollectionService follow this convention. For brevity, we'll refer to them going forward as selector$ properties or selectors$.

Note that these selector$ properties (with an 's') differ from the closely-related selector properties (no '$' suffix), discussed elsewhere.

A selector property returns a function that selects from the entity collection. That function is an ingredient in the production of values for its corresponding selector$ property.

The component class does not subscribe these selector$ properties but the component template does.

The template binds to them and forwards their observables to the Angular AsyncPipe, which subscribes to them. Here's an excerpt of the filteredHeroes$ binding.

<div *ngIf="filteredHeroes$ | async as heroes">
...
</div>

Call command methods

Most of the HeroesComponent methods delegate to EntityCollectionService command methods such as getAll() and add().

There are two kinds of commands:

  1. Commands that trigger requests to the server.
  2. Cache-only commands that update the cached entity collection.

The server commands are simple verbs like "add" and "getAll".
They dispatch actions that trigger asynchronous requests to a remote server.

The cache-only command methods are longer verbs like "addManyToCache" and "removeOneFromCache" and their names all contain the word "cache". They update the cached collection immediately (synchronously).

Most applications call the server commands because they want to query and save entity data.

Apps rarely call the cache-only commands because direct updates to the entity collection are lost when the application shuts down.

Many EntityCollectionService command methods take a value. The value is typed (often as Hero) so you won't make a mistake by passing in the wrong kind of value.

Internally, an entity service method creates an entity action that corresponds to the method's intent. The action's payload is either the value passed to the method or an appropriate derivative of that value.

Immutability is a core principle of the redux pattern. Several of the command methods take an entity argument such as a Hero. An entity argument must never be a cached entity object. It can be a copy of a cached entity object and it often is. The demo application always calls these command methods with copies of the entity data.

The current ngrx libraries do not guard against mutation of the objects (or arrays of objects) in the store. A future ngrx freeze feature will provide such a guard in development builds.

All command methods return void. A core principle of the redux pattern is that commands never return a value. They just do things that have side-effects.

Rather than expect a result from the command, you subscribe to a selector$ property that reflects the effects of the command. If the command did something you care about, a selector$ property should be able to tell you about it.

EntityServiceFactory

The create<T>() method of the ngrx-data EntityCollectionServiceFactory produces a new instance of the EntityCollectionServiceBase<T> class that implements the EntityCollectionService interface for the entity type T.