Liam
Liam

Reputation: 11

ngrx/store with ngModel caused some problems

I'm using primeng editable table and ngrx/store. The table value is from store: results.

The parent component:

this.results$ = store.pipe(select(mySelectors.results));

use async to pass it to child componnet [results]="results$|async".

In the child template:

<p-table [value]="results">
...
<ng-template pTemplate="body" let-rowData let-rowIndex="rowIndex">
  <tr [pSelectableRow]="rowData">
  ...
    <td pEditableColumn>
      <p-cellEditor>
        <ng-template pTemplate="input">
          <input pInputText type="text" [(ngModel)]="rowData.name">   // it's about this input
        </ng-template>
        <ng-template pTemplate="output">
          {{rowData.name}}
        </ng-template>
      </p-cellEditor>
    </td>
  </tr>
</ng-template>

the results model is like this:

{
 ...
 name: string;
 ...
}

I used ngrx-store-freeze. now the question is,the results from store cannot be changed directly. So even if I tried to use action to change values in the store, but I cannot get the changed value from the input tag with [(ngModel)]="rowData.name", cause it's read only.

So how can I get the changed value from input tag, or is there a better way to do this? I understand that ngModel is sort of conflict with ngrx/store, and I tried to deep copy results then bind it to the table, but it's not working, the deep copy is still read only.

I'm stuck here. please help me

Upvotes: 1

Views: 2695

Answers (3)

Liam
Liam

Reputation: 11

problem solved. There may be some bugs using ngrx-store-freeze with primeng editable table. when out of p-table, all will be ok.

Upvotes: 0

Adrian Brand
Adrian Brand

Reputation: 21658

If you want to use ngModel to two way bind with data that comes from an observable you should clone it into a template variable before you start modifying it. Otherwise you are mutating the store data. I use a clone pipe

<ng-container *ngIf="results$ | async | clone as results">
  Do what you want with results as it wont mutate the store because it is a clone
</ng-container>

My clone pipe looks like

@Pipe({
  name: 'clone'
})
export class ClonePipe implements PipeTransform {
  transform(value: any, args?: any): any {
    return clone(value);
  }
}

and the clone function looks like

export const clone = obj =>
  Array.isArray(obj)
    ? obj.map(item => clone(item))
    : obj instanceof Date
    ? new Date(obj.getTime())
    : obj && typeof obj === 'object'
    ? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
        o[prop] = clone(obj[prop]);
        return o;
      }, {})
    : obj;

It wont clone every JavaScript object but will be fine for anything that came from an API via JSON.

Upvotes: 0

Tony
Tony

Reputation: 20162

Ngrx store freeze prevent you to modify the state of the rowData.names because you are using input which mean you can modify the state.

So you can fix it by remove 2 way binding

<input pInputText type="text" [ngModel]="rowData.name"> 

Upvotes: 1

Related Questions