I'm creating a Backbone Model which has a child Backbone Model inside of it:
console.log("inside add item, video:", video instanceof Backbone.Model);
var playlistItem = new PlaylistItem({
video: video,
title: video.get('title')
});
afterwards, I save it:
playlistItem.save({}, {
success: function() {
console.log("Successfully saved.");
playlistItem.get('video').get('title');
}
});
In this example, I encounter an error -- video is not an instanceof Backbone.Model after calling save. Why?
I think you will need to override parse and toJSON.
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
// replace backbone model with json.
json.video = this.get('video').toJSON();
return json;
},
parse: function(data) {
// take json of video and set into model.
this.get('video').set(data.video);
delete data.video;
return data;
},
If you don't parse the json data like this, backbone is going to take the 'video' object from the json and overwrite your Backbone model.
Related
I made a website to create some maps. So, all my maps have an id, and my map has some elements with ids.
So I create a collection Map with an id and its model is my map object :
app.collections.mapDetailsCollection = Backbone.Collection.extend({
model: app.models.mapDetailsModel,
initialize: function(options) {
this.id = options.id;
},
url: function () {
return this.absURL + '/api/maps/' + this.id;
},
parse: function(response){
return response.features;
},
toGeoJSON: function(){
var features = [];
this.models.forEach(function(model){
var feature = model.toGeoJSON();
if (! _.isEmpty(feature.geometry)) {
features.push(feature);
}
});
return {
'type': 'FeatureCollection',
'features': features
};
}
});
But my model have an id too. I don't know for a model how to return url with a collection id.
I need to return /api/maps/id_collection/id_model.
When a model is added to a collection, it receives a reference to the collection. So each model has a collection property this.collection set if it's in a collection.
From the Backbone model constructor documentation (emphasis mine):
If you pass a {collection: ...} as the options, the model gains a
collection property that will be used to indicate which collection the
model belongs to, and is used to help compute the model's url. The
model.collection property is normally created automatically when you
first add a model to a collection. Note that the reverse is not true,
as passing this option to the constructor will not automatically add
the model to the collection. Useful, sometimes.
Then, you could use that to build the full url of a model:
var app.models.mapDetailsModel = Backbone.Model.extend({
urlRoot: function() {
// Backbone adds the model id automatically
return this.collection.url() + '/my-model/';
}
// ...snip...
});
Note that the default url for a model is what you want.
Generates URLs of the form: [collection.url]/[id] by default, but
you may override by specifying an explicit urlRoot if the model's
collection shouldn't be taken into account.
I'm looking for a way to intercept the value returned from a server when I fetch a backbone model (a collection, strictly speaking) from the server, then modify it before continuing. I would think that I could do something like this
SessionController.prototype._initPages = function() {
return App.pages.fetch({
reset: true,
success: function(model, response, options) {
//modify the contents of response
}
};
And my modifications would be reflected in the model that's used to initialize the view.
However I was looking at the backbone source and I think I may have misunderstood something.
fetch: function(options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0) options.parse = true;
var success = options.success;
var collection = this;
options.success = function(resp) {
var method = options.reset ? 'reset' : 'set';
collection[method](resp, options); //this line updates the model
if (success) success(collection, resp, options); // my success callback
collection.trigger('sync', collection, resp, options);
};
wrapError(this, options);
return this.sync('read', this, options);
}
For my needs, it seems the two commented lines need to be switched, though I assume I'm just misunderstanding how to use this feature.
How can I modify the server response before it becomes my model?
I think you could just override the parse function to modify your data as needed
http://backbonejs.org/#Model-parse
So I have a Resource defined as follows:
angular.module('questionService', ['ngResource'])
.factory('QuestionService', function($http, $resource) {
var QuestionService = $resource('/api/questions/:key', {}, {
query: {
method:'GET',
isArray:true,
},
save: {
method: 'POST',
}
});
return QuestionService
});
And later on I take some form input and do the following
var newQ = {
txt: $scope.addTxt
};
QuestionService.save(newQ)
The server responds to the POST request both by reissuing the JSON for the object and setting the location header with the new unique ID. The problem is that Angular is not saving that returned JSON or the location header into the object and it is not getting set with the ID from the server for future operations. I've tried a number of things such as:
transformResponse: function(data, headersGetter) {
locationHeader = headersGetter().location;
key = locationHeader.split('/').slice(-1)[0];
data.key = key;
return data;
}
However the returned data item doesn't seem to be getting used. Am I missing something? This seems like a pretty common use case for interacting with a REST api.
Thanks!
You need to have a success handler to assign the new id to newQ manually:
QuestionService.save(newQ, function(data) {
newQ.id = data.id;
});
But there is a better to achieve the same. Because your QuestionService is a resource class object, you can instantiate your newQ like this:
var newQ = new QuestionService({text: $scope.addTxt});
Then, call $save() of newQ:
newQ.$save();
Now newQ should have the new id returned by the server.
I have Asp.Net server with WebApi2 running, i use Ok(number) to return content, and it return for example '6' as result. once it return, the angular show an object containing promise and state, and prototypes and a deep repeated hierarchy, but no value, no '6'.
So i went to see where the data goes, for seeing where the data is i define a transform, and i see awo the data, but it's not a json...
later i change it to following, and now i have a obj in my success function, which has sub property called 'returnValue' (as i defined), and this object hold my value.
transformResponse: function(data, header){
var obj={};
obj.returnValue=angular.fromJson(data);
return obj;
},
I am running this code in backbone which saves some data to the server,
GroupModalHeaderView.prototype.save = function(e) {
var $collection, $this;
if (e) {
e.preventDefault();
}
$this = this;
if (this.$("#group-name").val() !== "") {
$collection = this.collection;
if (this.model.isNew()) {
console.log("MODEL IS NEW");
this.collection.add(this.model);
}
return this.model.save({ name: this.$("#group-name").val()}, {
async: false,
wait: true,
success: function() {
//console.log($this, "THIS");
console.log('Successfully saved!');
this.contentView = new app.GroupModalContentView({
model: $this.model,
collection: $this.collection,
parent: this
});
this.contentView.render();
return $this.cancel();
},
});
}
};
This works fine the first time I run it, however if I run it again straight after saving my first piece of data it does not save new data it merely updates the last saved data. So the first time I save it runs a POST request and the next time it runs a PUT request, why would this be?
I am not sure if you need this but here is my initialise function -
GroupModalHeaderView.prototype.initialize = function() {
_.bindAll(this)
}
Your view has a model object attached to it. As I understand you fill some forms, put their data to model and save the model. But all the time you have single model object, and you only update it's data. If you want to create a new object after saving model just add a line:
this.model = new YourModelClass();
right after line console.log('Successfully saved!');
From the backbone documentation:
If the model isNew, the save will be a "create" (HTTP POST), if the
model already exists on the server, the save will be an "update" (HTTP
PUT).
If you want to make a post request even if the model is new, just override the default save implementation and call Backbone.sync(method, model, [options]) with 'create' as the passed method.
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.