Backbone, getting parameters others than data in collection - backbone.js

Given the following json:
{
"admin": false,
"data": [
{
value: key,
value :key
},
{
value: key,
value :key
}
]
}
I defined my collection like this:
var myCollection = Backbone.Collections.extend({
url: myurl.com,
parse : function (response) {
return response.data;
}
});
It works like charm, it fill my collection with the data array, however, into the tamplate, I need to render some content when admin is equal true. But I cannot find a way to pass that value to the template.
Any chance any of u kind guys can point it into the right direction to solve this?

You could save the admin flag as a property of the collection in the parse method:
var myCollection = Backbone.Collection.extend({
model: myModel,
isAdmin: false,
...
parse : function (response) {
this.isAdmin = response.admin; //save admin flag from response
return response.data;
}
});
Then you could retrieve it and pass it to your template or use it in any other way in the view render method:
var myView = Backbone.View.extend({
collection: new myCollection(),
...
render: function(){
//retrieve admin flag from collection:
var isAdmin = this.collection.isAdmin;
//you could add it into the json you pass to the template
//or do anything else with the flag
}
});
You can try this fiddle with a very basic render function.

Related

Backbone error: Model is not a constructor

Afternoon all, I'm relatively new to backbone and have been stumped for 3 days with this error which I have not seen before.
I have a collection 'TestCollection' which defines it's model as a function. When the collection is loaded I get an error the first time it attempts to make a model with class 'TestModel'.
The error I get is:
Uncaught TypeError: TestModel is not a constructor
at new model (testCollection.js:14)
at child._prepareModel (backbone.js:913)
at child.set (backbone.js:700)
at child.add (backbone.js:632)
at child.reset (backbone.js:764)
at Object.options.success (backbone.js:860)
at fire (jquery.js:3143)
at Object.fireWith [as resolveWith] (jquery.js:3255)
at done (jquery.js:9309)
at XMLHttpRequest.callback (jquery.js:9713)
I believe I have given both the collection and the model all of the code they should need to work. It feels like something has gone wrong with the loading, but when I put a console.log at the top of the model file I could see that it is definitely being loaded before the collection attempts to use it.
Any help would be massively appreciated.
TestCollection:
define([
'backbone',
'models/testModel'
], function(Backbone, TestModel) {
var TestCollection = Backbone.Collection.extend({
model: function(attrs) {
switch (attrs._type) {
case 'test':
console.log('making a test model')
return new TestModel();
}
},
initialize : function(models, options){
this.url = options.url;
this._type = options._type;
this.fetch({reset:true});
}
});
return TestCollection;
});
TestModel:
require([
'./testParentModel'
], function(TestParentModel) {
var TestModel = TestParentModel.extend({
urlRoot: 'root/url',
initialize: function() {
console.log('making test model')
}
});
return TestModel;
});
File where TestCollection is made:
define(function(require) {
var MyProjectCollection = require('collections/myProjectCollection');
var TestCollection = require('collections/testCollection');
Origin.on('router:dashboard', function(location, subLocation, action) {
Origin.on('dashboard:loaded', function (options) {
switch (options.type) {
case 'all':
var myProjectCollection = new MyProjectCollection;
myProjectCollection.fetch({
success: function() {
myProjectCollection.each(function(project) {
this.project[project.id] = {};
this.project[project.id].testObjects = new TestCollection([], {
url: 'url/' + project.id,
_type: 'test'
});
});
}
});
}
});
});
I've had a look around stack overflow, it does not appear to be the issue below (which seems to be the most common issue).
Model is not a constructor-Backbone
I also do not think I have any circular dependencies.
Any help would be massively appreciated as I am completely stumped. I've tried to include only the relevant code, please let me know if additional code would be useful.
Thanks
I can't say for other parts of the code but an obvious problem you have is misunderstanding what data is passed to the model creator function.
var TestCollection = Backbone.Collection.extend({
model: function(attrs) {
switch (attrs._type) { // attrs._type does exist here
console.log( attrs ); // Will print { foo: 'bar' }
case 'test': // This will always be false since attrs._type does not exist
console.log('making a test model')
return new TestModel();
default:
return new Backbone.Model(); // Or return some other model instance,
// you MUST have this function return
// some kind of a Backbone.Model
}
},
initialize : function(models, options){
this.url = options.url;
this._type = options._type;
this.fetch({reset:true});
}
});
new TestCollection([ { foo: 'bar' }], {
url: 'url/' + project.id,
_type: 'test' // This will NOT be passed to the model attrs, these are
// options used for creating the Collection instance.
})
To re-iterate. When you instantiate a Collection you pass an array of plain objects [{ foo: 'bar'}, { foo: 'baz'}] ( or you get them via fetch like you're doing ). That object will be passed as the attrs parameter in the model function, and the model function MUST return at least some kind of a Backbone.Model instance so you need a fallback for your switch statement.

Backbone is interpolating query string in model URL. How do I stop it?

I have a Backbone model that looks like this
...
var Address = Backbone.Model.extend({
urlRoot: '/address/'
});
return { address: Address }
});
I have a template that prints out an address in a form. The template is rendered by a view that is passed an address id in it's 'render' function. The view is reached by a route like this 'address/:id'.
The view looks like this:
var AddressView = Backbone.View.extend({
el: $('#myclass'),
render: function(options) {
var that = this;
var addr = new A.address({id: options.aid});
addr.fetch({
reset: true,
success: function(address) {
var template = _.template(ATemplate, {address: address});
that.$el.html(template);
}
});
return this;
},
events: {
'submit .edit-address-form': 'editAddress'
},
editAddress: function(ev) {
//serializeObject creates JSON object from form data
var addressDetails = $(ev.currentTarget).serializeObject();
addr.save(addressDetails, function(addr) {
R.router.navigate('', {trigger: true});
});
return false;
}
});
return {
addressView: new AddressView()
};
});
There are two problems. The first problem is that the 'editAddress' function is never getting called, even though the class name is correct and the button type = is 'submit'.
The second problem is when I submit the address form the URL is garbled, a query string is interpolated between the base URL and /#/address, as in
http:///ldmanclient/address=2500+Moffitt+Library&address2=University+of+California%2C+Berkeley&city=Berkeley&zipcode=94720&mailcode=6000&aid=1#/address/1
Has anyone seen this type of behavior before? What am I doing wrong?
As mu said, the form is being submitted the standard way before Backbone gets to it. Try preventing the submit action:
editAddress: function(ev) {
ev.preventDefault();
// same code as above
}

backbone.js getting data into collection and template

i am new to backbone.js and need a little help sending data to a template. Im using a model with fetch, and a collection. here is the code :
(function($) {
var UserModel = Backbone.Model.extend({
urlRoot : '/users',
defaults : {
name : '',
email : ''
},
initialize : function() {
_.bindAll(this);
this.fetch();
},
parse : function(res) {
return JSON.stringify(res);
},
});
var users_coll = Backbone.Collection.extend({
//model: UserModel
initialize : function() {
var u = new UserModel();
this.model = u;
}
});
var displayView = Backbone.View.extend({
initialize : function() {
this.collection = new users_coll();
//_.each(this.collection.models, alert);
//console.log(this.collection);
//alert(JSON.stringify(this.collection.models));
this.render();
},
render : function() {
var tmpl = _.template($("#data-display-tpl").html());
this.$el.html(tmpl);
}
});
var view = new displayView({
el : $("#data-display")
});
})(jQuery);
it's working fine upto the model part. In the parse function of the model, i have used console.log() and everything seems fine. i get a properly formated json, and the fetch works fine too.
however in my collection i get nothing when i try console.log(user_coll.models).
i think i am probably missing something really small. not sure what, maybe the flow of things is all wrong.
I tried to modify your code just a bit to get poin trough...hope it helps clarify few basics.
I also didn't try provided example, but in theory it should work ;)
Here is how his example should be done...
Let's imagine Twitter app for example. Twitter app has only one model that represents one user in system. That's UserModel
var UserModel = Backbone.Model.extend({
urlRoot : '/user', // this is just for modifying one specific user
defaults : {
name : '',
email : ''
},
initialize : function() {
_.bindAll(this);
//this.fetch(); // WRONG: This call was "wrong" here
// fetch() should be done on Collection not model
},
parse : function(res) {
return JSON.stringify(res);
},
});
Now, you can have many lists of users on Twitter right. So you have two lists. In one list you have Friends users, and in other Family users
var UsersFriendsCollection = Backbone.Collection.extend({
model: UserModel // you tell Collection what type ob models it contains
url: '/users/friends',
initialize : function() {
// jabadaba whatever you need here
}
});
var UsersFamilyCollection = Backbone.Collection.extend({
model: UserModel // you tell Collection what type ob models it contains
url: '/users/family',
initialize : function() {
// jabadaba whatever you need here
}
});
...
var displayView = Backbone.View.extend({
initialize : function() {
this.collection = new UsersFriendsCollection();
this.collection.fetch(); // so you call fetch() on Collection, not Model
console.log(this.collection); // this should be populated now
//_.each(this.collection.models, alert);
//alert(JSON.stringify(this.collection.models));
this.render();
},
render : function() {
// collection data is avail. in templating engine for iteration now
var tmpl = _.template($( "#data-display-tpl" ).html(), this.collection);
this.$el.html(tmpl);
}
});
A collection's model attribute is meant for specifying what type of model the collection will contain and if specified you can pass the collection an array of raw objects and it will add and create them. From the docs
Override this property to specify the model class that the collection
contains. If defined, you can pass raw attributes objects (and arrays)
to add, create, and reset, and the attributes will be converted into a
model of the proper type
So when in your code you have
var u = new UserModel();
this.model = u;
You aren't actually adding the model to the collection. Instead you can use the collections add or fetch methods.

Laravel 4 and Backbone not playing well together -- JSON improperly formatted

I'm trying to return JSON output from a Laravel route. Here's my route:
Route::get('main-contact-count', function() {
$mc = MainContact::where('flag', '=', '1')->count();
return Response::json(['count' => $mc]);
});
If I look at the response tab in Firebug, I'm getting back:
{"count":9}
The JSON encoding is missing the square brackets. This JSON is different than the JSON Laravel returns from a resourceful controller. It has the square brackets. Backbone parses it fine. Backbone doesn't parse the above JSON correctly. If you look at the length and models when you console.log a collection, they're both zero. You can, however, drill down into the object and you can find a count property set to 9.
How do I get the correct JSON out of Laravel?
Just for kicks and giggles, I'm posting my Backbone, in case I'm doing something hinky there:
App.Collections.Count = Backbone.Collection.extend({
model: App.Models.Count,
initialize: function(models, options) {
this.fetch({
success: function(data, options) {
// console.log(data.models);
}
});
if (options) {
this.url = this.url || options.url;
}
}
});
App.Views.Count = Backbone.View.extend({
tagName: 'span',
className: 'leadCount',
template: _.template($('#contactCount').html()),
initialize: function() {
},
render: function() {
this.$el.html(this.template(this.collection.toJSON()));
return this;
}
});
And in my route:
var mc = new (App.Collections.Count.extend({ url: 'main-contact-count' }))();
var mcv = new (App.Views.Count.extend({ collection: mc }))();
The JSON response sent by your service is a single JSON object ({...}) Your collection is not able to parse it because it expects an array of JSON objects ([{...}, {...}]).
If I've understood your code correctly, /main-contact-count is a specialized service whose function is to return and single datum: a count of contacts. If this is the case, Backbone.Collection may not be the correct solution. Collections are meant for... well, collections of things.
You should use just a model instead:
App.Models.Count = Backbone.Model.extend({
url:'main-contact-count'
});
var countModel = new App.Models.Count();
countModel.fetch({success: function(model, resp) {
var count = model.get('count');
});
Or even better, forego Backbone altogether and just fetch the data using jQuery:
$.getJSON('main-contact-count', function(response) {
var count = response.count;
});
try:
return Response::json(array(array('count' => $mc));

populating nested collection with nested json

Solution
in my route
Myapp.Routes = Backbone.Router.extend({
init: function(){
user = new User();
user.fetch({user,
success: function(response){
user.classlist = new classes(response.attributes.classes);
});
}
});
I've got a serialized json array being returned from my server, and I am trying to put the nested objects into my nested collections.
This answer, I thought was going to get me there, but I'm missing something.
How to build a Collection/Model from nested JSON with Backbone.js
The json which I am trying to populate my nested model with is
{first_name: "Pete",age: 27, classes: [{class_name: "math", class_code: 42},{class_name: "french", class_code: 18}]}
I create my user model
MyApp.Models.Users = = Backbone.Model.extend({
initialize: function(){
this.classlist = new MyApp.Collections.ClassList();
this.classlist.parent = this;
}
});
I had tried to follow the example on the other page, and use
this.classlist = new MyApp.Collections.ClassList(this.get('classes'));
this.classlist.parent = this;
but this.get('classes') returns undefined.
I've also tried getting the classes array through this.attributes.classes, but that is also undefined.
------------updated to include re-initialize --------------------
The function where I am initializing the user and classes is in the User routes and is called re-initialize. I use this function to fetch the user and their classes and store the object.
re_initialize: function(id){
user = new MyApp.Models.User();
MyApp.editingClasses.url = 'classes/'+id;
MyApp.editingClasses.fetch({
success: function(response){
MyApp.editingClasses.parse(response);
}
});
new MyApp.Views.ClassesInput();
},
As you can see, I'm calling the parse explicitly in the success function, but it isn't adding the classes to the collection.
I can't include the 'collection' because for some reason I can't access it in backbone.
the user model, after getting returned to backbone includes the classes array, which I am trying to put into the ClassList collection.
The user model object copied from the javascript terminal looks like this.
attributes: Object
created_at: "2012-01-05T16:05:19Z"
id: 63
classes: Array[3]
0: Object
created_at: "2012-01-18T20:53:34Z"
id: 295
teacher_id: 63
class_code: 42
updated_at: "2012-01-18T20:53:34Z"
class_name: math
__proto__: Object
1: Object
2: Object
length: 3
__proto__: Array[0]
You can use the parse function to pre-process the server response:
MyApp.Models.Users = Backbone.Model.extend({
parse: function(response) {
var classesJSON = response.classes;
var classesCollection = MyApp.Collections.ClassList(classesJSON);
response.classes = classesCollection;
return response;
}
});
var user = new MyApp.Models.Users();
user.fetch();
// You should now be able to get the classlist with:
user.get('classes');
That said, the approach suggested in the other question should also work. It could be that when your initialize function is called, the model hasn't yet been populated with the data?
For example, if you're doing:
var user = new MyApp.Models.Users();
It won't have any attributes yet to give to the classlist collection. Could that be your problem?
Okay! you can maybe fetch the classes this way :
Model :
window.person = Backbone.Model.extend({
defaults: { }
});
Collection :
window.ClassesCollection = Backbone.Collection.extend({
model: person,
url: "http://your/url/data.json",
parse: function(response){
return response.classes;
}
});
Router :
window.AppRouter = Backbone.Router.extend({
routes: {
"" : "init"
},
init: function(){
this.classesColl = new ClassesCollection();
this.classesColl.fetch();
this.classesView = new ClassesView({collection: this.classesColl});
}
});
View : (for rendering every classes)
window.ClassesView = Backbone.View.extend({
el: $('...'),
template: _.template($("...").html()),
initialize: function() {
this.collection.bind("reset", this.render, this);
},
render: function(collection) {
_.each( collection.models, function(obj){
...
//obj.get('class_name') or obj.get('class_code')
...
}, this );
...
return this;
}
});

Resources