1

I'm dispatching the upsert action/ reducer on my store to add a new record to state and there is an effect that will call the backend and add the record/ document to my mongodb instance.

Given that the only sensible id for this particular model is defined by my backend logic at the time of document creation, how should i define selectId in my entity implementation in my front end?

At the moment an item is added to state with id: undefined and i get the warning:

entity.js:76 @ngrx/entity: The entity passed to the selectId implementation returned undefined. You should probably provide your own selectId implementation.

My adapter is defined as:

export const adapter: EntityAdapter<PublishedData> = createEntityAdapter<
  PublishedData
>({ selectId: (publishedData: PublishedData) => publishedData.doi });

and the relevant effect is:

@Effect()
  UpsertPublishedData$ = this.actions$.pipe(
    ofType<UpsertPublishedData>(PublishedDataActionTypes.UpsertPublishedData),
    switchMap(action => this.publishedDataApi.create(action.payload.publishedData)
    .pipe(mergeMap((data: PublishedData) => [ new UpsertPublishedData({ publishedData: data }),
      this.publishedDataApi.register(data[0].doi)]),
    catchError(err => of(new FailedPublishedDataAction(err)))))
  );

publishedData.doi is the offending field that I need to use to reference the entity.

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
ldgorman
  • 1,553
  • 1
  • 14
  • 39

3 Answers3

1

In this case, I will consider 2 options:

  • Option 1: implement a pessimistic creation (to stay simple)

When a new record is created by user, an action for instance AddRecord invokes a POST request to backend api. The successful response emits a new action AddRecordSuccess which add the new created record (with id sent by backend) into the store with @ngrx/entity.

During creation process (request to backend), a loader indicator should be displayed to notify user.

  • Option 2: implement a optimistic creation (for better UX)

If optimistic update is really a need or an objective to reach, either a temporary or permanent id should be generated by front-end app :

  • temporary id is used inside the store to manage entities to be synchronized. When entities are created by backend, the id should be updated or completed by a permanent backend id.

  • permanent id could be generated by front-end (UUID for instance), and sent to be used by backend.

In all the case, @ngrx/entity need an identifier (number or string) to identify and manage entities in store.

Hope it helps.

Thierry Falvo
  • 5,892
  • 2
  • 21
  • 39
  • Neither of these options answer the question. They just state the question again in less detail. – ldgorman Jul 09 '19 at 14:50
  • sorry but I don't understand your comment, I really tried to help by giving 2 options I will consider in such case. You need an identifier, you can't leave undefined. Maybe you should give more explanations or more details on your expectations. – Thierry Falvo Jul 09 '19 at 15:11
  • Option 1 could solve the problem because entity is added after backend response (so with an id). But I understand that pessimistic creation could not be an option regarding your UX specs. – Thierry Falvo Jul 09 '19 at 15:15
  • If you had implemented entity you would know that the same action is used for triggering effects as well as modifying the state so there is no "AddRecordSuccess" action – ldgorman Jul 09 '19 at 15:19
1

For now I have modified the reducer to not add the record to state on the first pass where the id field (doi) is undefined.

case PublishedDataActionTypes.UpsertWaitPublishedData: {
  if (action.payload.publishedData && action.payload.publishedData.doi) {
    return adapter.upsertOne(action.payload.publishedData, state);
  }
  return state;
}

from this originally:

  case PublishedDataActionTypes.UpsertPublishedData: {
      return adapter.upsertOne(action.payload.publishedData, state);
    }

This ensures the record in state matches that in the database. This approach is still in line with an entity approach whereby no additional UpsertPublishedDataSuccess action is required.

ldgorman
  • 1,553
  • 1
  • 14
  • 39
0

If you are talking about ngrx/entity, just specifiy a selectId method when creating your EntityAdapter. see for more info: createentityadapter

 dexport const adapter: EntityAdapter<Location > = createEntityAdapter<Location>({
  selectId: (location: Location) => location.locationid,
});
yido
  • 307
  • 4
  • 16