Looking for some advice on the best way to update multiple models on save. Why might I want to do this, you ask? Well, I'm using Parse as my backend and unfortunately relationships are only one way. In order for them to work in Ember, I need to manually manage pointers.
Example: Obj A belongs to Obj B. Obj B has many Obj As. Parse stores a pointer from Obj B to Obj A, but I need to store an array of objects in Obj B that contains IDs for all of the Obj As. When I go to delete an Obj A, I need to save Obj B with the updated array of As. Currently, I'm doing something like this in my component logic:
ObjA.save().then(() => {
ObjB.save();
});
That seems to work, but I'd like to move this logic to the adapter so I don't have to think about it every time I'm deleting (or adding) a new object.
I was thinking I could find a hook somewhere in the adapter to check if if an object is being deleted. If so, send a PUT request to ObjB with the new list of ObjAs. I just need some guidance figuring out where that would go. Or, maybe the best way is to handle this in each component.
There is didDelete on model so you should be able to do it there. That said, api that does not have integrity check is head scratching.
Related
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.
The idea of the ngrx store is to have one and only one single place of truth, which means all the objects are immutable, and the only way to change anything is to recreate everything as a whole. Also, you are probably using the ngrx freeze (https://github.com/codewareio/ngrx-store-freeze) which means that all of the objects will be created read-only so you wont be able to change any (This is good for development if you want to completely follow the redux pattern). If you remove the part where the store freezes the object, you will be able to change it, but thats not best practice.
What I would suggest you is the following: Use the ngrx observable with async pipe to put the data (in your case books) in a dumb component which can only get input and output some event. Than, inside of the dumb component you can "edit" that object by making a copy of it, and after you are done, you can emit back the changes to the smart component which is subscribed to the store and allow it to change the state via the store (commit). This way is best because it is not very common to change the whole state for a really small change (like two way binding, when user types..).
If you follow the redux pattern, than you will be able to add history, which means the store will keep a copies of the last X state recreations, so you can get UNDO functionality, easier to debug, timeline etc
Your problem is that you are directly editing the property instead of recreating the whole state.
I'll have to make an assumption about the actual scenario the OP is experiencing.
The problem
It's not possible to modify a member of a frozen object. Its the error being thrown.
The cause
ngrx-store-freeze is used as a meta-reducer to freeze any object that enters the store. On another place, when an object needs to be changed, a shallow copy is being made. Object.assign() doesn't do deep copy. A member of another object reached from the original object is being modified. This secondary object is also frozen, by it is not duplicated.
Solution
Use a deep copy like cloneDeep() from lodash. Or sent a bag of properties to be changed with a proper action. Process the changes on the reducer.
As already mentioned - the reason you are getting
Cannot assign to read only property 'name' of object
is because 'ngrx-store-freeze' freezes the state and prevents mutating it.
Object.assign will provide a new object as you expect, but it will copy the state's properties along with each property's own definition - such as the 'writable' definition (which 'ngrx-store-freeze' likely sets to false).
A different approach is described in this answer and explains how cloning objects with JSON.parse(JSON.stringify(yourObject)) as fastest, but this approach has flaws if you keep dates or methods etc' in your state.
using lodash's 'cloneDeep' is probably your best bet for deep cloning the state.
One way to accomplish this is a utility/helper method to make a new book from.
You could give it an existing book and the subset of properties you want to add to a new book (using Partial in typeScript if you want type safety).
createNewBook(oldBook: Book, newProps: Partial<Book>): Book {
const newBook = new Book();
for(const prop in oldBook) {
if(newProps[prop]) {
newBook[prop]=newProps[prop];
} else {
newBook[prop]=oldBook[prop];
}
}
return newBook
}
You could call it via newBook = createNewBook(new Book(), {title: 'first foo, then bar'});
and use this newBook to update your store.
I've one object, where one value is array of numbers and I call two functions, first function sorts data and display it, second function just display it.
problem is that, in second function, data is also sorted. (I'm not sorting there, data have come already sorted)
function1(data);
function2(data);
How can I fix it?
One simple solution to this would be create a copy of the object and then using it.
You can create a copy of the object, if don't want that using:
b = Object.create(a)
In this case b is a copy of a but if you make changes in a, it won't reflect in b. For more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Reason: In javascript objects are passed by reference
http://www.w3schools.com/js/js_function_parameters.asp
Create clone inside sorted function before sorting it
Try like this
var cloneData=data.slice();
your function1 is sorting the data which means the data will be sorted when you call function2, what you can do is in function1 take in data, but create a new variable from data and then update that so the original data is never changed
However displaying some code for function1 and function2 would really help us provide some useful code :)
You will need to duplicate your object to do this. It has to do with object refencing and such.
VERY short explanation (way too short to do it any justice) is that function1 and function2 are using the same array, so when function1 changes it, this is visible in function2 afterwards. read up in this question here, this is definitely something you should research as a programmer.
var data = ['b', 'c', 'a'];
var sortedData = data.slice(0); // shallow copy, objects in the array are not copied
call the function that sorts with sortedData, call the function that only displays with data
Read up more about object references and primitive values to understand how changing values in one array is going to affect to other one, but if you aren't changing the values anymore this is not an issue you need to worry about.
Hi y'all I'm trying out angular and firebase together for some cool 3 way binding action, but I'm running into some problems with binding. I don't really know how the objects ($scope and $firebase) should look like before being binded together. Right now, if I change through firebase, I am able to to see the change in my DOM almost immediately, but I need to be able to do some crud from DOM to FB for some real 3 way binding. Maybe I'm doing this completely wrong. :/
Here's what I have:
html (this creates a huge grid of 400 squares based off of my $scope.myGrid which is a $scope object referencing a $firebase object)
<div class="square" ng-repeat="(position, hex) in myGrid" style="background-color:{{hex}}" ng-click="squareClick({{position}})">
my Controller (anonymous fxn makes my $scope.myGrid object.)
$scope.paletteColor = "#f00";
//FIREBASE
var ref = new Firebase("https://MyAPP.firebaseio.com/");
//angularfire ref to the data
var sync = $firebase(ref);
//download the data into a local object
var syncObject = sync.$asObject();
console.log(syncObject); // firebase object is composed of root node with 400 child nodes with key:value like 01-01:"#f00", 01-02: "#ff0" which is exactly how my $scope.myGrid object looks like
$scope.myGrid = syncObject;
// binding Part taken from the docs which is a huge mystery to me.
// syncObject.bindTo($scope, "myGrid").then(function(){
// console.log($scope.myGrid);
// $scope.myGrid. = "baz";
// ref.$set({foo:"baz"});
// });
You do need to use the syncObject.bindTo syntax as you listed in the comment. This sets up the three-way binding. See this note from the official documentation below:
While 3-way bindings can be extremely convenient, be careful of trying to use them against deeply nested tree structures. Stick to practical uses like synchronizing key-value pairs.
If you need more functionality than basic key-value pairs you may want to look into extending Firebase factories. You can find it in the documentation at https://www.firebase.com/docs/web/libraries/angular/guide.html#section-extending-factories.
I have a JSON object that I used to create a form. This JSON object is parsed by KnockoutJS.
Now, when I modify the form, I want the JSON object to be updated according to the modifications made in the form. The thing is that I don't know in advance how the form will be like but I know in the JSON Object which fields need to be updated.
I really don't know what is the best way to procede. I know that I could reconstruct the JSON Object each time something has changed but this seems like a bad idea and a tedious process.
Is there a simple way to map each JSON Object field to form items in KnockoutJS ?
Here's a JSFiddle of what I'm currently doing:http://goo.gl/ZBaV7
Update :
I realized something interesting with this line:
<input type="text" data-bind="value: $data.value, attr : { disabled: $data.disabled }" />
I'm accessing the value directly from the array via ($data.value). Is there a way in the html to say to knockout to bind to this particular attribute in the array. I know that if the array would get reordered everything would get messed up but since I know that the only thing that can changed is this property I'm ready to take this risk ?
In other words, is there a way to manually say that when this value changes to change it in the array such as
data-bind="onChange: $data.value = this.value"
Is there a simple way to map each JSON Object field to form items in
KnockoutJS ?
Yes, If I understand what you want to do correctly. As of now, the values in your view model are not observables and won't be updated automatically as the form values change. There is a plugin to handle this mapping.
http://knockoutjs.com/documentation/plugins-mapping.html
Example: Using ko.mapping
To create a view model via the mapping plugin, replace the creation of
viewModel in the code above with the ko.mapping.fromJS function:
var viewModel = ko.mapping.fromJS(data);
This automatically creates observable properties for each of the
properties on data. Then, every time you receive new data from the
server, you can update all the properties on viewModel in one step by
calling the ko.mapping.fromJS function again:
ko.mapping.fromJS(data, viewModel);
Hopefully this helps.
If your Knockout ViewModel matches your form, you could just use the built in ks.toJSON()
http://knockoutjs.com/documentation/json-data.html
A better option, especially if your form is large or complex, is to use either the mapping or viewmodel plug-ins
http://knockoutjs.com/documentation/plugins-mapping.html
http://coderenaissance.github.io/knockout.viewmodel/
The simplest way to turn your json model into a usable knockout model is with the mapping plugin.
Alternatively, you can manually copy fields from your json model into ko.observable members of your view model, which give you more control and lets you choose to skip read-only properties.
Can you associate a component or element in EXT-JS with a arbitrary object?
e.g. store(component, 'key', obj) or get(component,'key');
I am not quite sure about the solution to this question, but you should check #extjs # irc.freenode.net if nothing comes up here. They are very helpful people.
Hope this helps somehow.
I just ran across this old unanswered question while retagging, so for posterity...
All referenced elements and created components are automatically cached in global hashes by the Ext framework. For elements, you would retrieve them like so:
var myEl = Ext.get('myId');
Components are managed by the ComponentManager singleton and retrieved like so:
var myComp = Ext.getCmp('myId');
If you simply want to store an arbitrary reference to an element, component or anything else for that matter, you can do so in any way that you would normally do it generically in JS (store off the var reference directly in application-level scope, store it in an array or hash object, etc.)