angular-ngrx-data

Using ngrx-data how to configure the app to fetch data of specific entities from specific urls


If we follow ngrx-data example and look at the Entity DataService, we can fetch the Hero data that we have in-memory (hard-coded) without any configuration. The default will work the same as if we configured:

const defaultDataServiceConfig: DefaultDataServiceConfig = {
  root: 'api',      // or a running server url, e.g: 'http://localhost:4000/api'
  timeout: 3000,    // request timeout
}

and in e.g: EntityStoreModule

@NgModule({
  providers: [{ provide: DefaultDataServiceConfig, useValue: defaultDataServiceConfig }]
})

Question:

How will we configure our app to fetch data for entity "Heros" from the default source:

root: 'api'

and data for entity "Villans" from a URL:

root: 'http://localhost:4000/villans'

and data for other entities from their (other/various) respective URLs ...?


Solution

  • After reviewing the docs specifically: Custom EntityDataService and Replace the HttpUrlGenerator I came up with this solution. Anyone feel free to comment.

    1. Define/review your data types - entity metadata - entity names;
    2. Create mapping to plurals for non-default plural entity names (default is: name + 's');
    3. For entities with the non-default root URL create a mapping of entity names to specific URL;

    File: ../entity-metadata.ts

                                 // Step 1:
    const entityMetadata: EntityMetadataMap = {
        Hero: {},
        Villan: {},
        Creature: {},
        DataA01: {}
        // etc.
    }
    
                                 // Step 2:
    const pluralNames = {
        Hero: 'heroes',
        DataA01: 'data-a01'
    }
    
    export const entityConfig = {
        entityMetadata,
        pluralNames
    };
    
                                 // Step 3:
    export const rootUrls = {
     // Hero:        - not needed here, data comes from default root
        Villan:      'http://localhost:4001',
        Creature:    'http://localhost:4001',
        DataA01:     'http://remoteserver.net:80/publicdata',
    }
    
    1. Replace the HttpUrlGenerator (doc) with your own URL generator (DynamicHttpUrlGenerator)

    File: ../http-dyn-url-generator.ts

    import { Injectable } from '@angular/core';
    import {
      DefaultHttpUrlGenerator,
      HttpResourceUrls,
      normalizeRoot,
      Pluralizer,
      DefaultPluralizer,
    } from '@ngrx/data';
    import { rootUrls } from '../data/ngrx-data/db01-entity-metadata';
    
    
    
    @Injectable()
    export class DynamicHttpUrlGenerator extends DefaultHttpUrlGenerator {
    
      constructor(private aPluralizer: Pluralizer = new DefaultPluralizer(undefined)) {
        super(aPluralizer);
      }
    
      protected getResourceUrls(entityName: string, root: string): HttpResourceUrls {
        let resourceUrls = this.knownHttpResourceUrls[entityName];
        if ( ! resourceUrls) {
                                // rootUrls contains
                                // mapping of individual ngrx data entities 
                                // to the root URLs of their respective data sources.
                                // It contains only entities which do not have
                                // the default root URL.
          if (rootUrls.hasOwnProperty(entityName)) {
             root = rootUrls[entityName];
          }
          const nRoot = normalizeRoot(root);
          const url = `${nRoot}/${this.aPluralizer.pluralize(entityName)}/`.toLowerCase();
    
                                // remove after testing
    console.log('-- entityName: ' + entityName + ', URL: ' + url)
    
          resourceUrls = {
              entityResourceUrl: url,
              collectionResourceUrl: url
          };
          this.registerHttpResourceUrls({ [entityName]: resourceUrls });
        }
        return resourceUrls;
      }
    }
    
    1. For each of your data entity create a custom EntityDataService

      • HeroDataService
      • VillanDataService
      • CreatureDataService
      • DataA01DataService
      • etc.

    (doc and code is here) - the code example is under

    // store/entity/hero-data-service.ts

    1. Register your DynamicHttpUrlGenerator and your custom EntityDataServices in your app's module, in my case:

    File: ../ngrx-data-store.module.ts

    (in a simple app, directly in file: app.module.ts)

    @NgModule({
        imports: [ ... ],
        providers: [ { provide: HttpUrlGenerator, useClass: DynamicHttpUrlGenerator },
                     HeroDataService,
                     VillanDataService,
                     CreatureDataService,
                     DataA01DataService
                   ]
     })
    
    1. Use your custom EntityDataServices in your components for each given entity the same way as all standard or default EntityDataServices to fetch data. The data will be pulled from the respective URLs you set in the const: rootUrls. Don't forget to get your URLs' data server(s) configured and started.

    2. A few important considerations:

      • on your server you may need to enable CORS handling. E.g: on nestjs use:

      app.enableCors();

      • if your client app uses: Angular in-memory-web-api you need to enable access to remote server as follows:

    File: ../in-mem-data.module.ts (or as you named it)

    import { NgModule } from '@angular/core';
    import { HttpClientModule } from '@angular/common/http';
    import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
    import { InMemDataService } from '../../services/data/in-mem-data/in-mem-data.service';
    
    @NgModule({
     imports: [
       HttpClientModule,
       HttpClientInMemoryWebApiModule.forRoot(InMemDataService, {
          passThruUnknownUrl: true   // <--- IMPORTANT for remote data access
        }),
     ]
    })
    export class InMemDataModule {}