I'm using @ngrx/store for an Angular 2 app.
My store holds a list of say, Book
objects. I want to update a field in one of those objects. I also happen to have an Observable of the Book instance I'm looking to update (say, selectedBook
).
To do the update I intend on calling the reducer with an UpdateBookAction
, and a payload of the new Book. So I make a deep copy of the existing Book object by subscribing to selectedBook
and then calling Object.assign().
But when I try to write to one of the fields of the copy I get the following error. (It happens to be the same error I get if I were to try to write directly to the Book object in the store.)
Error
Cannot assign to read only property 'name' of object '#<Object>' at ViewWrappedError.BaseError [as constructor]
Code
ngOnInit() {
this.book$ = this.store.let(fromRoot.getSelectedBook);
//...
}
someFunction() {
//...
this.book$.subscribe(book => {
let updatedBook = Object.assign({}, book);
updatedBook.name = 'something else'; // <--- THIS IS WHAT THROWS
let action = new BookUpdateAction(updatedBook);
this.store.dispatch(action);
}
}
Clarification after Comments
I was under the assumption that I could have an action with a payload that was not the entire state of the store. (In fact that seems necessary, no?) I'm confident that this is the case given the documentation.
The action I'm looking to take is something like this:
Action = UPDATE, payload = {'id': 1234, 'name': 'something new'}
As mentioned, I intend on making that call like this:
this.store.dispatch(action);
Presumably under the hood, ngrx is passing my action to the reducer along with the (immutable) current state.
So from there, everything should work okay. My logic inside the reducer doesn't mutate the existing state, it simply creates a new one out of the existing state and the payload I've passed in.
The real question here is how I can reasonably build the new "objectToUpdate" such that I can pass that in as the payload.
I could do something like this:
this.book$.subscribe(book => {
let updatedBook = new Book();
updatedBook.id = book.id;
//set all other fields manually...
updatedBook.name = 'something else';
let action = new BookUpdateAction(updatedBook);
this.store.dispatch(action);
}
But we're not just talking about two fields here... what if my book has several fields? Do I have to manually build from scratch a new Book each time just to update one field?
My solution was to do a deep copy using Object.assign({}, book)
(and not mutate the old one!) and subsequently make the update to solely the field I was looking to touch.