0

I have an Akita store that gets populated with data from server. I am trying to get an object from the store and edit it using angular two way binding [()].

Any time I try to change the value of the object (in the template HTML by typing in a field or in component Typescript after data load from service) I get an error about the property not being editable.

I have tried adding ImmerJS in the Akita EntityStore and tried using {...data} in the component. Same result.

Some code...

Akita store:

export interface GradingMetaState extends EntityState<Grading, number>, ActiveState {
  loading: boolean;
  gradings: Grading[];
}

@StoreConfig({ name: 'gradingMetaStore', producerFn: produce})
export class GradingMetaStore extends EntityStore<GradingMetaState> {
  constructor() {
    super();
  }
}

Bit from the service, this is returning data to the component:

  public get gradings$(): Observable<Grading[]> {
    return this.gradingMetaQueries.selectAll();
  }

From the component:

  ngOnInit(): void {
    this.gradings = new Array<Grading>();
    this.busy = true;

    this.service.gradings$.subscribe((gradings) => {
      // this.gradings = {...gradings}; - error about can only be array/iterable

      this.gradings = gradings;
      if (this.gradings.length > 0) {

        // Data is present, but this code produces error in browser console
        // "ERROR TypeError: "firstName" is read-only"

        this.log.debug('changing Albert Einstein to william Einstein');
        this.gradings[1].examiners[1].firstName = 'william'; // ERR line
        this.log.debug('changing Albert Einstein to william Einstein DONE');
      }
      this.busy = false;
    });
  }

The component above errors as described, but if I remove the name setting from there I get the same result if I try to edit the value in an input field:

      <mat-form-field>
        <mat-label>First name</mat-label>
        <input matInput [disabled]="busy" (blur)="saveGrading(grading)"
               [(ngModel)]="examiner.firstName" placeholder="First name">
      </mat-form-field>

The value is populated on the page when template loads.

Now it's very possible I've missed some crucial aspect of Angular and/or Akita, feel free to tell me so!

EDIT - potential solution So Immer in the store doesn't seem to be doing what I hoped returning 'draft' objects, which I took to mean mutable (https://datorama.github.io/akita/docs/immer/).

In my service I have code to fetch data from the store via queries. I tried changing the service (added code below is horrible, acknowledged) to copy datastore objects and return the copy. That seems to allow end to end functionality to work.

However I'm not sure that's the right thing to do...

Old service method:

  public get gradings$(): Observable<Grading[]> {
    return this.gradingMetaQueries.selectAll();
  }

New service method:

  public get gradings$(): Observable<Grading[]> {
    const observable = new BehaviorSubject([]);
    this.gradingMetaQueries.selectAll().subscribe((gradings: Grading[]) => {
      // not nice, but seems to do the job. 
      // Probably better ways available to copy object into mutable.
      const thing = JSON.parse(JSON.stringify(gradings));
      observable.next(thing);
    });
    return observable;
  }

DaFoot
  • 1,475
  • 8
  • 29
  • 58

1 Answers1

0

While I don't use Akita, but I do have experience in using immer. I don't see where you are doing "produce" function.

Based on the documentation from akita on immer. Looks like they extended the functionality of produce with update.

Something along this should update the value of your state.

    this.gradingMetaStore.update(draft => {

        draft.[1].examiners[1].firstName = 'william';

    }
penleychan
  • 5,370
  • 1
  • 17
  • 28
  • Thanks for having a look. The produce function in @storeconfig is there to use immer as items are retrieved from the store. That's something I took from the Akita docs somewhere. I (think I) need objects retrieved from store to be mutable, not as they go back into the store. – DaFoot Jan 22 '22 at 12:34