4
votes

I'm learning how to use the ngrx/store library in Angular 5 and I noticed an unexpected behavior... Please, help me understand if this is the default behavior for this library, because I really wasn't expecting this at all...

Well, my objective is to create a singleton store for storing application config and multiple states for all my components, so when a component updates the store, all subscribed components receives the updated store.

In one of my components I'm using the following constructor:

newItems: any

constructor (
private _store: Store<State>
) {

_store.select('operations').subscribe(state => {
  console.log(state)
  this.newItems = state.newItems
})

}

So I grab the current state of my store and set "this.newItems" variable to "state.newItems". I have some inputs in my template binded with some properties of "newItems", like this:

itemProperties: any = [
{
  name: 'Description',
  key: 'desc'
},
{
  name: 'Manufacturer',
  key: 'manufacturer'
},
{
  name: 'Model',
  key: 'model'
},
{
  name: 'Observation',
  key: 'genObs'
}
]

<div *ngFor="let prop of itemProperties" class="gen-label-input" [ngClass]="{'filled': newItems[prop.key]}">
    <label>{{prop.name}}</label>
    <input [(ngModel)]="newItems[prop.key]">
</div>

I was expecting to need to dispatch an action in my component to actually modify my store, but it seems that the two-way data binding through [(ngModel)] is doing this automatically, without the need of any actions at all! I just modify my input and my store is updated, and the updated version is automagically available through my entire application.

Is this the expected behavior of ngrx/store when I assing one store property to a component variable and combine it with [(ngModel)]?

If yes, this is really good in some cases, as I don't have to create inumerous actions to modify my store properties. But I'm worried this is some kind of error that I'm not being able to identify.

Thanks in advance!

2
it seems that you wrote your store in a wrong way. It should be immutable. Think twice before utilizing it because it can lead to numerous problems. You shouldn't need to write an action for each of your store's model property just update the whole model each time you need it updated. Utilizing Angular's Form system could be handy in here - SebOlens
Yes, that is exactly the problem I was thinking about, it should be immutable. But it seems that if you use the two-way data binding without creating a copy of my store property, it becomes mutable... Well, this is absolutely not the objective of stores at all, but I think this could be useful when I have multiple components that share the same configuration... What do you think? Thank you! - Felipe Micali
It's very risky but the world belongs to the bravest :) - SebOlens
Hahaha ok, thank you for your opinion! In my application I don't think this is going to be a problem, it's only some arrays and configurations that should be synchronized between 5 different components, so I think I'm using this approach in my favor. Another solution would be to create rxjs BehaviorSubjects and expose them as Observables, subscribing to them in my components and emiting changes via "next()", but the global two-way binding with my store seems to be an easier solution. - Felipe Micali

2 Answers

2
votes

You should create a copy of items from store when you receive them

 this.newItems = state.newItems.map((item) => Object.assign({}, item))

And yes, it is expected behavior if value is array or object

0
votes

Yes it is the expected behavior and you are right the state should be immutable.

You can use ngrx-store-freeze in dev mode to help you detect when you do something you should not be doing.