angularangular-akitaakita

Why does Akita statestore return an empty entity rather than updated entity?


I am using Akita as a state store for my Angular application.

I am successfully fetching data from a backend server and populating my store (data is displayed in components) but when I try to update an entity in the store it emits an empty object.

I suspect this something fundamental to my understanding of Akita I'm missing, but I expected calling the following would

  1. update the entity with given ID
  2. trigger myQuery.select(id).subscribe() with the updated entity
     myStore.setActive(idOfThingToUpdate)
     myStore.updateActive({someProperty: newPropertyValue})
     myStore.removeActive(idOfThingToUpdate)

I have also tried this syntax in the service:

        this.myStore.update(entityId, {propertyName: newPropertyValue});

The subscribe function shown above is returning an empty object. If I create a constructor in my entity class and set a couple of defaults, only those properties defined in the constructor are returned in the query.select() call. And the values of those properties are defaults (defined in constructor) not values that would have existed in the store.

So it seems one or both of the following is true:

Hope that all makes sense....

Some code:

Service:

this.contestStore.setActive(contestId);
// attempt set a single property 'winningLicence' on the active entity
this.contestStore.updateActive({
      winningLicence: newWinningLicence
  }
);
this.contestStore.removeActive(contestId);

Am I meant to pass a newly constructed entity into the updateActive() function or just the props I want updated?

export interface ContestState {
  loading: boolean;
  contests: Contest[];
}

export function createInitialState(): ContestState {
  return {
    loading: false,
    contests: []
  };
}

@StoreConfig({ name: 'contestStore', resettable: true})
export class ContestStore extends EntityStore<ContestState> {
  constructor() {
    super(createInitialState());
  }
}

Query - this does return meaningful data when I call selectAll in a component:

@Injectable()
export class ContestsQuery extends QueryEntity<ContestState> {
  constructor(protected contestStore: ContestStore) {
    super(contestStore);
  }
}

From component...

  ngOnInit(): void {
    // contest is @Input and may be a new unsaved/no id contest, hence check
    if (this.contest.id) { 
      this.contestsQuery.selectEntity(this.contest.id).subscribe((contest: Contest) => {
        // this comes back as empty object after calling my update code in service shown above
        console.log(contest); 
      });
    }
  }

Solution

  • Well I found a solution, not sure I understand it completely but here it is...

    In my service I've a function to refresh cache with fetches via HTTP from backend, then iterates response objects and calls store.set() to setup the store....

            const contests = [];
            contestData.forEach((json) => {
              const contest = Object.assign(new Contest(), json);
              contests.push(contest);
            });
            this.contestStore.set(contests);
    

    I changed to use upsert instead of trying to set the collection in one go using the set() function:

            contestData.forEach((json) => {
              const contest = Object.assign(new Contest(), json);
              this.contestStore.upsert(contest.id, contest);
            });
    
    

    This now seems to work as I'd originally expected and the query.select/get functions return updated data after I've used update(id, object) to update an object.

    I could presumably use upsertMany([]), but I'm not sure how that differs to set([]) in functionality. Indeed using upsertMany and passing an array of Contest fails in the same way as set([]). So for now I'll stick to upsert(id, entity) for each one in turn.

    Maybe I've missed a step when I tell Akita the id field in the Contest object should be extracted and used as the ID?

    https://datorama.github.io/akita/docs/entities/entity-store