How to add more than one model/collection to the Backbone View? - backbone.js

When I add collection to the view like this:
var View = new MyCollectionView({ collection: new MyCollection() });
everything is okey. I can use this collection in initialize method (for binding events, for example). But how can I add another one?
I can't do this way:
var View = new MyCollectionView({
collection: new MyCollection(),
secondCollection: new MySecondCollection()
});

From the fine manual:
constructor / initialize new View([options])
There are several special options that, if passed, will be attached directly to the view: model, collection, el, id, className, tagName, attributes and events.
So, if you create a view like this:
new View({collection: c})
then Backbone will automatically assign c to the view's this.collection. But if you create the view like this:
new View({collection: c, secondCollection: c2})
then inside the View's constructor:
initialize: function(options) {
// this.collection will be 'c' from above
// options.secondCollection will be 'c2'
}
So you can do this:
var View = new MyCollectionView({
collection: new MyCollection(),
secondCollection: new MySecondCollection()
});
provided that your MyCollectionView has an initialize method that knows to pull the secondCollection out of its options argument.
Open your JavaScript console and have a look at what this does:
var V = Backbone.View.extend({
initialize: function(options) {
var c1 = options.collection;
var c2 = options.secondCollection;
console.log(this.collection);
console.log(c1);
console.log(c2);
}
});
var view = new V({collection: 1, secondCollection: 2});
Demo: http://jsfiddle.net/ambiguous/XyeSD/

Related

How to assign different class name on view element

In my Backbone view, i am setting tagname, classname, temp value.. all are works fine apart from classname..
how do i set the classname.. or what is the mistake on my code..
define(["singleton","listCollection","listModel"],function(singleton,collection,listModel){
singleton.view = Backbone.View.extend({
tagName :'article',
className :'indBoard',
projectName : true,
template0 : _.template($('#listTemplate').html()),
template1 : _.template($('#boardTemplate').html()),
initialize :function(options){
this.template = this['template'+options.tempNo];
this.tagName = options.tagName;
//i am changing to 'li' works
this.className = options.cName;
//changing to new class name not working
console.log(options.cName);//consoles new class name properly
this.projectName = options.subTempNo == 0 ?true:false;
//condition as well works..
},
render:function(){
var temp = this.template;
this.$el.html(temp(this.model.toJSON()));
return this;
}
});
return singleton.view;
});
If you set options.className when you create your view instance instead of options.cName, you don't need to try to set it in initialize like that (same for tagName).
Try something like this instead:
var view = new singleton.view({className: 'someClass'});
className is one of the special options that Backbone looks for during the view creation.
From Backbone source:
// List of view options to be merged as properties.
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
Actually, I think the reason tagName is working for you is because it is being merged in by Backbone, and not because you are setting it in initialize.

Backbone.js - Build view with sub models

I have an edit view for a Backbone Model that I create each time the the element is clicked on. The problem I have is that the edit view needs two Backbone collections to create the edit form (it contains two <select> lists).
The view:
MyApp.elementView = Backbone.View.extend({
events: {
'click .edit': 'editForm',
},
editForm: function(ev) {
var editView = new TimeTrack.Views.EditJob({
model: this.model
// This view needs two more collections
// for the <select> elements
});
...
}
});
Instantiate the view:
var elementView = new MyApp.elementView({
collection: elementCollection
});
What is the best way to push the needed collections to the edit view? Do I have to pass the collections need for the edit view from the elementView form the instantiation? Or is there a better way of doing this?
I did so, passed in view 2 collections, 1 - the main and the other as follows:
to elementView - second collection and in elementView recive her.
example:
in router I'm
initialize: ->
(YourNameSpace).secondCollection = new (YourNameSpace).secondCollection
elements: =>
view = new (YourNameSpace).elementView( secondCollection: #secondCollection )
$('.l-yield').html(view.render().el)

How to know if element is already in the DOM when creating new Backbone view

here's the situation:
When page is opened for the first time, it already has prepared DOM by server(php).
If user has javascript turned on, then i want to convert my page to web app(or whatever you call it).
As soon as Javascript is initialized, Backbone fetches collection from server.
The problem is, that some of these fetched items are already on page.
Now how can i mark those items which already are in the DOM?
And how can i tie them up with the Backbone view?
Hooking up a Backbone.View to an existing DOM element is simple:
//get a referent to the view element
var $el = $("#foo");
//initialize new view
var view = new FooView({el:$el});
The view now handles the #foo element's events, and all the other View goodness. You shouldn't call view.render. If you do, it will re-render the view to the element. This means that you can't define any necessary code in the render method.
As to how to find out which elements are already in the DOM, and how to find the corresponding element for each view - that's a bit more complicated to answer without knowing exactly how your data and html looks like. As a general advice, consider using data-* attributes to match up the elements.
Let's say you have a DOM tree:
<ul id="list">
<li data-id="1">...</li>
<li data-id="2">...</li>
<li data-id="5">...</li>
</ul>
You could bind/render a model to the container like so:
var view;
//container element
var $list = $("ul#list");
//find item node by "data-id" attribute
var $item = $list.find("li[data-id='" + model.id+ "']");
if($item.length) {
//element was in the DOM, so bind to it
view = new View( {el:$item, model:model} );
} else {
//not in DOM, so create it
view = new View( {model:model} ).render();
$list.append(view.el);
}
Ok, i managed to do that like so:
var Collection = Backbone.Collection.extend({...});
var ItemView = Backbone.View.extend({...});
var ItemsView = Backbone.View.extend({
initialize: function () {
var that = this,
coll = new Collection;
coll.fetch({ success: function () {
that.collection = coll;
that.render();
}});
},
render: function () {
this.collection.each(this.addOne, this);
},
addOne: function (model) {
var selector = '#i'+model.get("id");
if( $(selector).length ) {
//If we are here, then element is already in the DOM
var itemView = new ItemView({ 'model': model, 'el': selector, 'existsInDom': true });
} else {
var itemView = new ItemView({ 'model':model });
}
}
});

Subclassing Backbone.View

I have several views that have common code I'd like to abstract into a custom Backbone.View class. Is there any best practices for doing this?
is a good pattern to do something like this? :
// Base Grid view
var GridView = Backbone.View.extend({
initialize : function(){
//common view init code ..
//do the plug in overrides
if (options.addHandler)
this.addHandler = options.addHandler;
if (options.events)
//?? extend default events or override?
this.events = $.extend(this.events, options.events);
},
addHandler : function() {
//defaulthandler this code can be overridden
});
});
// in another object create some views from the GridView base
....
var overrides = { events:"xxx yyy", el: ulElement addHandler: myAddFunction }
var UserList = GridView.extend(overrides)
var userList = new UserList(users, options);
....
var coursesOverrides : {addHandler: ...}
var coursesOptions: {el: courseElement, ...}
var CourseList = GridView.extend(coursesOverrides)
var courseList= new CourseList (courses, coursesOptions)
// along the same lines maybe there's an abstraction for toolbar views
var ClassToolbarView = ToolbarBase.extend(toolOverrides)
var classtoolbar = new ClassToolbarView(actions, toolbaropts)
Any pointers to good examples of extending a View for refactoring common view code is appreciated.
First, I don't see the options being passed in your initializer(), so that's a bug.
Secondly, the .extend() method is inherited:
var GridView = Backbone.View.extend({ ... })
var GridViewWithNewFunctionsAndEvents = GridView.extend({ ... })
And you can replace or extend GridView's functionality, and call new GridViewWithNewFunctionsAndEvents() and get the extra functionality in a new object you need, just like you extend the Backbone stock View class.
If you need to extend the initializer, you can do this to call the initializer on the superclass:
var GridViewWithNewFunctionsAndEvents = GridView.extend({
initializer: function(options) {
GridView.prototype.initializer.call(this, options);
/* Your stuff goes here */
}
});

Calling invoke on Backbone Collection

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)

Resources