Unexpectedly, the following code will print Test1 undefined instead of Test1 Test2.
var MyView = Backbone.View.extend({
initialize: function () {
console.log(this.collection, this.test);
}
});
new MyView({collection: "Test1", test: "Test2"});
What am I doing wrong?
Only the following properties from the options argument get merged in as view properties.
model, collection, el, id, attributes, className, tagName (See the source code)
Any other property gets placed on the options property of the view.
So to access the test property from the initialize method, you can update your code as follows.
console.log(this.collection, this.options.test);
Related
I'm trying to add a new model to my parent view from a child view.
In my parent view, I render the child view like this:
renderEngineOptions: function () {
var view = new EngineOptionsView({ model: this.model });
this.$('.engineOptions').append(view.render().el);
},
In my child view(engineOptionsView), I define the parent as 'this.engine' in the initialize method:
initialize: function (args) {
this.engine = args.model;
}
Then I have an html button, that when clicked, fires this event:
addTurbo: function() {
this.$('.turboOption').show();
this.engine.turbo = new Turbo();
}
Turbo is a new model that I'm trying to add to the engine. But nothing happens. I get no error in the console, but when I write out the engine model in the console, no turbo was ever added.
How can I add the turbo model to the parent, from the child?
Thanks!
When you pass a model with the model option, the view automatically apply it to itself, so it's available through this.model directly.
var view = new EngineOptionsView({ model: this.model });
in the child:
initialize: function (args) {
this.engine = args.model; // not necessary
console.log(this.model === this.engine); // same thing
}
You are adding the turbo as a property of the model
this.engine.turbo = new Turbo();
So if you want to get the turbo from the parent:
this.model.turbo;
If you want to add the turbo as an attribute of the model from the child view:
this.model.set('turbo', new Turbo());
And to get it from the parent:
this.model.get('turbo);
Without more details, it's hard to guess where the problem is coming from.
My Code:
I am new to Backbone.js and trying to build an app with Backbone.js and PHP. When I am trying to call add in the router, I am getting error:
Uncaught TypeError: Object [object Object] has no method 'set'.
Please help me to find my mistake.
Thanks.
// Models
window.Users = Backbone.Model.extend({
urlRoot:"./bb-api/users",
defaults:{
"id":null,
"name":"",
"email":"",
"designation":""
}
});
window.UsersCollection = Backbone.Collection.extend({
model:Users,
url:"./bb-api/users"
});
// Views
window.AddUserView = Backbone.View.extend({
template:_.template($('#new-user-tpl').html()),
initialize:function(){
this.model.bind("click", this.render, this);
},
render:function(){
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
events:{
"click .add":"saveUser"
},
saveUser:function(){ alert('saveUser');
this.model.set({
name:$("#name").val(),
email:$("#email").val(),
designation:$("#designation").val()
});
if(this.model.isNew()){
this.model.create(this.model);
}
return false;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"welcome",
"users":"list",
"users/:id":"userDetails",
"add":"addUser"
},
addUser:function(){
this.addUserModel = new UsersCollection();
this.addUserView = new AddUserView({model:this.addUserModel});
$('#content').html(this.addUserView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
As suggested in the comments, the problem starts here here:
this.addUserModel = new UsersCollection();
this.addUserView = new AddUserView({model:this.addUserModel});
and finishes here:
saveUser:function(){ alert('saveUser');
this.model.set({
By passing a collection in place of a model you create confusion, and as a result later in the saveUser function you try to call a Backbone.Model method (set) on a Backbone.Collection instance.
Note: As of version 1.0.0 Backbone.Collection now has a set method. In previous versions, such as the one used by the question's author, that method was instead called update.
There are several steps you can take to clarify this code. For starters, I would rename your model and collection classes so that it's clear that the model is the singular form and the collection is the plural form:
window.Users => window.User
window.UsersCollection => window.Users
Next, I would create a new User model, instead of a Users collection, and pass that to your view:
this.addUserModel = new User();
this.addUserView = new AddUserView({model:this.addUserModel});
Finally, I'd remove these lines:
if(this.model.isNew()){
this.model.create(this.model);
}
For one thing, the model will always be new (as you just created it before passing it in), but more importantly you don't need to call the Collection's create method because that method creates a new model, when you already have one created. Perhaps what you should add instead is :
this.model.save();
if your intent is to save the model to your server.
Since you already specified a urlRoot for the model, that should be all you need to create a new model, pass it to your view, have your view fill in its attributes based on DOM elements, and finally save that model's attributes to your server.
I think you are facing problem with object scope. When event fired it send to event object to that function. Just try this it may work
Declare global variable with the current view inside the initialize
initialize : function(){ self = this; }
then change this to self,
saveUser:function(){ alert('saveUser');
self.model.set({
name:$("#name").val(),
email:$("#email").val(),
designation:$("#designation").val()
});
if(self.model.isNew()){
self.model.create(this.model);
}
return false;
}
I need to create model which holds css properties for one element. My model looks like this:
StyleModel = Backbone.Model.extend( {
defaults : {
productName : '',
styles:{
'font-weight':'normal',
'font-style':'normal',
'text-decoration':'none',
'visibility':'visible'
'color':'blue',
'border-width':'1px',
'border-color':'white',
'font-color':'white'
}
},
initialize : function(property, values) {}
...}
How to notify view when I change value of some property or delete from list ?
(For example when user set border-width to 3px or when delete font-weight. Or is it better solution not to hold properties in hash and to set that every property be element in model ?)
Backbone won't recognize settings in your hash, on it's own. But you can create methods that handle this for you:
Backbone.Model.extend({
setCss: function(key, value){
var css = this.get("styles");
css[key] = value;
this.trigger("change", this, key, value);
this.trigger("change:css", key, value);
this.trigger("change:css:" + key, value);
}
});
Then you would call model.setCss("background-color", "#ff0faf") and it would trigger the three "change" events for you to bind to in your views.
In the view, you can bind the change events in the initializer, and have jQuery apply all of the styles to the DOM element that the view controls:
Backbone.View.extend({
initialize: function(){
this.model.on("change:css", this.setCss, this);
},
setCss: function(){
var css = this.model.get("styles");
this.$el.setCss(css);
}
});
You might need to clear existing css before applying the new set, to make sure you get rid of anything that was removed. More likely, though, you'll want to have a deleteCss method on the model, have it raise a css:deleted event from the model, and have the view respond to that event by removing the css attribute in question.
Use the change event that is available on the model http://documentcloud.github.com/backbone/#Model-change
Have your view bind to the event. http://documentcloud.github.com/backbone/#Events-on
I've a Backbone Collection initialized but calling invoke on collection doesn't work. For some reason I'm getting JS error:
var vw = new SomeView(); // A view with function 'refresh'
var col = new Backbone.Collection();
col.add(vw);
...
setTimeout(function(){ col.invoke('refresh'); }, 1000); // Error: Cannot call method 'apply' of undefined
However, invoking method like isEmpty works fine
console.log("Is empty? ", col.isEmpty()); // prints: 'Is Empty? false'
It seems I'm missing something very obvious.
N.B: I'm not interested in calling each function and then invoking refresh on view object because that's just clunky.
A collection in backbone is a list of models. So when you add a view to a collection internally it will call something like this col.add(Backbone.Model.extend(vw)). So it will create a new model with your view as constructor params. I f you wanna store your view in a list just use a JavaScript array or a smarter underscore collection
you try to create a collection by instantiating the collection itself,
you first have to extend from it and tell it what models it's holding
and of what i see above you are trying to put your views in a collection?
that is not possible directly, as a collection holds a list of models, not views.
you can however create a model defining your view.
var myView = Backbone.View.extend({});
var myModel = Backbone.Model.extend({});
var myCollection = Backbone.Collection.extend({ model: myModel });
$(function(){
// creating your view
var vw = new SomeView();
// creating a model for the view
var viewModel = new myModel({ linkedview : vw });
// creating a collection
var modelList = new myCollection();
modelList.add(viewModel);
});
the gist of it is, that you create a model, containing a reference to your view, and not add the view directly into the collection (which will not work)
Backbone is not using the model specified for the collection. I must be missing something.
App.Models.Folder = Backbone.Model.extend({
initialize: function() {
_.extend(this, Backbone.Events);
this.url = "/folders";
this.items = new App.Collections.FolderItems();
this.items.url = '/folders/' + this.id + '/items';
},
get_item: function(id) {
return this.items.get(id);
}
});
App.Collections.FolderItems = Backbone.Collection.extend({
model: App.Models.FolderItem
});
App.Models.FolderItem = Backbone.Model.extend({
initialize: function() {
console.log("FOLDER ITEM INIT");
}
});
var folder = new App.Models.Folder({id:id})
folder.fetch();
// later on change event in a view
folder.items.fetch();
The folder is loaded, the items are then loaded, but they are not FolderItem objects and FOLDER ITEM INIT is never called. They are basic Model objects.
What did I miss? Should I do this differently?
EDIT:
Not sure why this works vs the documentation, but the following works. Backbone 5.3
App.Collections.FolderItems = Backbone.Collection.extend({
model: function(attributes) {
return new App.Models.FolderItem(attributes);
}
});
the problem is order of declaration for your model vs collection. basically, you need to define the model first.
App.Models.FolderItem = Backbone.Model.extend({...});
App.Collections.FolderItems = Backbone.Collection.extend({
model: App.Models.FolderItem
});
the reason is that backbone objects are defined with object literal syntax, which means they are evaluated immediately upon definition.
this code is functionality the same, but illustrates the object literal nature:
var folderItemDef = { ... };
var folderItemsDef = {
model: App.Models.FolderItem
}
App.Models.FolderItem = Backbone.Model.extend(folderItemDef);
App.Collections.FolderItems = Backbone.Collection.extend(folderItemsDef);
you can see in this example that folderItemDef and folderItems Def are both object literals, which have their key: value pairs evaluated immediately upon definition of the literal.
in your original code, you defined the collection first. this means App.Models.FolderItem is undefined when the collection is defined. so you are essentially doing this:
App.Collection.extend({
model: undefined
});
By moving the model definition above the collection definition, though, the collection will be able to find the model and it will be associated correctly.
FWIW: the reason your function version of setting the collection's model works, is that the function is not evaluated until the app is executed and a model is loaded into the collection. at that point, the javascript interpreter has already found the model's definition and it loads it correctly.