问题
I have a state that contains a collection of items:
import { State, Action, StateContext } from '@ngxs/store';
import {AddItem} from './app.actions';
export interface Item {
id: number;
name: string;
}
export interface AppStateModel {
items: Item[];
}
@State<AppStateModel>({
name: 'app',
defaults: {
items: []
}
})
export class AppState {
@Action(AddItem)
addItem(ctx: StateContext<AppStateModel>, action: AddItem){
const state = ctx.getState();
ctx.patchState({
items: [
...state.items,
{ id: action.id, name: action.name}
]
});
}
}
My component is subscribed to the list of items in the store, when I add a new item that is reflected in the list displayed (all fine).
Observed behaviour
I then bind that displayed item to an input
field - when I type in that input field I seem to be modifying the state of that item, i.e. the 'View Name' display of the item is also changing.
<ng-container *ngIf="app$ | async as app">
Name: <input #name />
<button (click)="addItem(name.value)">Add Item </button>
<br/>
<br/>
Items List:
<div *ngFor="let item of app.items">
View Name: <span>{{item.name}}</span>
</div>
<br/>
Items List 2 with updates:
<div *ngFor="let item of app.items">
Update Name: <input [(ngModel)]="item.name" />
</div>
</ng-container>
Is this expected behaviour? I was expecting that I would not see that change reflected in the 'view only' list.
Or is this just a case of me doing something I shouldn't be - I know I should really be dispatching that change via an action to the store like this:
Update Name: <input [ngModel]="item.name" (ngModelChange)="updateItem(item.name)" />
updateItem(value) {
this.store.dispatch(new UpdateItemAction(value));
}
I'm testing this with
* ngxs: 3.0.1
* @angular/core: 6.0.0
See full sample repo here https://github.com/garthmason/ngxs
Thanks!
回答1:
It's not a bug, it's a feature ;)
When selecting a piece of the state it will in fact return the object that is the state.
Which means that if you change a property to something else the state will get updated.
What I would suggest when working with a form is that you make a copy of the data before you start changing it with ngModel.
when using reactive forms there is a function called patchValue which will do this. https://angular.io/guide/reactive-forms#patchvalue
When it comes to normal forms you will have to do it manually.
lodash comes with a function called cloneDeep which should help you https://lodash.com/docs#cloneDeep
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
来源:https://stackoverflow.com/questions/50247260/binding-to-state-is-this-expected-behaviour