0

I am trying to setup NgRx Data in sample project. The use case is to fetch Employee entities using Data Service and store them. So I defined data service, collection service and app module as below. I added All Employees button on home page which gets all employees in database. When I click on All Employees button, the end point is not being called. It seems I am missing something basic here

all-employees.component.html

<h1>All Employees</h1>
<ul>
    <li *ngFor="let employee of employees$ | async">{{ employee.lastName }}, {{ employee.firstName }}</li>
</ul>

employee-data.service.ts

@Injectable()
export class EmployeeDataService extends DefaultDataService<Employee> {
    constructor(http: HttpClient, httpUrlGenerator: HttpUrlGenerator, logger: Logger) {
        super('Employee', http, httpUrlGenerator);
        logger.log('Created custom Employee EntityDataService');
    }

    override getAll(): Observable<Employee[]> {
        return this.http.get<Employee[]>('http://localhost:3000/employees');
    }
}

and employee service like this employee.service.ts

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

and AppModule that ties all of these together app.module.ts

@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        HttpClientModule,
        RouterModule.forRoot(
            [
                {
                    path: 'all_employees',
                    component: AllEmployeesComponent,
                },
            ],
            { initialNavigation: 'enabledBlocking' }
        ),
        StoreModule.forRoot(reducers, {
            metaReducers: !environment.production ? [] : [],
            runtimeChecks: {
                strictActionImmutability: true,
                strictStateImmutability: true,
            },
        }),
        EntityDataModule.forRoot({
            entityMetadata: appEntityMetadata,
        }),
        EffectsModule.forRoot([]),
        !environment.production ? StoreDevtoolsModule.instrument() : [],
        StoreRouterConnectingModule.forRoot(),
    ],
    providers: [EmployeeDataService],
    bootstrap: [AppComponent],
})
export class AppModule {
    constructor(entityDataService: EntityDataService, EmployeeDataService: EmployeeDataService) {
        entityDataService.registerService('Employee', EmployeeDataService);
    }
}

Code: The code uploaded to Github and run it by following the below instructions

  1. Clone the repo and install dependencies
  2. Start the sample JSON server
json-server --watch db.json
  1. Run the project
nx serve ngrx-demo
Pavan Jadda
  • 4,306
  • 9
  • 47
  • 79

1 Answers1

2

Edit: Ok, this is pretty hard to detect, but actually the order of declaration in the NgModule matters. I compared with one of my project and found that EntityDataModule is loaded the last, after both :

  • StoreModule
  • EffectsModule
EffectsModule.forRoot([]),
!environment.production ? StoreDevtoolsModule.instrument() : [],
StoreRouterConnectingModule.forRoot(),
EntityDataModule.forRoot(entityConfig),

This makes sense but it was tricky ! And also, be careful with naming your services, I would advice not to use Uppercase for the first letter of your instance : employeeDataService: EmployeeDataService


Maybe I missed something in your code, but I don't see the place where you actually ask the service to retreive the Entities. From my experience you not only need to create an Observable from the adapter, but you need to call one of the built-in method of NgRx Data like :

https://ngrx.io/guide/data

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

It should happen here : https://github.com/pavankjadda/nx-projects/blob/main/apps/ngrx-demo/src/app/all-employees/all-employees.component.ts

So, if I'm not wrong you simply add this call and your endpoint should be triggered ! Cheers.

Alain

Alain Boudard
  • 768
  • 6
  • 16
  • That was my first modification, it didn't work. See updated code line [here](https://github.com/pavankjadda/nx-projects/blob/main/apps/ngrx-demo/src/app/all-employees/all-employees.component.ts#L18=) – Pavan Jadda Jun 27 '22 at 23:26
  • I edited the answer, tell me if it works for you ! – Alain Boudard Jun 28 '22 at 12:39
  • I updated the code, still the same issue – Pavan Jadda Jun 28 '22 at 13:46
  • Check my fork here, it works : https://github.com/aboudard/nx-projects. I fixed the entityMetadata too. – Alain Boudard Jun 28 '22 at 14:52
  • I got it working without your changes. The only problem, the external API being hit every time instead of using cache after first fetch. You can see the changes [here](https://github.com/pavankjadda/nx-projects/blob/main/apps/ngrx-demo/src/app/all-employees/all-employees.component.ts#L15=) – Pavan Jadda Jun 28 '22 at 19:02
  • I'm glad it works. But you're not supposed to use the `getAll()` method to retreive the entities, you should use one of the inner Observables like `entities$`. And to my knowledge, there is no built-in Http cache strategy in NgRx Data, did you mean the use of the Store ? If you don't want to call the `getAll()` method too often, simply put this call at a higher level of your application. – Alain Boudard Jun 28 '22 at 21:50
  • I am trying to cache entity data. Store in cache on initial load/cache miss and load from cache on subsequent calls. Don't know why is it so difficult in NgRx (unless I am doing something wrong here) – Pavan Jadda Jun 29 '22 at 22:59
  • Well it's not specifically difficult, but it's not how it works. When you work with your Store, it won't make any request unless you want it to, so don't call `getAll()` when you don't need it, and the rest of the time, use the built-in `Observables` exposed by the lib. NgRx Data is simply making easy to map endpoints with Store Entities. – Alain Boudard Jun 30 '22 at 10:49