Saving and restoring an observableArray containing nested observableArrays - javascript

I have a model which I need to save to JSON and re-load as required. The idea is that the user can save the model to a basket or database (as a JSON string) and then reload it. It's simple enough to create the JSON and save it but re-loading is seemingly much more difficult. The problem is that my model includes some complex objects similar to the Knockout Contacts example:
http://knockoutjs.com/examples/contactsEditor.html
I can even generate the JSON and pass it via a web service into an .NET object.
I've looked at the Knockout documentation for loading a data model:
http://knockoutjs.com/documentation/json-data.html
...and I can easily restore my "simple" observables but I don't see how I can initialise my arrays of complex objects where these objects also contain observable arrays. Any ideas?
I've even tried using the mapping plugin but the resulting JSON doesn't contain the information in the arrays.
Doing this creates the JSON and all my data is in there:
var jsonData = JSON.stringify(ko.toJS(self.contacts), null, 2)
Ideally, I just need to reverse this or at least create all the objects one by one.
Thanks for looking :)

The mapping plugin should work. I'm not sure if i understood correctly your situation, but try somethig like that:
ViewModel = function() {
this.contacts = ko.observableArray();
}
var model = new ViewModel();
ko.applyBindings(model);
var json = '{"contacts":[{"name":"Tom","phones":["00000000","1111111111"]},{"name":"Bill","phones":["222222222","333333333"]},{"name":"Bert","phones":["444444444","55555555"]}]}';
ko.mapping.fromJSON(json, {}, model);
The observableArray of "contacts" and "phones" in each contact are deserialized correctly from the json string.
http://jsfiddle.net/k4Sth/1/

Related

Knockout view model serialize to db and retrieve

I Have web form which use knockout and i have to implement a new feature to save form as a draft to db and later load again to modify or submit.
Is there any feature on knockout framework to serialize viewmodel to any other form(like json) that i could save to db. then later load it and populate my view easily.
I know that i can save viewmodel as a json to db and then later i can load it and fill each property on view model like below. but im looking for a feature like serialize and later populate whole viewmodel at once using it.I have lot of properties and i don't want to fill each property by writing a code line as below.
var someJSON = /* fetched the saved viewmodel as a json */;
var parsed = JSON.parse(someJSON);
// Update view model properties
viewModel.firstName(parsed.firstName);
viewModel.pets(parsed.pets);
Use the mapping plugin and replace your code with that one:
var someJSON = /* fetched the saved viewmodel as a json */;
var parsed = JSON.parse(someJSON);
// Update view model properties
viewModel = ko.mapping.fromJS(data);
You can use the mapping plugin as other questions have mentioned here, but it definitely isn't perfect.
Most notably, if you have object properties, they won't be converted into observables.
var pojo = {
someStringProperty: 'lol',
someObjectProperty: { }
};
var vm = ko.mapping.fromJS(pojo);
if(!ko.isObservable(vm.someObjectProperty)) console.log('FAIL!');
I've looked into forking, but it's kinda not worth it. I just post-process the object graph looking for properties that aren't observable and that contain an object and convert them into observable properties.

Get entire model in Backbone JS

In backbone javascript models, we get individual items as shown below:
var type = this.model.get("type");
Here, type will be defined in server side & then fetched in JS using above syntax.
My question is how do I get the entire model in one shot?
I tried this.model.toString() but it prints [object object]
Any suggestion?
EDIT: I'm using above line of code in backbone view & not the model. And in this view, I need to read all the models data even if its JSON string, thts fine with me. How do I get it. I don't want to use separate collection or anything else. I need to update the above view only to get entire model.
You can use model.toJSON() to get all the attributes of a model.
you use a collection
http://backbonejs.org/#Collection
You can then iterate though the collection to obtain each model.
var Library = Backbone.Collection.extend({
model: Book
});
for example and then
books = new Library();
books.each(function(book) {
book.publish();
});
to iterate

Creating and updating nested objects in Ember

I got nested JSON data from the server like this:
{
name: "Alice",
profile: {
something: "abc"
}
}
and I have the following model:
App.User = Ember.Object.extend({
name: null,
profile: Ember.Object.extend({
something: null
})
})
If I simply do App.User.create(attrs) or user.setProperties(attrs), the profile object gets overwritten by plain JS object, so currently I'm doing this:
var profileAttr = attrs.profile;
delete attrs.profile
user.setProperties(attrs); // or user = App.User.create(attrs);
user.get('profile').setProperties(profileAttrs);
It works, but I've got it in a few places and in the real code I've got more than one nested object, so I was wondering if it's ok to override User#create and User#setProperties methods to do it automatically. Maybe there's some better way?
Based on your comment, you want the automatic merging behaviour you get with models (the sort of thing you get with .extend()). In that case, you could try registering a custom transformer, something like:
App.ObjectTransform = DS.Transform.extend({
deserialize: function(json){
return Ember.Object.create(json);
}
});
App.User = DS.Model.extend({
profile: DS.attr('object')
});
See: https://github.com/emberjs/data/blob/master/TRANSITION.md#json-transforms
If you are doing your server requests without an adapter you can use the model class method load() with either an array of json objects or a single object. This will refresh any known records already cached and stash away the JSON for future primary key based lookups. You can also call load() on a model instance with a JSON hash as well but it will only update that single model instance.
Its unclear why you are not using an adapter, you can extend one of the Ember Model adapters and override the the record loading there, eg. extend from the RESTAdapter and do any required transform on the JSON if required by overriding _loadRecordFromData
You can also override your models load function to transform data received if required as well. The Ember Model source is fairly easy to read so its not hard to extend to your requirements.

parsing large json data in and out of models

I have a large json server response which nests models in models. I have no way of making the server rest based. I need to parse the json tree into collections and collections within collections. I would then like to export the backbone models in the same json structure to the server after modification.
i have a collection called sections, and using this method to nest the questions collection within each section model:
http://documentcloud.github.com/backbone/#FAQ-nested
the top level of the json response is the array of sections so i have been able to pass that directly to the sections collection, I have then used the initialize method to parse out the child questions and then deleted them from the attributes. But this means i dont get the model.questions returned in any toJSON on the sections collection.
SCC.Models.Section = Backbone.Model.extend({
initialize: function() {
var questions = this.get('questions');
this.questions = new SCC.Collections.Questions(questions);
delete this.attributes.questions;
}
});
SCC.Collections.Sections = Backbone.Collection.extend({
model: SCC.Models.Section
});
SCC.Sections = new SCC.Collections.Sections();
//imagine window.SectionData is the server response
SCC.Sections.reset(window.SectionData);
Hopefully I am clear. Let me if you need more info.
thanks.
If you have a limited number of nested models which are under you control and are not subject to frequent change, the easy way is to override toJSON() method for your models that have nested collections:
SCC.Models.Section = Backbone.Model.extend({
initialize: function() {
var questions = this.get('questions');
this.questions = new SCC.Collections.Questions(questions);
delete this.attributes.questions;
},
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
json.questions = this.questions.toJSON()
return json
}
});
However, if you have a lot of nested collections which are subject to change, more abstract way would be nice.
I can think for example of storing all nested collections in model's special property (say nestedCollections) and then monkey patching Backbone.Model.prototype.toJSON so that it will always parse all nestedCollections and add them to JSON object before returning it.
Another way would be to use Backbone relational instead of nested models (it automatically handles toJSON so you won't have to think about it).

Persisting & loading metadata in a backbone.js collection

I have a situation using backbone.js where I have a collection of models, and some additional information about the models. For example, imagine that I'm returning a list of amounts: they have a quantity associated with each model. Assume now that the unit for each of the amounts is always the same: say quarts. Then the json object I get back from my service might be something like:
{
dataPoints: [
{quantity: 5 },
{quantity: 10 },
...
],
unit : quarts
}
Now backbone collections have no real mechanism for natively associating this meta-data with the collection, but it was suggested to me in this question: Setting attributes on a collection - backbone js that I can extend the collection with a .meta(property, [value]) style function - which is a great solution. However, naturally it follows that we'd like to be able to cleanly retrieve this data from a json response like the one we have above.
Backbone.js gives us the parse(response) function, which allows us to specify where to extract the collection's list of models from in combination with the url attribute. There is no way that I'm aware of, however, to make a more intelligent function without overloading fetch() which would remove the partial functionality that is already available.
My question is this: is there a better option than overloading fetch() (and trying it to call it's superclass implementation) to achieve what I want to achieve?
Thanks
Personally, I would wrap the Collection inside another Model, and then override parse, like so:
var DataPointsCollection = Backbone.Collection.extend({ /* etc etc */ });
var CollectionContainer = Backbone.Model.extend({
defaults: {
dataPoints: new DataPointsCollection(),
unit: "quarts"
},
parse: function(obj) {
// update the inner collection
this.get("dataPoints").refresh(obj.dataPoints);
// this mightn't be necessary
delete obj.dataPoints;
return obj;
}
});
The Collection.refresh() call updates the model with new values. Passing in a custom meta value to the Collection as previously suggested might stop you from being able to bind to those meta values.
This meta data does not belong on the collection. It belongs in the name or some other descriptor of the code. Your code should declaratively know that the collection it has is only full of quartz elements. It already does since the url points to quartz elements.
var quartzCollection = new FooCollection();
quartzCollection.url = quartzurl;
quartzCollection.fetch();
If you really need to get this data why don't you just call
_.uniq(quartzCollecion.pluck("unit"))[0];

Categories