Backbone adding data from model A to Collection with model B - javascript

In a model I have one some attributes.
I would like one of the attributes to be placed in a seperate collection with another model, after I altered the data.
Now altering the data is not a problem and I know how to create the new object.
However I don't know what the best way to go is with:
where to alter the data
how to get it in my collection.
I tried several things from sending the complete attribute to the collection, where I do I have a parse which should give back a new object.
This however fails.
I also tried to do the same with the model and a parse.
Then I tried to just return the object in the 'url' section of the collection.
but as expected this does not work either. After which I tried to stringify the object to json, but this didn't work either.
So now I am thinking it might be the best way to start altering the data in Model A, after the data has ben received, and then push or add the data to the collection.
Maybe anyone else has a better idea of doing this?
And how do I know the data is in Model A, so I can start running the script?
First try in the collection: by URL
SatPhotoDataCollection = Backbone.Collection.extend({
model: SatPhotoDataModel,
url: function() {
var obj = satPhotoModel.attributes.Layer;
var layerNames = {};
for (var i = 0; i < obj.length; i+=1) {
//do some massive things filling layerNames
}
return layerNames;
}
});
Same thing, not using URL but parse, sending the 'satPhotoModel.attributes.Layer' to the collection using:
SatPhotoDataCollection.add(satPhotoModel.attributes.Layer);
SatPhotoDataCollection = Backbone.Collection.extend({
model: SatPhotoDataModel,
url: "",
parse: function(data) {
var layerNames = {};
for (var i = 0; i < data.length; i+=1) {
//do some massive things filling layerNames
}
return layerNames;
}
});
Of course I did the same in the model satPhotoDataModel (model B) in the URL and Parse, however I must the 'layerNames' I return is really a collection of 'layers', so hence the need for a collection ;)
Now in the satPhotoModel (model A) which will have the Layer in it's attributes I could also try something like:
SatPhotoModel = Backbone.Model.extend({
initialize: function() {
},
url: "http://geoservertest/geoserver/DIWADIS/ows?service=WMS&version=2.0.0&request=GetCapabilities",
sync: function(method, model, options) {
options.dataType = "xml";
options.crossDomain = true;
options.contentType = 'application/json; charset=utf-8';
return Backbone.sync(method, model, options);
},
//we are expecting an XML since we want a normal object, we do this with parsing
parse: function(data) {
var obj = $.xml2json(data);
this.parseLayers(obj.Capability.Layer);
return obj.Capability.Layer;
},
defaults: {
Abstract: "",
BoundingBox: {},
CRS: [],
Ex_GeographicBoundingBox: {},
Layer: [],
Tile: ""
},
parseLayers: function(data) {
var layerNames = {};
for (var i = 0; i < data.length; i+=1) {
//do some massive things filling layerNames
}
SatPhotoDataCollection.add(layerNames);
}
});
I am just doubting if that is really the correct way to go.
So any help and enlightenment would be awesome :-)

Related

Load Model From More Than One URL

How can I load different attributes for a model using different URLs?
E.g. I have 3 different and independent URLs that will return some attributes and I want to add all of those to a single model, suppose that I have the promise returned by all of them like this:
var model = //...
$.when(nameAttributes, addressAttributes, metaAttributes).then(
function(nameData, addressData, metaData) {
return _.extend({}, nameData, addressData, metaData);
})
.done(function(allData) {
model.set(allData);
doStuffWith(model);
});
Is there a way to turn that into this:
model.fetch().done(function(){ doStuffWith(model); });
Well.. Ok, this should do what you want. I STRONGLY suggest you do NOT take this route. Keep the data separately in individual Model()s that way you can update it and pull it when ever you need to. If you take this approach saving data will be a disaster.
http://jsfiddle.net/kjhvwxg4/
PS. I tried to not add any more code then i had to, keep in mind this will probably not handle error handling very well since xhr will never throw an error, you need to catch it yourself -- i didnt want to spend the time coding that since this is just a proof of concept.
var Model1 = Backbone.Model.extend({
fetch: function(options) {
options = _.extend({
parse: true
}, options);
var model = this;
var success = options.success;
var error = options.error;
options.success = function(resp) {
var serverAttrs = options.parse ? model.parse(resp, options) : resp;
if (!model.set(serverAttrs, options)) return false;
if (success) success.call(options.context, model, resp, options);
model.trigger('sync', model, resp, options);
};
options.error = function(resp) {
if (error) error.call(options.context, model, resp, options);
model.trigger('error', model, resp, options);
};
// custom code starts here
var call1 = $.getJSON('http://jsonplaceholder.typicode.com/posts/1');
var call2 = $.getJSON('http://jsonplaceholder.typicode.com/comments/2');
var call3 = $.getJSON('http://jsonplaceholder.typicode.com/albums/3');
var xhr = $.when(call1, call2, call3);
// mimics the same triggers the normal backbone does
model.trigger('request', model, xhr, options);
xhr.done(function(one, two, three){
var resp = _.extend(one[0], _.extend(two[0], _.extend(three[0], {})));
options.success(resp);
});
// we still need to send back an event handler
return xhr;
}
});
var model = new Model1();
model.fetch();
model.on('sync', function(model) {
alert(JSON.stringify(model.toJSON()));
});

Check if backbone has been fetched, or changed?

I need to have two url properties inside my Backbone.Collection.extend() because if a collection is fetched then I need to use a specific url if the collection gets a new model then I want to change the url
module.exports = MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
if (fetch method is called) {
return '/api/messages/' + this.id;
} else {
// here if a model is being added?
return '/api/messages'
}
},
model: MessageModel
});
The reason for this is because I only want to pull down the models from the server based on the user.
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
messages.fetch({
success: function() {
App.data.messages = messages;
App.core.vent.trigger('app:start');
}
});
}
});
When the user creates a new model within the app I want it to go into the main collection?
Does this mean I should create a sub collection based on the main collection somehow?
Edit:
My create looks like this somewhere else in the app window.App.data.messages.create(Message); I am thinking maybe I could write something like
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
var allMessages = new MessagesCollection();
messages.fetch({
success: function() {
App.data.messages = messages;
App.data.allMessages = allMessages;
App.core.vent.trigger('app:start');
}
});
}
});
Then create window.App.data.allMessages.create(Message);> It sounds like it can cause problems IDK any ideas?
Edit:
The above worked but I had to create a new Backbone.Collection.extend() passing the same model but just writing it like
var Backbone = require('backbone'),
MessageModel = require('../models/message');
module.exports = AllMessagesCollection = Backbone.Collection.extend({
model: MessageModel,
url: '/api/messages'
});
So let me really break this question down, is this solution problematic. What is the best way to do this? The worst thing I can think of is bandwidth, using this method I would constantly be sending requests!
If you need to use different url only when create new model you can override collection.create method:
var MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/api/messages/' + this.id;
},
create: function(model, options){
var extendedOptions = _.extend(options || {}, {url: '/api/messages'});
return this.constructor.__super__.create.call(this, model, extendedOptions);
}
});

specifying a url function in backbone.js

I'm trying to specify a function url for a backbone model like this:
var Directory = Backbone.Model.extend({
defaults: {
path: '/',
entries: new DirectoryEntries
},
initialize: function() {
this.listenTo(this, 'change:path', this.fetch);
this.fetch();
},
parse: function(response, options) {
if (response != null) {
for (var i = 0; i < response.length; ++i) {
this.get('entries').add(new DirectoryEntry(response[i]));
}
}
this.trigger('change');
},
url: function() {
return '/svn-ls.php?path=' + this.get('path');
},
The initial call to fetch() in my initialize function seems to work fine, but when fetch gets called on a change to path, backbone tries to make the following JSON request: http://localhost:8000/function%20()%20%7B%20%20%20%20%20%20return%20'/svn-ls.php?path='%20+%20this.get('path');%20%20%20%20}
Ie, it seems to be trying to use this.url instead of actually calling it.
Anyone know what the problem is here?
edit: I changed the listenTo call to the following:
var self = this;
this.listenTo(this, 'change:path', function() { self.fetch(); });
and now everything seems to work. I have no idea why binding this.fetch directly messed things up, though.

Having different models for request and response - backbone.js

//A backbone model
var RequestModel = Backbone.Model.extend({});
//A backbone model
var ResponseModel = Backbone.Model.extend({});
RequestModel.save({
success: function (ResponseModel ) {
alert(ResponseModel .toJSON());
}
})
Can i have a separate Model for Request and Response, as both Request and Response does not match. Its a total RPC call and not a CRUD operation.
I've thought about this same problem before, and I feel there isn't a great way to achieve this in Backbone. The best I've come up with is to implement a fromResponse and toRequest method on the model, and override model.parse and model.sync to map the model object to them. Something like:
var Model = Backbone.Model.extend({
fromResponse: function(responseAttrs) {
var modelAttrs = {}; //map response attributes to modelAttrs
return modelAttrs;
},
toRequest: function() {
//map model attributes to response attributes here
var modelAttrs = this.toJSON();
var responseAttrs = {}; //map models attributes to requestAttrs
return responseAttrs;
},
parse: function(response) {
return this.fromResponse(response);
},
sync: function(method, model, options) {
options = options || {};
options.data = this.toRequest();
Backbone.sync(method, model, options);
}
});
If the parse and sync are overridden in some kind of a base class, then you only need to implement the fromResponse and toRequest mappers for each model.
Another option would be to override Backbone.sync altogether, and map each Model type to some kind of ModelRequestMapper and ModelResponseMapper object to (de-)serialize each model. I feel that would be more complicated, but might scale better, if you have lots of models.
/Code sample not tested

Javascript Keeping variables in scope on success callback (jquery/backbonejs/parse function)

Below I am using backbone js to fetch a collection. On the request's response, I would like to perform some logic on the collection to massage the data. I am modifying the parse method on the parent collection (source types) to attach a child collection (source accounts) and only include a source type if there are actually source accounts.
I don't want to render this without having all the data loaded primarily, so I figure with javascript the only way to be safe is with a success callback.
As you can see in the below code, inside the parse function, I construct a new object called "response_object". The issue is that when i attempt to pack this object inside the success callback, the collection does not include any of the object that i packed into it. How would you rewrite this to make this work correctly?
window.AdminSourceTypes = Backbone.Collection.extend({
model: window.AdminSourceType,
url: '/api/sources',
parse: function(response){
// create response object
response_object = [];
x = 0;
_.each(response, function(item){
//get child collection (accounts) from one of "source type's" attributes
collection = new window.AdminAccounts();
collection.url = item.accounts_url;
//get all accounts
collection.fetch({
//only add the source type to the response object
//if there are accounts associated to it
success: function(accounts_collection){
if(accounts_collection.size() > 0){
response_object[x] = item;
}
}
});
x++;
});
return response_object;
}
});
This is what I went with:
window.sources = new window.AdminSourceTypes({
events: {
'reload':'render'
}
render: function(){... bla bla .... }
});
window.sources.fetch({});
window.AdminSourceTypes = Backbone.Collection.extend({
model: window.AdminSourceType,
url: '/api/sources',
parse: function(response){
cmp = this;
response_object = [];
x = 0;
y = 0;
_.each(response, function(item){‣
collection = new window.AdminAccounts();
collection.url = item.accounts_url;
collection.fetch({
success: function(data){
x++;
if(data.size()>0){
item.accounts = collection;
window.sources.add(item);
window.sources.trigger('reload');
}
}
});
});
}
});
So instead of waiting for the success callback, i'm just going to have the parse method return nothing, and just have the item get added to the collection if it satisifes my condition.

Categories

Resources