Felix
Felix

Reputation: 1810

ngrx/data - alternate id `has a missing or invalid entity key (id)`

As there are many IDs in the system, my entities linked to database tables and the tables use 'DbId' as the primary key - instead of the ngrx/data default: 'id'.

That is addressed inside the EntityMetadata map by the property: selectId. I followed the documentation here and practically my setup is done that way. All appears working fine, except when adding records to db table.

The addition to the db table proceeds, but after, on the client site, ngrx/data generates error: has a missing or invalid entity key (id) by the: EntityActionGuard.

Here are the relevant code snippets:

                    /**
                     * Authority roles assigned to user
                     * 
                     */
export class UserRolesEntity {
  DbId?: number;
  DbId_User?: number;
  DbId_Role?: number;
  Timestamp?: Date;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - -

export const entityMetadata: EntityMetadataMap = {
. . .
User_Roles: { selectId: (user_roles: UserRolesEntity) => user_roles.DbId },
. . . 
}

.

class NgrxDataService_UR<User_Roles> extends EntityCollectionServiceBase<User_Roles>

. IN COMPONENT:

constructor(dsUR: NgrxDataService_UR) { . . . }
. . .
fn() {
  const addItem: UserRolesEntity = {
    DbId_User: 3,
    DbId_Role: 11
  }
  this.dsUR.add(addItem);
}

The step creates entry in the database table. Back on client, in debugger, I see NgRx/data utilizes class: EntityActionGuard and fn: mustBeEntity(action) { . . .}

My returned action looks like this:

>payload:
correlationId: "CRID14"
data: Array(1)
>0: {DbId: 29,               <<<----
     DbId_Role: 11,
     DbId_User: 3}
[[Prototype]]: Object
length: 1
[[Prototype]]: Array(0)
entityName: "User_Roles"
entityOp: "@ngrx/data/save/add-one/success"
isOptimistic: false
[[Prototype]]: Object
type: "[User_Roles] @ngrx/data/save/add-one/success"

Note the property DbId in data - it is there with a value ! Still the fn: inside mustBeEntity(action) returns undefined !

const id = this.selectId(data);
// id is undefined 

What may be wrong here ?

Edit:

I have upgraded from Angular 8 to Angular 13. Could this be an issue?

Upvotes: 1

Views: 1484

Answers (2)

Parth Developer
Parth Developer

Reputation: 1857

Finally, I got the solution:

We need to write a custom data service that extends default data service of ngrx/data.

@Injectable()
export class ForReportsDataService extends DefaultDataService<any>{}

and define the CRUD operation and make sure you return the specific object.

Here in my case, I need whatever is there inside the item object.

    // POST /api/for-reports
    add(entity: ForReport): Observable<any> {
        return from(this.dataService.POST("/api/reports", entity).then(res => {
            return res["item"]; // CHECK THIS IS CORRECT IN YOUR CASE
        }));
    }

Upvotes: 0

Felix
Felix

Reputation: 1810

For you colleagues who encounter similar issue.

Make sure the server end returns the proper data type, the same type, the client end expects. In my case the problem was that the server end was string-ifying the response, while it should have been returning the response object as such.

Hope that helps someone dealing with similar pitfall.

Upvotes: 0

Related Questions