Introduction
I've been building a Dashboard-style monitoring application for a client for the last few weeks. The application requires CRUD functionality across a series of resources. Although there is a lot of shared functionality across these different resources, each one has a set of business rules when it comes to creating, updating and destroying these resources. When starting out, I had to select a few libraries that would help me avoid having to write all of the common CRUD functionality while allowing me to easily insert the business logic at any point.
After some research, I settled on NGRX-Data for state management and AG Grid for the resource views. You might've heard some criticism around NGRX on how much boilerplate it requires but I want to clarify that NGRX Data is an additional layer of abstraction on top of the basic NGRX Store library which helps the developer avoid the common boilerplate code. In fact, I found myself writting very little code beyond configuration to get the majority of the necessary functionality going.
As for the UI, I chose AG Grid since it comes with tons of functionality out of the box and is very easy to extend. It comes with sensible defaults while also offering tons of extension points. I have yet to find any significant limitation on this library and I definetly recommend it's use for an application requiring anything beyond a trivial data table.
Finally, we'll be leveraging the Angular 2+ web application framework and the RxJs library. Be sure to understand both of these tools to follow along although this post will be more focused on NGRX Data and AG Grid.
Demo Data
I'll be using data fromJSON Placeholder which is a free-to-use, mock API. It doesn't belong to me so much gratitude toTypicode for making this awesome tool available.
Installation
Creating an Angular project
Let's get our application setup. First, start a new Angular 2+ project. If you don't already have the@angular/cli
installed, run the following:
npm i-g @angular/cli
Be sure to include routing and SCSS in the Angular application prompts.
ng new ngrx-data-ag-grid-democdngrx-data-ag-grid-demo
Install AG Grid:
npminstall--save ag-grid-community ag-grid-angular
We need to add some styles for AG Grid to ourstyles.scss
file.
@import"~ag-grid-community/dist/styles/ag-grid.css";@import"~ag-grid-community/dist/styles/ag-theme-balham.css";
Install NGRX Data
npm i--save @ngrx/data @ngrx/store @ngrx/entity @ngrx/effects
NGRX Data still requires NGRX Store, Effects and Entities. It does however add a lot of the functionality for CRUD actions freeing up developers to focus on the business domain. Create anapp-store.module.ts
file and add the following:
import{NgModule}from"@angular/core";import{CommonModule}from"@angular/common";import{StoreModule}from"@ngrx/store";import{EffectsModule}from"@ngrx/effects";import{EntityDataModule,DefaultDataServiceConfig}from"@ngrx/data";import{PostCollectionService}from"./posts/post-collection.service";import*asfromPostsfrom"./posts";constNGRX_STORE_CONFIGURATION={};constREGISTERED_EFFECTS=[];constENTITY_METADATA={};constENTITY_PLURAL_NAMES={};constNGRX_DATA_SERVICE_CONFIGURATION={};@NgModule({imports:[CommonModule,StoreModule.forRoot(NGRX_STORE_CONFIGURATION),EffectsModule.forRoot(REGISTERED_EFFECTS),EntityDataModule.forRoot({entityMetadata:ENTITY_METADATA,pluralNames:ENTITY_PLURAL_NAMES})],providers:[{provide:DefaultDataServiceConfig,useValue:NGRX_DATA_SERVICE_CONFIGURATION},PostCollectionService]})exportclassAppStoreModule{}
Configuring the API endpoint
Configure the API address by providing aDefaultDataServiceConfig
object. Add the following toapp-store.module.ts
:
...constNGRX_DATA_SERVICE_CONFIGURATION={root:"https://jsonplaceholder.typicode.com/"};...
Add the Store to the App
Import theAppStoreModule
within theapp.module.ts
:
import{BrowserModule}from"@angular/platform-browser";import{NgModule}from"@angular/core";import{AppRoutingModule}from"./app-routing.module";import{AppStoreModule}from"./app-store.module";import{AppComponent}from"./app.component";@NgModule({declarations:[AppComponent],imports:[BrowserModule,AppStoreModule,AppRoutingModule],providers:[],bootstrap:[AppComponent]})exportclassAppModule{}
Configuring the first Entity Collection
NGRX Data is focused on entities which are just collections of JS objects. It'll handle the synchronization of a local cache and a remote endpoint by default with pessimistic strategies. It can however be configured to use optimistic strategies, multiple endpoints, etc. All defaults can be overriden.
Define the Entity state and configuration
The first entity will be thePost
entity. Start by creating aposts
directory and astate.ts
file and anindex.ts
file. Add the following tostate.ts
:
exportconstentityCollectionName="Post";exportconstpluralizedEntityName="posts";exportconstentityCollectionEndpoint=pluralizedEntityName;exportinterfacePost{id:number;userId:number;title:string;body:string;}
Export the Entity state and configuration
And,index.ts
:
export*from"./state";
Configure the Entity in the Store
Theapp-store.module.ts
needs to be updated with thePost
entity collection configuration:
...import*asfromPostsfrom'./posts';...constENTITY_METADATA={[fromPosts.entityCollectionName]:{}};...constENTITY_PLURAL_NAMES={[fromPosts.entityCollectionName]:fromPosts.pluralizedEntityName};...
NGRX Data has a default pluralization feature based on the collection name but we found it to be very unreliable. We decided to always provide the pluralNames from configuration instead. This also makes mapping application routes to API calls more reliable.
Creating the Entity Collection Service
NGRX Data provides theEntityCollectionServiceBase
class which provides the high-level implementation for the observable state and actions of the Entity store. Each Entity will have a dedicated service that extends this class.
Create a file within theposts
directory namedpost-collection.service.ts
and add the following:
import{Injectable}from"@angular/core";import{EntityCollectionServiceBase}from"@ngrx/data";import{EntityCollectionServiceElementsFactory}from"@ngrx/data";import*asfromPostsfrom"./";@Injectable()exportclassPostCollectionServiceextendsEntityCollectionServiceBase<fromPosts.Post>{constructor(readonlyelementsFactory:EntityCollectionServiceElementsFactory){super(fromPosts.entityCollectionName,elementsFactory);}}
Display the data with AG Grid
Create a directory within theposts
directory namedposts-list
and add aposts-list.component.ts
file. Add the following:
import{Component}from"@angular/core";import{concat}from"rxjs";import{startWith}from"rxjs/operators";import{FirstDataRenderedEvent}from"ag-grid-community";import{PostCollectionService}from"../post-collection.service";@Component({selector:"app-posts-list",template:` <h1>Posts</h1> <hr /> <ag-grid-angular [columnDefs]="columns" [rowData]="rows$ | async" [pagination]="true" [paginationAutoPageSize]="true" (firstDataRendered)="onFirstDataRendered($event)" ></ag-grid-angular> `,styles:[` :host { display: flex; flex-direction: column; justify-content: center; padding-left: 5vw; } .grid { height: 80vh; width: 90vw; } `]})exportclassPostListComponent{privatecolumnDefaults={resizable:true,sortable:true,filter:true};readonlycolumns=[{...this.columnDefaults,headerName:"ID",field:"id",resizable:false},{...this.columnDefaults,headerName:"Title",field:"title"},{...this.columnDefaults,headerName:"Body",field:"body"}];readonlyrows$=concat(this.postCollectionService.getAll(),this.postCollectionService.entities$).pipe(startWith(null));constructor(privatepostCollectionService:PostCollectionService){}onFirstDataRendered({columnApi}:FirstDataRenderedEvent):void{columnApi.autoSizeAllColumns();}}
Setup lazy loading for Feature modules
This is a great opportunity to setup lazy-loading of each feature module. We'll load the proper presentation components based on the current route.
First, create aposts-routing.module.ts
and add the following:
import{NgModule}from"@angular/core";import{RouterModule,Routes}from"@angular/router";import{PostListComponent}from"./posts-list/posts-list.component";constroutes:Routes=[{path:"",component:PostListComponent}];@NgModule({imports:[RouterModule.forChild(routes)],exports:[RouterModule]})exportclassPostsRoutingModule{}
Second, create aposts.module.ts
and add the following:
import{NgModule}from"@angular/core";import{CommonModule}from"@angular/common";import{AgGridModule}from"ag-grid-angular";import{PostsRoutingModule}from"./posts-routing.module";import{PostListComponent}from"./posts-list/posts-list.component";constAG_GRID_CUSTOM_COMPONENTS=[];@NgModule({imports:[CommonModule,AgGridModule.withComponents(AG_GRID_CUSTOM_COMPONENTS),PostsRoutingModule],declarations:[PostListComponent]})exportclassPostsModule{}
Next, add the router outlet to theapp.component.html
file:
<router-outlet></router-outlet>
Finally, add the first route to theapp-routing.module.ts
:
...import*asfromPostsfrom'./posts';...constroutes:Routes=[{path:fromPosts.entityCollectionEndpoint,loadChildren:()=>import("./posts/posts.module").then(m=>m.PostsModule)}];...
We should now be able to navigate in our browser tohttp://localhost:4200/posts
and see a grid populated with data from JSONPlaceholder. Not bad for how little code we've had to write.
Conclusion
For Part 2, we'll be adding theUser
entity and interpolating the author's name into each of thePost
entries in the AG Grid.
Top comments(3)

Thank you for the post!
It would be great to see in part 2 the implementation of a pagination considering this response
{data:[{id:1,...},{id:2,...}],page:2,...,}
Seereqres.in API for more info.

- LocationNY/NJ
- WorkSVP Banking at Commercial Bank
- Joined
Get post thanks. think you left out
import { HttpClientModule } from '@angular/common/http';
and add HttpClientModule to the imports section of app.module.ts
There's not many sample apps around using ngrx-data so thanks for that. Would be interested in seeing post #2 on this topic!
Thanks and cheers

- LocationMar del Plata, Argentina
- WorkWeb UI Developer at Trick Gaming Studios
- Joined
Hi Esteban, thanks for this first part. What about the second? 🤣
For further actions, you may consider blocking this person and/orreporting abuse