Backbone: how to reuse a view rendered during page load - backbone.js

I am creating an url manager where user can bookmark his favorite webpage URLs and then manage them in folders.
During app load, I render a view which shows the list of folders user has created. this view is invisible on page load.
then user navigates to his home page to see a list of urls
from there, user can arrange the urls in folders
user clicks the 'Move to folder' icon in URL view
I want to display the folderSelectorView absolute positioned below the 'Move to Folder' icon. How can I do that?
Also if the url has already been moved to a folder, I want a checked sign to appear in the folderSelectorView in the folder where the url has been moved to.
How should I proceed here?
Here is how I load the list of folders.
var folderColl = new FolderColl();
new FolderSelectorView({collection: folderColl});
Here is the code for URL view.
URLView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#URLTempalte').html()),
events: {
'click .FolderChange': 'showFolderSelector'
},
initialize: function() {
_.bindAll(this, 'render');
},
render: function() {
return $(this.el).html(this.template(this.model.toJSON()));
},
showFolderSelector: function() {
// How should I display the view here
// view should display below the "Move to folder" icon
// Doing something like `new FolderSelectorView` is not what I'm after
// since that will just re-render the view for every URL displayed
}
});

You can attach a backbone view to an existing HTML element, easily. all you need to do is pass the el for the view, into the view when you instantiate it. the el can be anything you want - including an existing html element, found through a jQuery selector.
var myEl = $("#someElementOnThePage");
var myView = new MyView({
el: myEl
});
This will attach your view to the #someElementOnThePage element, and you can then use it as if you had rendered it yourself.
For more examples, see my blog post on progressive elaboration: http://lostechies.com/derickbailey/2011/09/26/seo-and-accessibility-with-html5-pushstate-part-2-progressive-enhancement-with-backbone-js/

Related

When I define a backbone view does the view element have to be loaded into the DOM?

I have some code which defines a backbone view being loaded as soon as a web page is loaded. The JavaScript is possibly executed before the DOM is fully loaded. If the dom element which becomes $el is not available when the code that defines the view is run is this a problem?
Programmatically I have something like this:
var view = Backbone.View.extend({
el: jQuery("#test")
events: {
}
render: function() {
this.$el.html();
}
//other view code
});
return view;
//some time passes
//with the view rendered above I now call :
view.render()
the problem is that when the render method is called this.$el is undefined. This is because when the first block of code was executed #test had not been loaded into the DOM. So; when the function Backbone.View.extend is called the 'el' element has to be loaded?
The context of this is a backbone application loaded via AMD. The first block of code is in a module. The module is 'required' before the DOM is loaded. Is this a common problem? How is it normally dealt with?
Thanks
el should not be a jQuery object but only a selector, $el will be the jQuery object for that selector. Also the id you specify as the el has to be found at initialization. So yes, the element has to be in the dom before you make it the el for the view.
If you're not creating / populating the view before making it a Backbone view you could do something like this to have backbone create the html tags etc. for you:
var view = Backbone.View.extend({
tagName: 'div',
id: 'test',
events: {
},
render: function() {
this.$el.html();
},
//other view code
});
You could then use templates to populate the view in your render function.
Short answer: Yes, the view element has to be loaded into the DOM when you initialize the view.

How to render a backbone collection into two views

I am using twitter bootstrap link. When the user clicks the link a bootstrap modal appears.
Now because of some bootstrap technical difficulties in modal rendering i need to seperate the link and put the modal out the navbar div.
So consider i have two separate div
<div id="linkDiv">
</div>
and
<div id="modalDiv">
</div>
Now i have only one View which makes a call to the server to get the collection
app.View.FriendRequestListView = Backbone.View.extend( {
templateModalLink: _.template($('#link').html()),
templateModal: _.template($('#modal').html()),
tagName: 'div',
initialize: function(){
this.friendRequestCollection = new app.Collection.FriendRequestCollection();
this.friendRequestCollection.bind("reset", this.render, this);
this.friendRequestCollection.fetch();
},
render: function() {
$(this.el).html(this.templateModalLink({
friendRequestCollection: this.friendRequestCollection}));
return $(this.el);
},
});
Than i can render only one div like following
var list = new app.View.FriendRequestListView();
$('#linkDiv').html(list.$el);
My question is , Is it possible to render two templates at the same time and add the two templates to different DIV like for example in my case i want to get update
templateModalLink template to linkDiv and templateModal template to modalDiv with the collection I am getting from the server.
You have to instantiate the collection before app.View.FriendRequestListView(s) and pass app.View.FriendRequestListView(s) the collection:
var friendRequests = new app.Collection.FriendRequestCollection();
friendRequests.fetch(
success: function(collection, response, options) {
var list1 = new app.View.FriendRequestListView({collection: collection});
var list2 = new app.View.FriendRequestListView({collection: collection});
$('#linkDiv').html(list1.$el);
$('#modalDiv').html(list2.$el);
}
);

Backbone JS view theory

I'm new to Backbone JS, and am having some trouble wrapping my head around a concept.
I have an interface with panels, where one panel is displayed at a time on the screen. Each panel is controlled by its own view, with its own model attached. Now, each panel as an "activator" tab that can be clicked to show the next panel.
In my mind, those tabs are actually sub-views of the parent panel view. Without a panel, the tab shouldn't exist. However, all tabs must appear on the screen at once, so that the user can switch between panels (views). So essentially all panels (their templates, anyway) would be loaded, but hidden until triggered by the click of a tab, at which time its content will be populated or else updated.
My problem, architecturally, comes with binding events to the tab views. So, for example:
window.PanelTabView=Backbone.View.extend({
className: 'view panel-tab-view',
el: '#appPanelTabs',
tagName: 'li',
events: {
'click a': 'test'
},
initialize: function() {
},
render: function(panel) {
this.$el.append(this.template(panel.toJSON()));
},
test: function(x) {
console.log(this.cid);
}
});
So when the tab is clicked, every click event is fired for all tab views.
Maybe I should treat all tabs as a single view, then? But I like the idea of each tab having it's own view from the point of modularity in the template.
Or maybe I'm missing something greater about Backbone and its MVC-esque approach.
What would you do in this scenario?
Why not have a parent view that simply manages the tabs and then simply delegates the click to the tab views? It would be a pretty simple approach.
Alternatively, you could be using a router, and in the router you could create your individual tab views based on a particular route taken. This would allow your page to be linkable.
First Approach:
ParentView = Backbone.View.extend({
el : '#your-tabs',
events : {
'click #tab1' : 'tab1',
'click #tab2' : 'tab2'
},
tab1 : function() {
var t1 = new Tab1();
t1.render();
},
tab2 : function() {
var t2 = new Tab2();
t2.render();
}
});
With a Router:
MyRouter = Backbone.Router.extend({
routes : {
"tab/1" : 'tab1',
"tab/2" : 'tab2'
},
tab1 : function() {
var t1 = new Tab1();
t1.render();
},
tab2 : function() {
var t2 = new Tab2();
t2.render();
}
});
Backbone isn't particularly opinionated about how views are constructed, but these seems to fit into their line of thinking.
Treating all tabs as a single view wouldn't make much sense. It would be silly to re-render all tabs just because the data on one tab changed.
I didn't understand much of your problem but at least I got that you in fact have only 1 button. Therefore you shouldn't have several views (it makes no sense anyway).
What you could do however, is use a "selected" attribute in your models which your views would listen to. So basically, when the user clicks on your button, you'll get the next panel thanks to your collection (I guess), unselect the former panel (set the selected attribute to false), therefore its view would disappear, and select the next, and its view would appear. I'll put some code later if you need.

Tabs in Backbone

I'm new with Backbone and I'm making an example app in which I have to include tabs. The thing is that I have a collection of cities and I want to create one tab for each city (the collection fetchs from the server). I made a view called TabsView, which in the render function passes the collection to a template, and this one loops through the collection and renders the tabs.
What I want to do is that the first tab appears as 'active'. What I've done for the moment is that each tab has a href to a route in the router which changes it's class to active using jquery. Don't know if this is the best way to do this but it works. Maybe there's a better way. Also, when the user clicks a tab, I want to be able to render other view.
Hope I made myself clear. Thanks, cheers,
Martin
Ok I solved this problem doing something like the following:
var Tabs = Backbone.View.extend({
template: JST['tabs'],
events: {
'click li' : 'switchTab'
},
tagName: 'ul',
className: 'nav-tabs',
render: function() {
this.renderTabs();
return this;
},
renderTabs: function() {
this.$el.html(this.template({ cities: this.cities }));
this.$('li:first').addClass('active');
},
switchTab: function(event) {
var selectedTab = event.currentTarget;
this.$('li.active').removeClass('active');
this.$(selectedTab).addClass('active');
}
});
It works fine, maybe it can be improved.

binding backbone events in jqMobi Nav, or when the dom elements are duplicated in the dom

I've been porting my app to use jqMobi and jqUI, but I've run into a problem with backbone delegating events.
The way jqUI creates a side nav bar is umm.... interesting to say the least.
Each panel can have a distinct nav bar, but the nav bar is never actually visible to the user, you populate the nav bar, and then jqUI copies the html into the div#menu element.
My view is fairly straightforward
MyApp.Views.UserMenu = Backbone.View.extend({
el: 'nav#user_menu',
initialize: function(){
//empty out and unbind in-case it is already populated
$(this.el).empty().unbind();
this.render();
},
events: {
"click div#add_friend": "new_friend"
},
render: function(){
$(this.el).append(HandlebarsTemplates['friends/new_friend']());
// here I am trying to change the 'el' to point to where the menu is in the DOM
this.el = 'div#menu';
this.delegateEvents();
return this;
},
new_friend: function(){
alert('clicked');
}
});
I've tried changing the el to the div#menu after populating the nav, but that isn't working. I've also tried populating the div#menu directly, but that doesn't seem to work either.
Any suggestions? I'm assuming the issue is that the elements are being moved, but it could be something else, and maybe I'm not sure how to debug the other case.

Resources