I am working with mongoLab and the model id looks like this
"_id": {
"$oid": "50f9a0f5e4b007f27f766cf3"
},
I am using the idAttribute to set the model id to _id and everything works fine until I attempt to update the model.
Because the _id attribute exists in the model, I am getting an error when I attempt to insert.
Do I need to remove the attribute _id from my attributes? I was under the assumption that the magic of Backbone would clean up the attributes appropriately
You would need to remove the _id attribute.
In the MongoLab REST API, the id isn't part of the data payload itself, but that isn't the case for all backends. It probably makes more sense for Backbone to assume that the id should be present in the payload, than it would to assume it should not.
That being said there's no real nice way to get Backbone to clean the id from the payload automatically. Your best bet without monkeypatching/rewriting too much of the code would probably be to override Model#toJSON, something akin to:
Backbone.Model.prototype.toJSON = function (options) {
var attrs = _.clone(this.attributes);
// In this case you'd have to pass `includeId: true` to `toJSON` when you
// actually *want* the _id in the output.
return options && options.includeId ? attrs : _.omit(attrs, '_id');
};
You could also monkeypatch sync, something like:
var sync = Backbone.sync;
Backbone.sync = function (method, model, options) {
options || (options = {});
// if options.attrs is present, Backbone will use it over dumping toJSON
if (!options.attrs) options.attrs = _.omit(model.attributes, '_id');
return sync.call(Backbone, method, model, options);
};
had the same issue where _id translated to null when in javascript..
had to do something like..
var myModel = Backbone.Model.extend({
parse: function(response){
var response = response.whatever;
response.id = response.null;
delete response.null;
return appointment;
}
});
or for a collection
systems.forEach(function(system){
console.log(system);
system.id = system.null;
delete system.null;
});
Related
I'm trying to JSON.stringify() the model of a route inside the controller by using the below code. It works and it returns all model attributes, except for the actual id of the model. Can we receive the id as well?
var plan = this.get('model');
var reqBody = JSON.stringify(
{
plan,
token
});
You need to pass in the includeId option to the toJSON method in order to get the ID in the JSON.
var plan = this.get('model');
var reqBody = JSON.stringify({
plan: plan.toJSON({ includeId: true }),
token
});
And if you didn't know, JSON.stringify() will call toJSON() for you (which is what is happening in your case). If you want to call JSON.stringify() instead of model.toJSON({}), you can always override it:
App.Plan = DS.Model.extend({
toJSON: function() {
return this._super({ includeId: true });
}
});
That way JSON.stringify(plan) will give you exactly what you want.
I want use Backbone.save the model,and the model's nest data need to be filter,so i use
model.save(null,{
success: ...,
error:...,
data: {
id:null,
name:'myname',
nestmodel: {
id:'xx'/*Other data i don't need it,so just id column*/
}
}
}
And I don't want to use patch HTTP METHOD. Because i just add a new model,not change part data.
And i don't want to post some nestmodel data,Because it's to big and i just want the id is ok.
And nestmodel just need the id.
I have read Exclude model properties when syncing (Backbone.js) and Backbone.js/express.js parameters for model.save()
There is a way to solve that problem.
That's whole my code:
sync: function(method, model, options) {
var data, orderSuiteItems;
if (method === 'create') {
options.url = this.url;
} else {
// MUST setting the url .options's url is undefined
options.url = this.url + this.idUrl(this.get('id'));
}
// IF `create` or `update` , pick the we need properties
if (method === 'create' || method === 'update') {
orderSuiteItems = [];
if (this.has('orderSuiteItems')) {
// Because the `dishes` and `dishesUnitPrice` have a lot of prop,
// Each of `dishes` or `dishesUnitPrice` may each have 10K data
orderSuiteItems = _.map(this.get('orderSuiteItems'), function(osi) {
return {
id: osi.id,
qty: osi.qty,
servingQty: osi.qty,
confirmQty: osi.confirmQty,
deleted: osi.deleted,
orderItem: _.pick(osi.orderItem, 'id'),
dishes: _.pick(osi.dishes, 'id'), // HAVE a large prop
dishesUnitPrice: _.pick(osi.dishesUnitPrice, 'id'), // HAVE a large prop
orderItemStatus: osi.orderItemStatus,
piece: osi.piece
};
});
}
data = {
id: this.get('id'),
order: this.get('order'),
price: this.get('price'),
dishes: _.pick(this.get('dishes'), 'id', 'isSuite'),
dishesUnitPrice: _.pick(this.get('dishesUnitPrice'), 'id'),
qty: this.get('qty'),
servingQty: this.get('servingQty'),
confirmQty: this.get('confirmQty'),
sum: this.get('sum'),
orderSuiteItems: orderSuiteItems,
orderItemStatus: this.get('orderItemStatus')
};
// Setting attrs with pick data.
options.attrs = data;
return Backbone.sync(method, model, options);
} else {
return Backbone.sync(method, model, options);
}
}
I hope you just put the data option for the sake of the example's clarity.
Anyway, how about using unset to remove your attribute just before using Model#save? Re-set it just afterwards.
Another solution would be to override the Model#save method.
You could also shadow the same method by defining it as a property and not in the prototype (that'd give you the opportunity to switch back).
Solution #1 or something similar would be the easiest. Solution #2 may be more, let's say, risky, but would have maybe less boilerplate. I would use the #3 only in some very specific case (can't even think about one as of now) that would include: object being a singleton (because we're not using the prototype)(or only in a limited number), need to switch the 2 modes a lot, better to have only 1 method.
Edit:
Solution #1:
var nestedModel = myModel.get('nestmodel');
myModel.save('nestmodel', nestedModel.id, {silent: true});
myModel.set('nestmodel', nestedModel, {silent: true});
I added the silent flag as I don't know if you're listening to your nestmodel attribute's changes. I'll add code for the other solutions if this one doesn't suit you.
I just recently started using Backbone.js and I'm working on an app now using Brunch that does a JSONP request to an external API to populate my collection and models. I'm following these previous posts (this and this) on doing JSONP requests with Backbone, but my collection still isn't getting the data for some reason.
My model (app/models/model.js):
module.exports = Backbone.Model.extend({
});
My collection (app/models/collection.js):
var Post = require('./model');
module.exports = Backbone.Collection.extend({
model: Post,
url: "http://somedata.com/api/posts/list/stuff",
sync: function(method, model, options) {
options.timeout = 10000;
options.dataType = "jsonp";
options.jsonp = "JSONPcallback";
return Backbone.sync(method, model, options);
},
parse: function(response) {
if (response) {
var parsed = [];
for(var i = 0; i < response.results.length; i++) {
parsed.push(response.results[i][0]);
}
return parsed;
}
}
});
Then, in the initialize method in app/application.js I'm calling it by:
var Category = require('models/collection');
this.cat = new Category();
this.cat.fetch();
Now, when I look at the parse function in console.log, I see the data being fetched, so the request is going through successfully. However, when my views are rendered and I do console.log(application.cat.models) in app/views/view.js, I get nothing -- why's this happening? Is there anything wrong with the code on my model/collection?
Also, the JSONP data has the following format, which is why looping through for response.results[i][0] and returning an array with all of it, that should do the trick, right?
{"results":[
{"0":{"id":xxx,"title":xxx,"link":xxx},
"description":xxx},
{"0":{"id":xxx,"title":xxx,"link":xxx},
"description":xxx},
{"0":{"id":xxx,"title":xxx,"link":xxx},
"description":xxx},...
]}
Would really appreciate any help...
I have 2 comments here :
I see that you have names both your model and collection as module.exports , a common practice is to make the model as singular (module.export) and make the collection for those models plural module.exports , just common practice , nothing "wrong" otherwise
You can have 2 callbacks in your code , when the collection is done fetching data(asynchronous event) also considering module.exports as your collection here ,
A. You could do this :
module.exports.fetch({
success : function(data){
console.log(JSON.stringiy(data));
//do remaining programming here
}
});
B. you could have a event listener for reset , from the documentation here , the collection fires a reset event when it completes the fetch , so could add an event listener on the collection like this :
module.exports.on('reset',function(data){
console.log(JSON.stringify(data));
//do remaining programming here
},this);
I'm looking for a classy strategy to retrieve the current user information with emberjs (and ember-data).
Currently, I do the following :
My API responds to /users/me with the current user.
In my adapter, when the retrieved object id isn't the one I'm hoping for, I add a real_id which is the id (the user's id in the database) and I replace the real returned id by what I'm expecting.
So when my adapter has "me" as user's id, but the server returns 1, I get the following json :
{"real_id": 1, "id": "me"}
This works. But I'm not a big fan, and I'd like to avoid as mush as possible to change the default content of the adapter.
Do you use any strategy for this ? What would you recommend ?
I would use a controller App.currentUserController for this.
App.CurrentUserController = Ember.ObjectController.extend({
content: null,
retrieveCurrentUser: function() {
var controller = this;
Ember.$.getJSON('/users/me', function(data) {
App.store.load(App.User, data);
var currentUser = App.store.find(data.id);
controller.set('content', currentUser);
});
}
});
get = Em.get
set = Em.set
App.AccountController = Em.Controller.extend
contentBinding: '_content.firstObject'
init: ->
set #, "_content", App.User.find()
I need to pass an id to a collection for use in the url (e.g. /user/1234/projects.json) but am not sure how to do this, an example would be wonderful.
The way my application is structured is on launch a collection of 'users' is pulled and rendered, I then want when a user is clicked their 'documents' are pulled from the server into a new collection and rendered in a new view. The issue is getting the user id into the documents collection to give the relevant URL for the documents.fetch().
think I've got it, here is an example:
//in the the view initialize function
this.collection = new Docs();
this.collection.project_id = this.options.project_id;
this.collection.fetch();
//in the collection
url: function() {
return '/project/api/' +this.project_id+'/docs';
}
Your user collection url should be set to /user. Once that's set, your models should utilize that url in order to do their magic. I believe (not completely positive) that if a model is in a collection, calling the 'url' method will return /user/:id. So all your typical REST-ish functionality will be utilized on '/user/:id'. If you are trying to do something with a relationship (a user has many documents) it's kind of rinse and repeat. So, for your documents collection (which belogs to user correct?) you'd set the url to 'user_instance.url/documents'.
To show a one to many relationship with a backbone model, you'd do something like this (upgrade to backbone 0.5.1 for urlRoot):
var User = Backbone.Model.extend({
initialize: function() {
// note, you are passing the function url. This is important if you are
// creating a new user that's not been sync'd to the server yet. If you
// did something like: {user_url: this.url()} it wouldn't contain the id
// yet... and any sync through docs would fail... even if you sync'd the
// user model!
this.docs = new Docs([], {user_url: this.url});
},
urlRoot: '/user'
});
var Doc = Backbone.Model.extend();
var Docs = Backbone.Collection.extend({
initialize: function(models, args) {
this.url = function() { args.user_url() + '/documents'; };
}
});
var user = new User([{id: 1234}]);
user.docs.fetch({ success: function() { alert('win') });
Why do you need to override the URL property of the collection with a function?.. you could do:
this.collection = new Docs();
this.collection.project_id = this.options.project_id;
this.collection.url = '/project/api/' + this.options.project_id + '/docs';
this.collection.fetch();
I like the answer from Craig Monson, but to get it working I needed to fix two things:
Binding the User url method before passing it to the Docs
A return statement from the url function in Docs
Updated example:
var User = Backbone.Model.extend({
initialize: function() {
// note, you are passing the function url. This is important if you are
// creating a new user that's not been sync'd to the server yet. If you
// did something like: {user_url: this.url()} it wouldn't contain the id
// yet... and any sync through docs would fail... even if you sync'd the
// user model!
this.docs = new Docs([], { user_url: this.url.bind(this) });
},
urlRoot: '/user'
});
var Doc = Backbone.Model.extend();
var Docs = Backbone.Collection.extend({
initialize: function(models, args) {
this.url = function() { return args.user_url() + '/documents'; };
}
});
var user = new User([{id: 1234}]);
user.docs.fetch({ success: function() { alert('win') });