• Stars
    star
    216
  • Rank 183,179 (Top 4 %)
  • Language
    TypeScript
  • Created over 6 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Sample app that can be expanded to use ngrx-data

NgRx Data Lab

Want to learn how to use NgRx without all of the boilerplate? Try out ngrx-data with our quickstart!

QuickStart

This quick start begins with a working angular app that has CRUD operations for heroes and villain entities. This app uses traditional services and techniques to get and save the heroes and villains. In this quick start you will add NgRx and ngrx-data to the app.

What are we doing? Great question! We're going to start with a reactive Angular app and add ngrx to it, using the ngrx-data library.

Let's go

Try these steps yourself on your computer, or if you prefer follow along here on StackBlitz.

If you don't want to try the steps yourself, you can jump right to the solution by cloning the finish branch.

Step 1 - Get the app and install ngrx

The app uses a traditional data service to get the heroes and villains. We'll be adding ngrx and ngrx-data to this application.

git clone https://github.com/johnpapa/ngrx-data-lab.git
cd ngrx-data-lab
npm install
npm i @ngrx/effects @ngrx/entity @ngrx/store @ngrx/store-devtools ngrx-data --save

Step 2 - Create the NgRx App Store

We start by creating the NgRx store module for our application. Execute the following code to generate the module and import it into our root NgModule.

ng g m store/app-store --flat -m app --spec false

First we set up NgRx itself by importing the NgRx store, effects, and the dev tools. Replace the contents of app-store.module.ts with the following code.

import { NgModule } from '@angular/core';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../../environments/environment';

@NgModule({
  imports: [
    StoreModule.forRoot({}),
    EffectsModule.forRoot([]),
    environment.production ? [] : StoreDevtoolsModule.instrument()
  ]
})
export class AppStoreModule {}

Step 3 - Define the entities for our store

Next we define the entities in our store by creating a plain TypeScript file named entity-metadata.ts in the store folder.

We need to tell ngrx-data about our entities so we create an EntityMetadataMap and define a set of properties, one for each entity name.

We have two entities: Hero and Villain. As you might imagine, we add one line of code for every additional entity. That's it!

Add the following code in the entity-metadata.ts to define our entities:

import { EntityMetadataMap } from 'ngrx-data';

const entityMetadata: EntityMetadataMap = {
  Hero: {},
  Villain: {}
};

// because the plural of "hero" is not "heros"
const pluralNames = { Hero: 'Heroes' };

export const entityConfig = {
  entityMetadata,
  pluralNames
};

Notice we export the entity configuration. We'll be importing that into our entity store in a moment.

Step 4 - Import the entity store into the app store

We need to add the entity configuration that we just created in the previous step, and put it into the root store for NgRx. We do this by importing the entityConfig and then passing it to the NgrxDataModule.forRoot() function.

Add the following two lines of code to import the symbols into app-store.module.ts.

import { DefaultDataServiceConfig, NgrxDataModule } from 'ngrx-data';
import { entityConfig } from './entity-metadata';

Then add the following line into the imports array.

  NgrxDataModule.forRoot(entityConfig),

Next, we need to import the app store module into our app module. Add the following line into the imports array in app.module.ts

  AppStoreModule,

Step 5 - Simplify the Hero and Villain data services

ngrx-data handles getting and saving our data for us. Replace the contents of heroes/hero.service.ts with the following code.

import { Injectable } from '@angular/core';
import {
  EntityCollectionServiceBase,
  EntityCollectionServiceElementsFactory
} from 'ngrx-data';
import { Hero } from '../core';

@Injectable({ providedIn: 'root' })
export class HeroService extends EntityCollectionServiceBase<Hero> {
  constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
    super('Hero', serviceElementsFactory);
  }
}

Replace the contents of villains/villain.service.ts with the following code.

import { Injectable } from '@angular/core';
import {
  EntityCollectionServiceBase,
  EntityCollectionServiceElementsFactory
} from 'ngrx-data';
import { Villain } from '../core';

@Injectable({ providedIn: 'root' })
export class VillainService extends EntityCollectionServiceBase<Villain> {
  constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
    super('Villain', serviceElementsFactory);
  }
}

Step 6 - Refactor the Container Component to use Observables

Our component currently uses an array of heroes. We need that to switch to an Observable so we can observe and display the changes made in the ngrx store.

Open the heroes.component.ts file and modify the heroes array to be an Observable<Hero[]>.

heroes$: Observable<Hero[]>;

Modify the loading proeprty to be an Observable<boolean>.

loading$: Observable<boolean>;

Add the import for Observable to the top of the file.

import { Observable } from 'rxjs';

We need to listen for the stream of hereos. Set your new heroes$ property to the Observable returned from the heroeService.entities$

constructor(private heroService: HeroService) {
  this.heroes$ = heroService.entities$;
  this.loading$ = heroService.loading$;
}

Here is the fun part, all of our logic in the component becomes simpler.

The add, delete, getHeroes, and update methods get a whole lot simpler, and shorter as ngrx-data handles these common operations. Replace your similar methods with the following ones.

add(hero: Hero) {
  this.heroService.add(hero);
}

delete(hero: Hero) {
  this.heroService.delete(hero);
  this.close();
}

getHeroes() {
  this.heroService.getAll();
  this.close();
}

update(hero: Hero) {
  this.heroService.update(hero);
}

The only change to our template is to look at the observable of heroes$ instead of the former array of heroes. Change the *ngIf by adding the async pipe and labelling the result as heroes.

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

Also find the loading reference in this template file and change it to the following:

  <mat-spinner *ngIf="loading$ | async;else heroList" mode="indeterminate" color="accent"></mat-spinner>

Now repeat these steps for the VillainsComponent.

Step 7 - Run it

Run the app!

ng serve -o

Step 8 - Verify the Redux actions are being dispatched

In the Chrome browser, open the DevTools and select the Redux tab to view the Redux plugin.

In the application, add, update, and remove heroes and villains. As you do this, notice the actions being dispatched in the Redux Devtools

What we did

In retrospect, here are the changes we made to our app to add NgRx via the ngrx-data library.

  • installed our dependencies
  • added these files store/app-store.module.ts and store/entity-metadata.ts
  • told NgRx and ngrx-data about our entities
  • refactored and simplified our data services heroes/hero.service.ts and villains/villain.service.ts
  • refactored and simplified our container components heroes/heroes.component.ts and villains/villains.component.ts

What we accomplished

OK, but why? Why did we do this? Why should I care?

We just added the redux pattern to our app and configured it for two entities. Your app may have dozens or even hundreds of entities. Now imagine what you would need to do to support all of those entities with this pattern. With ngrx-data you add a single line of code to store/entity-store.module.ts for each entity and that's it! You heard that right, one line!

ngrx-data provides all commonly used selectors, actions, action creators, reducers, and effects out of the box.

It is only natural that our apps may not follow the exact conventions that ngrx-data uses. That's OK! If your entity needs a different reducer or effect, you can replace those via configuration. If your web API url doesn't follow the pattern /api/heroes, that's OK! Just override the convention with your own configuration. All of the custom tweaks that you want are available to you when you need them.

Bonus: re-enable toast notifications for ngrx-data

When we migrated to ngrx-data, we lost the toast notifications that were part of the services. We can restore notifications with these easy steps.

Bonus Step 1 - Add a NgrxDataToastService

Create a ngrx-data-toast.service.ts file using the following CLI command

ng g s store/ngrx-data-toast -m store/app-store --spec false

Bonus Step 2 - Show toasts on success and error actions

The service listens to the ngrx-data EntityActions observable, which publishes all ngrx-data entity actions.

We'll only notify the user about HTTP success and failure so we use the RxJs pipe operator and then we filter for entity operation names that end in "_SUCCESS" or "_ERROR".

The subscribe method raises a toast message for those actions (toast.openSnackBar()).

import { Injectable } from '@angular/core';
import { Actions, ofType } from '@ngrx/effects';
import {
  EntityAction,
  EntityCacheAction,
  ofEntityOp,
  OP_ERROR,
  OP_SUCCESS
} from 'ngrx-data';
import { filter } from 'rxjs/operators';
import { ToastService } from '../core/toast.service';

/** Report ngrx-data success/error actions as toast messages * */
@Injectable({ providedIn: 'root' })
export class NgrxDataToastService {
  constructor(actions$: Actions, toast: ToastService) {
    actions$
      .pipe(
        ofEntityOp(),
        filter(
          (ea: EntityAction) =>
            ea.payload.entityOp.endsWith(OP_SUCCESS) ||
            ea.payload.entityOp.endsWith(OP_ERROR)
        )
      )
      // this service never dies so no need to unsubscribe
      .subscribe(action =>
        toast.openSnackBar(
          `${action.payload.entityName} action`,
          action.payload.entityOp
        )
      );

    actions$
      .pipe(
        ofType(
          EntityCacheAction.SAVE_ENTITIES_SUCCESS,
          EntityCacheAction.SAVE_ENTITIES_ERROR
        )
      )
      .subscribe((action: any) =>
        toast.openSnackBar(
          `${action.type} - url: ${action.payload.url}`,
          'SaveEntities'
        )
      );
  }
}

Bonus Step 3 - Connect the toastService to the Store

Inject the new service into the constructor for AppStoreModule

  constructor(toastService: NgrxDataToastService) {}

Add the following import to the AppStoreModule

import { NgrxDataToastService } from './ngrx-data-toast.service';

Bonus Step 4 - Run it

Run the app!

ng serve -o

More Repositories

1

angular-styleguide

Angular Style Guide: A starting point for Angular development teams to provide consistency through good practices.
23,968
star
2

lite-server

Lightweight node server
JavaScript
2,308
star
3

ng-demos

variety of angular demos
JavaScript
1,704
star
4

vscode-peacock

Subtly change the color of your Visual Studio Code workspace. Ideal when you have multiple VS Code instances, use VS Live Share, or use VS Code's Remote features, and you want to quickly identify your editor.
TypeScript
1,040
star
5

angular-ngrx-data

Angular with ngRx and experimental ngrx-data helper
TypeScript
971
star
6

generator-hottowel

Yo generator that creates an Angular app via HotTowel
JavaScript
837
star
7

angular-tour-of-heroes

Angular - Tour of Heroes - The Next Step after Getting Started
TypeScript
822
star
8

vscode-angular-snippets

Angular Snippets for VS Code
TypeScript
567
star
9

gulp-patterns

Playground for Gulp Recipes
JavaScript
501
star
10

HotTowel-Angular

HotTowel with Angular (for NuGet)
JavaScript
238
star
11

pwa-angular

PWA Example
TypeScript
207
star
12

vue-getting-started

This project is seen in demos including the Pluralsight course "Vue: Getting Started" to help represent a fundamental app written with Vue. The heroes and villains theme is used throughout the app.
Vue
191
star
13

heroes-angular

Tour of Heroes app written with Angular
TypeScript
163
star
14

vscode-winteriscoming

Dark theme with fun and bright foreground colors
CSS
162
star
15

pluralsight-gulp

Starter Code for Pluralsight Course "JavaScript Build Automation with Gulp.js"
JavaScript
161
star
16

angular-event-view-cli

Angular Demo with a Little bit of a lot of features
TypeScript
154
star
17

vue-typescript

Vue.js with TypeScript (OLD - in process of updating)
Vue
145
star
18

hottowel-angular-typescript

As seen at //Build 2015 presented by Erich Gamma, Chris Dias and John Papa.
JavaScript
145
star
19

heroes-vue

Tour of Heroes app written with Vue
Vue
144
star
20

HotTowel

John Papa's ASP.NET MVC SPA Template (Durandal)
CSS
142
star
21

heroes-react

Tour of Heroes app written with React
JavaScript
134
star
22

vscode-angular-essentials

Dockerfile
133
star
23

shopathome

Choose from Angular, React, Svelte, and Vue applications with an Azure Functions API, that deploys to Azure Static Web Apps
TypeScript
132
star
24

PluralsightSpaJumpStartFinal

Source code for the SPA JumpStart Pluralsight course at http://jpapa.me/spajsps
JavaScript
132
star
25

hello-worlds

Hello world apps for angular, react, svelte, and vue
TypeScript
128
star
26

heroes-angular-serverless

TypeScript Node/Express πŸ‘‰TypeScript Serverless βž•Angular
TypeScript
125
star
27

angular2-force

ngConf 2016 live coding demo
JavaScript
115
star
28

vscode-cloak

Cloak allows you to hide/show environment keys, to avoid accidentally sharing them with everyone who sees your screen.
TypeScript
106
star
29

angular-preload-and-interceptors

TypeScript
104
star
30

angular-first-look-examples

angular first look for pluralsight
HTML
89
star
31

angular-first-look-hosted

Hosted Code from Pluralsight Course "Angular First Look"
HTML
86
star
32

angular-cosmosdb

Cosmos DB, Express.js, Angular, and Node.js app
TypeScript
81
star
33

CodeCamper

JavaScript
73
star
34

pluralsight-vscode-samples

VS Code samples for Pluralsight course on Code
JavaScript
70
star
35

docker-angular-cli

Dockerfile and image for node plus angular CLI
Dockerfile
69
star
36

awesome-angular-workshop

Angular: The Awesome Parts - Workshop
TypeScript
68
star
37

angular.breeze.storagewip

Save Work in Progress to Local Storage for Angular and Breeze apps
JavaScript
66
star
38

Pluralsight-CC-Angular-Breeze-Extra

Supporting files for the Pluralsight "SPA with Angular and Breeze" course by John Papa.
JavaScript
64
star
39

node-hello

Hello World for Node.js
JavaScript
63
star
40

one-with-angular

TypeScript
61
star
41

node-ts

Simple Node app Written with TypeScript
TypeScript
59
star
42

http-interceptors

The Web apps in this monorepo make HTTP requests and require uniform consistency in how they are executed and handled. This monorepo demonstrates the same app written with Angular and with Svelte. Each app uses HTTP interceptors. The Angular app uses HttpClient and its interceptors while the Svelte app uses Axios and its interceptors.
TypeScript
58
star
43

angular-what-if

TypeScript
57
star
44

vikings

TypeScript
53
star
45

express-to-functions

TypeScript Node/Express πŸ‘‰TypeScript Serverless βž• Angular
TypeScript
53
star
46

vue-guide

Super Simple Vue Samples
HTML
51
star
47

ngrx-demo

NgRx demo
TypeScript
50
star
48

ng-patterns-testing

JavaScript
48
star
49

typescript-async

Creating Asynchronous Code with TypeScript
TypeScript
47
star
50

vscode-angular1-snippets

vscode-angular1-snippets
41
star
51

vscode-pwa

VS Code Extension for PWA Tools
TypeScript
39
star
52

angular-lazy-load-demo

Lazy loading Angular components
TypeScript
32
star
53

github-templates

31
star
54

angular-2-first-look-launcher

deprecated
JavaScript
30
star
55

heroes-svelte

Tour of Heroes app written with Svelte
Svelte
30
star
56

kis-requirejs-demo

Keep It Simple RequireJS Demo. Shows simple demo of require.js before and after
JavaScript
30
star
57

heroes-node-api

node api for the heroes apps
JavaScript
29
star
58

heroes-vue-node-api

As seen in Vue Conf 2019
Vue
28
star
59

toastr-bower

toastr's bower repo
CSS
24
star
60

star-wars-api

Star Wars API
JavaScript
23
star
61

vue-workshop

Vue Fundamentals Workshops
Vue
23
star
62

vue-simple

This project was created to help represent a fundamental app written with Vue. The heroes and villains theme is used throughout the app.
Vue
22
star
63

typescript-fundamentals

HTML
21
star
64

vscode-read-time

TypeScript
19
star
65

vue-intro

Vue.js app using Vue's CLI
Vue
19
star
66

angular-rxjs-shared-examples

rxjs examples
TypeScript
17
star
67

serverless-thank-you

Say thank you to everyone who takes the time to create and discus or a pull request in your Github repository, using Azure Functions
TypeScript
17
star
68

swa-workshop

TypeScript
16
star
69

cloud-coding-with-codespaces

Live demo using Angular, github.dev, codespaces, copilot, azure static web apps, and devcontainers
TypeScript
13
star
70

vscode-azure-functions-tools

Azure Functions Tools for VS Code - DEPRECATED
12
star
71

vue-cli-preset-all-javascript

Vue CLI Preset for All JavaScript Prompts
12
star
72

vscode-presenter-pro

11
star
73

vue-ts

simple repro of master details with vue 3, composition api, and typescript
Vue
10
star
74

ios-play

iOS Playground
Swift
8
star
75

security-strategy-essentials

JavaScript
8
star
76

telekinesis

"Think" code
JavaScript
7
star
77

johnpapa

7
star
78

house-bot

Home automation with AI, LUIS, Serverless
JavaScript
7
star
79

first-serverless-api

Create your first serverless API endpoints with Azure Functions
JavaScript
7
star
80

innersource

6
star
81

Angular-NuGet

NuGet Repo for Angular Packages
JavaScript
6
star
82

angular-expiring-http-cache

TypeScript
5
star
83

ng-ai-hackathon

TypeScript
5
star
84

starwars-ios

Swift
5
star
85

one-with-angular-api

JavaScript
5
star
86

shopping-for-codespaces

CSS
4
star
87

typescript-hello-world

Simple hello world project for running TypeScript with Node.js
TypeScript
4
star
88

svelte-intro

JavaScript
3
star
89

glimpse.toastr

JavaScript
3
star
90

react-book-repo

3
star
91

pluralsight-ng-testing

pluralsight-ng-testing
CSS
2
star
92

skills-copilot-codespaces-vscode

My clone repository
1
star
93

dotfiles

1
star
94

aggregator-app

serverless function with api aggregator with azure
JavaScript
1
star
95

.github

1
star
96

vscode-import-bug

referencing https://github.com/microsoft/TypeScript/issues/35591
TypeScript
1
star