Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Esteban Hernández
Esteban Hernández

Posted on

     

Angular - NGRX-Data - AG Grid - Pt. 1

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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
draylegend profile image
Vladimir Drayling
angular dev
• Edited on• Edited

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.

CollapseExpand
 
t2k profile image
Ted T. Killilea
open source programming +++
  • Location
    NY/NJ
  • Work
    SVP 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

CollapseExpand
 
nacholangdon profile image
Juan Ignacio Langdon Sagasta
  • Location
    Mar del Plata, Argentina
  • Work
    Web UI Developer at Trick Gaming Studios
  • Joined

Hi Esteban, thanks for this first part. What about the second? 🤣

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software Engineer specializing on performant web applications.
  • Location
    Jersey City, NJ
  • Work
    Software Engineer at J.P. Morgan-Chase
  • Joined

More fromEsteban Hernández

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp