Here is an example code from the github of backbone layout manager.
The custom render for the view is not getting called/it doesn't stop on the break point.what is happening.
// Create a Content view to be used with the Layout below.
var ContentView = Backbone.Layout.extend({
template: "#content"
});
// Create a new Layout with a sub view for content.
var layout = new Backbone.Layout({
template: "#layout",
// This will place the contents of the Content View into the main
// Layout's <p></p>.
views: {
// Appending a new content view using the array syntax
p: new ContentView({
// Custom render function that reverses everything.
render: function(template, context) {
return template(context).split("").reverse().join("");
}
})
}
});
// Attach the Layout to the main container.
layout.$el.appendTo(".main");
// Render the Layout.
layout.render();
This was answered by the Github when I posted the issue. it should be layout.renderTemplate()
Related
I'm developing a single-page web application using Backbone and Laravel. I've set my router to use pushState and configured Laravel to send all other requests to the main view of the backbone application, where backbone takes care of the routing.
My problem/question is as follows:
I have a route called 'dashboard', this route is the main application view and is shown after login. It uses a collection called Clients.
dashboard:function(uri){
dashboardCallback = function(data){
if(data.check){
console.log('generate dashboard');
//get clients collection
clientsCollection = new Dash.Collections.Clients();
clientsCollection.fetch().then(function(clients){
//genenerate dashboard view
new Dash.Views.Dashboard({collection:clientsCollection}).renderDashboard();
});
}
else{
router.navigate('/', {trigger:true, replace:true});
}
}
Dash.Utilities.user.isLoggedIn(dashboardCallback);
},
The Dash.Views.Dashboard view takes care of all the views in the application, when calling the renderDashboard(); method, it starts rendering all client views. This is where it gets interesting.
The code for rendering all the client views is as follows:
renderClients:function(){
console.log('Rendering all clients', this.collection);
clientsView = new Dash.Views.Clients({collection:this.collection}).render();
$(this.el).html(clientsView.el);
}
with the above code, it works in all cases. With that i mean when I log in first and the application routes me to the dashboard view all the clients gets rendered and appended to the DOM, the same thing happens when I access /dashboard immediately (afther the application checks if i'm logged in).
But, when I use the following code it doesn't load the client views when I first log in. It does load the client views when i access /dashboard directly.
renderClients:function(){
console.log('Rendering all clients', this.collection);
clientsView = new Dash.Views.Clients({collection:this.collection}).render();
this.$el.html(clientsView.el);
}
It took me a while to figure out that the fix of the problem was that I had to replace this.$el with $(this.el), but I alway's thought it didn't matter because they are essentially the same, or am I wrong in this assumption?
Can someone explain to me this weird behaviour?
As requested, here is my global Dashboard view
Dash.Views.Dashboard = Backbone.View.extend({
tagName:'div',
id:'main',
className:'dashboard',
initialize: function(){
console.log('Initializing Global Dashboard View');
//make sure the main element is only added once.
if(!$('.dashboard').length){
$('body').append(this.el);
}
else{
this.el = $('.dashboard');
}
},
renderDashboard: function(){
console.log('Render all Dashboard components');
this.renderNavBar();
this.renderClients();
},
renderNavBar: function(){
var navBarView = new Dash.Views.NavBar().render();
$(this.el).before(navBarView.el);
},
renderLogin: function(){
var logInView = new Dash.Views.Login().render();
$(this.el).html(logInView.el);
},
renderWhoops:function(error){
console.log('Render Whoops from Global Dashboard');
var whoopsModel = new Dash.Models.Whoops(error);
$(this.el).html(new Dash.Views.Whoops({model:whoopsModel}).render().el)
},
renderClients:function(){
console.log('Rendering all clients', this.collection);
clientsView = new Dash.Views.Clients({collection:this.collection}).render();
$(this.el).html(clientsView.el);
}
});
I'd guess that your problem is right here:
if(!$('.dashboard').length){
$('body').append(this.el);
}
else{
this.el = $('.dashboard'); // <----- Broken
}
If there is no .dashboard then you directly assign to this.el and that's a mistake as it won't update this.$el. The result is that this.el and this.$el reference different things and nothing works. You should use setElement to change a view's el:
setElement view.setElement(element)
If you'd like to apply a Backbone view to a different DOM element, use setElement, which will also create the cached $el reference and move the view's delegated events from the old element to the new one.
So you should be saying this:
if(!$('.dashboard').length){
$('body').append(this.el);
}
else{
this.setElement($('.dashboard')); // <----- Use setElement
}
Apologies for the possibly poorly formulated title. New to Backbone.
I'm having trouble wrapping my head around how to deal with routes in association with views. Basically I have a view (let's call it ListView) that, depending on its viewMode, renders ItemViews using different templates. It looks something like this:
var ListView = Backbone.View.extend({
// Cache a bunch of templates here
viewMode: 'list', // Default is list
render: function() {
switch(this.viewMode) {
case 'list':
// Render ItemView based on list template
break;
case 'gallery':
// Render ItemView based on gallery template
break;
}
// Render all items in list
this.collection.each(function(model, index) {
new ItemView(); // Maybe pass viewMode as a parameter
});
}
});
My goal is that whenever ListView uses the viewMode "list" or "gallery", this should be reflected in the address bar, and likewise manually entering or clicking a link that leads to e.g. mysite.com/page.html#items/list or #items/gallery should render the same results.
Is there a way of automating this process, or in some other way solve it?
Think your router would be something like:
var yourRouter = Backbone.Router.extend({
routes: {
"items/list": "showList",
"items/gallery": "showGallery"
},
showList: function() {
listView.viewMode = "list"
listView.render();
}
showGallery: function() {
listView.viewMode = "gallery"
listView.render();
}
});
Then in your view events, you can call the navigate method of your router. This will update the address bar.
yourRouter.navigate("items/list")
I have two different backbone js file for 2 different view. I need to call the render method of the second js file from the first one. How can i do that
I have one backbone.js file which as a view called DocumentsPageView. In my second backbone js file when i click button on the first js file i have to call the render method of DocumentsPageview
first js file
first.backbonejs = (function($) {
case myapp
sectionView = new second.mysecondbackbone.DocumentsPageView();
sectionView.render();
break;
}
}(jQuery)
second js file
second.mysecondbackbone = (function($) {
var DocumentsPageView= Backbone.View.extend({
render: function(){
//render the page
}
});
}(jQuery)
I am getting object undefined in the declaration section
Thanks & Regards
Ashik
My advice is, don't.
Use a mediator object that sits between the two, and controls the process of working with both views.
It can be as simple as this:
myProcess = {
show: function(){
var view1 = new View1();
view1.on("foo", this.doMoreStuff, this);
this.showView(view1);
},
doMoreStuff: function(){
var view2 = new View2();
this.showView(view2);
},
showView: function(view){
// code to stuff view.$el in to the DOM
}
}
The advantage here is that you have a high level workflow that can be managed and maintained on it's own, separate from the implementation details of the individual views. You don't have to trace down in to the individual views to see how they work together.
I wrote more about this, here: http://lostechies.com/derickbailey/2012/05/10/modeling-explicit-workflow-with-code-in-javascript-and-backbone-apps/
I'm trying to get my Marionette views working in combination with application regions and layouts, but I just can't seem to get the nested views in the layout to render.
Edit: I expected both the OptionsView and BreadcrumbsView to be rendered in the NavigationLayout, which should be rendered in the navigation region. However, the navigation region isn't rendered at all. The console doesn't show any errors.
My structure is as follows:
- Navigation region
- Navigation layout
- Options region
- Breadcrumbs region
- Content region
Assigning an ItemView to the navigation region works as expected.
App = new Backbone.Marionette.Application();
App.addRegions({
'nav': '#nav',
'content': '#content'
});
var NavigationLayout = Backbone.Marionette.Layout.extend({
template: '#nav-template',
regions: {
'breadcrumbs': '#breadcrumbs',
'options': '#options'
}
});
var BreadcrumbsView = Backbone.Marionette.ItemView.extend({
template: '#breadcrumbs-template'
});
var OptionsView = Backbone.Marionette.ItemView.extend({
template: '#options-template'
});
var ContentView = Backbone.Marionette.ItemView.extend({
template: '#content-template'
});
App.addInitializer(function(options) {
var navigationLayout = new NavigationLayout();
App.nav.show(navigationLayout);
App.content.show(new ContentView());
navigationLayout.breadcrumbs.show(new BreadcrumbsView());
navigationLayout.options.show(new OptionsView());
});
$(function() {
App.start();
});
A reduced test case can be found here
Ok, finally found the problem: You can't name a region options in a layout.
All of the regions that are defined in a Layout are directly attached to the layout instance. So, a region defined like this:
Layout.extend({
regions: {
options: "#options"
}
});
ends up setting the layoutInstance.options to the Region instance. This is a problem because Backbone.View defines and uses the options for other purposes.
Renaming the region to anything other than an existing keyword or attribute used by any existing view will fix this.
Layout.extend({
regions: {
optionRegion: "#options"
}
});
Working JSFiddle here: http://jsfiddle.net/tegon/64ovLf64/
I'm quite new in the world of Backbone and I decided to use Marionette for my first serious project with it.
With some difficulties I managed to set up my app's basic options and routing and I was pretty happy with it, but now I'm facing a blocking problem with a CompositeView that represent a Table.
This View is rendered inside a region of a specific layout, called "grid". This layout has 3 region: the top_controls, table_view and bottom_controls. Since I needed to bind some action on some of the elements of the layout I decided to use it as a View, and to include the "master" collection inside it, so I can just rendered a filtered version of the collection inside the CompositeView, without touching the main one.
From my router I call it in this way:
App.grid = new Grid({collection: Clt});
App.page.show(App.grid);
The structure of the layout is this (I'm using requireJS):
var Grid = Backbone.Marionette.Layout.extend({
className: "container-fluid",
template: gridLayout,
regions: {
top_controls: "#top_controls",
table_view: "#table_view",
bottom_controls: "#bottom_controls",
},
initialize: function(){
this.renderTable(this.collection, true);
},
renderTable: function(collection, fetch){
if(fetch){
collection.fetch({success:function(){
var vista = new CompView({collection: collection});
App.grid.table_view.show(vista);
}});
} else {
var vista = new CompView({collection: collection});
App.grid.table_view.show(vista);
}
},
events: {
"keyup input":"filter_grid"
},
filter_grid: function(e){
var $el = e.currentTarget;
var to_filter = $($el).val();
if(to_filter==""){
this.renderTable(this.collection, false);
} else {
var filtered = this.collection.filter(function(item){
return item.get("link_scheda").toLowerCase() == to_filter;
});
if(filtered.length>0){
var filtro = new AssocCollection();
filtro.reset(filtered);
this.renderTable(filtro, false);
}
}
}
});
return Grid;
The Layout template looks like this:
<div class="row-fluid" id="top_controls"><input type="text" id="filter" class="input"/></div>
<div class="row-fluid" id="table_view"></div>
<div class="row-fluid" id="bottom_controls"><button class='add btn btn-primary'>Add</button></div>
My CompositeView is structured like that:
var AssocView = Backbone.Marionette.CompositeView.extend({
tagName: 'table',
className: 'table table-bordered table-striped',
id: 'tableAssoc',
template: assocTemplate,
itemView: assocRow,
appendHtml: function(collectionView, itemView, index){
collectionView.$("tbody").append(itemView.el);
},
events: {
"click .sort_link":"sort_for_link",
},
sort_for_link: function(){
this.collection.comparator = function(model){
return model.get("link_value");
}
this.collection.sort();
},
onRender: function(){
console.log("render table!");
}
});
return AssocView;
The first display of the table is done right, and the filtering too. The problem occur when
I click the table header with the class "sort_link": the entire Table is wiped away from the HTML while the collection stay the same (I suppode the entire layout is re-rendered). If for example I render the CompositeView in another place, like the app main region, it all works as intended. So I guess to problem it's located inside my Layout declaration.
Any help will be much appreciated!
In your Grid, you need to override the initialEvents method and don't do anything in it.
Grid = Backbone.Marionette.Layout.extend({
initialEvents: function(){},
// ... everything you already have
});
Layout extends from ItemView, and ItemView provides the initialEvents implementation. This method checks to see if it was given a collection, and if it does, it wires up the collection "reset" event to the "render" method of the view. In your case, you are passing the collection through and don't want this behavior. So, overriding the initialEvents method will correct it.
Update: I thought I had removed that initialEvents a long time ago. If you're keeping up to date w/ Marionette versions, grab v0.9.10 (or whatever the latest is) and this problem is gone now.