The NgRx Component Store is neat!
Tags: angular, ngrx, typescript
Published: 2023-12-18 21:10:41

---
title: The NgRx Component Store is neat!
published: true
description: The NgRx component store is a clever bit of code. This article demonstrates how I've used it in a recent project
tags: angular, ngrx, typescript
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-18 19:16 +0000
---
[See this project on Stackblitz](https://stackblitz.com/edit/aurelia-ngrx-component-store-example?file=src%2Fmain.ts)

Lately I've been messing around with the [NgRx Component Store](https://ngrx.io/guide/component-store), which is a sort of stripped down version of their [Store](https://ngrx.io/guide/store) software. The central idea is that your Angular application (or in my case, component) has a central data store class that manages the application / component state. What is "state" in this context? It's all the data that your application / component works with. The larger application that I'm currently working on uses worker processes to handle certain long-running jobs, so in my case, the state of my component is the execution status of those jobs, as reported by an HTTP backend.

A job status record has the following fields:
* `worker` - the name of the worker that is recording this status entry. For example, if you had a worker that was transcoding a video stream, you might call it "transcode"
* `job` - The name of the job the worker is executing. Using the previous example of transcoding a video stream, you might call it "home-movie-1994-11-17.mpg".
* `jobType` - This used to give more information about the job. Using the previous example, you might use "mpg_to_mp4"
* `jobStatus` - This is used to record the status of the job. For the previous example, you might could put a completion percentage here. You could also use something like "Received", "In progress", "Complete" for jobs that aren't easily broken down into percent complete.
* `error` - This is an optional field, only populated when something's gone wrong with the job. It will contain a (hopefully useful) error message.

To display these job status records, let's put them in a table. 
Before I learned about NgRx, I would have gone about writing this component by first writing a [service class](https://angular.io/guide/architecture-services) that mediated the interaction with the backend. I then would have generated a [component](https://angular.io/guide/architecture-components) that interacted with the service class and fed data to the HTML template. If I wanted to be able to filter my results, I would have added form fields to the HTML template and added listeners on each form field's `onChange` event. The component class would be fairly large in terms of lines of code with all these listeners and whatnot, and I'd have to do some jiggery-pokery with [RxJs](https://rxjs.dev/) to get everything plumbed up properly. I would also have had to manage the [Subscriptions](https://rxjs.dev/guide/subscription) for each form field to make sure they all got unsubscribed whenever the component was destroyed. And that's all stuff I've done before, but it's kind of a lot.

With NgRx, however, this all becomes much easier. I create the service class as before, but then I create a [component store](https://ngrx.io/guide/component-store). This component store is going to manage *all* of the data for my component. If I want the data laid out a certain way (in my case, I wanted a unique list of worker names, job IDs, job types, and job statuses), the code to do that will be in the component store. 

{% embed https://stackblitz.com/edit/aurelia-ngrx-component-store-example?embed=1&file=src%2Fworker-job-status%2Fworker-job-status.store.ts %}

The most important thing in this class is the definition of `WorkerJobStatusState`:

```
export interface WorkerJobStatusState {
  statuses: WorkerJobStatus[];
  worker: string[];
  job: string[];
  jobType: string[];
  jobStatus: string[];
}
```

This encapsulates the *entire* state of the component at any given time. Notice how it doesn't just have the list of status records. The values of each of the filters are part of the state as well. 

The real beauty of a component store, I think, comes in the selectors, which are pure functions providing a custom view of the component state. They can be composed together, as shown in the `selectedStatuses$` selector, below:

```
readonly selectedStatuses$ = this.select(
    this.statuses$,
    this.selectedWorker$,
    this.selectedJob$,
    this.selectedJobType$,
    this.selectedJobStatus$,
    (
      statuses,
      selectedWorkers,
      selectedJobs,
      selectedJobTypes,
      selectedJobStatuses
    ) =>
      statuses.filter(
        (status) =>
          (selectedWorkers.length
            ? selectedWorkers.includes(status.worker)
            : true) &&
          (selectedJobs.length ? selectedJobs.includes(status.job) : true) &&
          (status.jobType && selectedJobTypes.length
            ? selectedJobTypes.includes(status.jobType)
            : true) &&
          (selectedJobStatuses.length
            ? selectedJobStatuses.includes(status.jobStatus)
            : true)
      )
  );
```

20 lines of code handles *everything* around the status records. We take in the list of statuses provided by the backend service and the values of the filters and we return a list of matching statuses.

Now let's look at the component class for the status list. Notice how the only external class it exchanges data with is the store, and notice how it doesn't do *any* massaging / modification / filtering of the data the store provides. All of that is handled by the store.

{% embed https://stackblitz.com/edit/aurelia-ngrx-component-store-example?embed=1&file=src%2Fworker-job-status%2Fworker-job-status-list%2Fworker-job-status-list.component.html %}

We need to connect the filter form fields to the store, and we do that with these [updater methods](https://ngrx.io/guide/component-store/write#updater-method), which are methods provided by the component store allowing the caller to update the state. 

```
    this._workerJobStatusStore.workersSelected(
      this.selectedWorker.valueChanges
    );
    this._workerJobStatusStore.jobsSelected(this.selectedJob.valueChanges);
    this._workerJobStatusStore.jobTypesSelected(
      this.selectedJobType.valueChanges
    );
    this._workerJobStatusStore.jobStatusesSelected(
      this.selectedJobStatus.valueChanges
    );
```

Note that all we have to do in the way of plumbing is to pass the `valueChanges` Observable from each of the filtering form fields to the updater functions. NgRx will handle everything else for us, including unsubscribing when the component that instantiated the store is destroyed.

Now, if you look at the component code, you will notice that it doesn't do anything to connect the component store to the backend service. The component has no idea there even *is* a backend service, and that's intentional. The worker job status list component is intended to be purely presentational. The data store is provided by [dependency injection](https://angular.io/guide/dependency-injection-overview), but how, you may ask, does the store get connected to the backend service? That gets handled in the container component:

{% embed https://stackblitz.com/edit/aurelia-ngrx-component-store-example?embed=1&file=src%2Fworker-job-status%2Fworker-job-status-container%2Fworker-job-status-container.component.ts %} 

The reason I use a container component here is so that I have a single module that provides the data store and populates the state. If I want to add a status detail component later, one that maybe would provide the logs for each job, I could add another presentational component to do that, but the data store would still be provided by the container.

OK, so I've added a lot of extra abstraction to what could have been a fairly simple component. Why? What good will this do me? I'll tell you:

* Testability - by abstracting the data interaction into the data store class, I can test those methods without having to worry about setting up the component in my test harness or mocking the backend service
* Maintainability - By centralizing common functionality into a single class, I don't have to (for example) populate the data store for every component.
* Extensibility - It's easy to add a new component that uses the same data store.

And I think it makes the code more readable. The job status list component only handles displaying the list. The container component only handles populating the data store. The data store class only handles viewing / modifying the state. Sure, this was a fairly simple example, but for more complex components, this could be invaluable.

So what do you think? Have you worked with component store / NgRx at all? What were your experiences like?