Question

If you were to build a single page web application (SPWA) using Backbone.js and jQuery with--for example--two controllers that each required a unique page layouts, how would you render the layout?

  • ControllerA is a three column layout.
  • ControllerB is a two column layout.
  • The default route activates ControllerA.Welcome() -- the initial rendering.
  • Both controllers have different views rendered within their columns that take advantage of all the Backbone.js model/view goodness.

The Problem

When the user requests a route mapped to ControllerB, the entire page layout needs to change to no longer use the ControllerA layout. This would hide ControllerA's layout and show ControllerB's layout -- or, render the layout if not already in the DOM.

My First Thought

Would you use a Backbone.js view to render the layout, and then, render each column with it's model-bound views?

My Second Thought

Would you add a setup/layout method to your controller that used jQuery to render the layout and then allow the action responsible for the route do it's thing? Using jQuery within the controller feels a little off to me, but, I want the controller to be responsible for ensuring the right layout is visible for it's routes.

Here is a snippet for my second thought:

var Controller = Backbone.Controller.extend
({
    routes :
    {
       "" : "welcome" // default action
    }
    /** Constructor **/
    ,initialize: function(options)
    {
        console.log('Workspace initialized');               
    }
    // LAYOUT
    ,renderLayout : function ()
    {
        console.log('Rendering Layout.');
        var $ = window.$;
        var layout = require('js/layout/app/big_menu');
        $(layout.parent).html(layout.html);
    }
    // ACTIONS
    /** Default Action **/
    ,welcome : function ()
    {
        this.renderLayout();
        console.log('Do the whole model/view thing...');
    }
});

Thank You

Thanks for taking the time to respond. I appreciate it!

Was it helpful?

Solution

I prefer to have the skeleton of the application laid out on the page already. So you have the complete layout with the different elements on the page and you create your backbone view against those elements so they are correctly laid out.

This works well when you have a single layout, things get fun when you have multiple. You could put all layouts on the page and hide the different configurations depending on your logic. You can see the layout has being the initial view of an hierarchy. So you render the layout and then have the views load.

There is no real one way of doing this. There are pros and cons for each. One thing I would not do is render the layout in the controller. I put all rendering and html in views so I can deal with logic on the controller and model (think MVC here).

OTHER TIPS

I tend to agree with Julien -- it's nice to keep your layouts as stateless as possible. Everything is always laid out on the page, in skeleton form, at least. When the particular layout or configuration needs to be displayed, you lazily-render its contents, and display that portion of the UI with CSS. Mutually-exclusive CSS classes are useful for this, things like: "projects-open", "documents-open", "notes-open".

I'm designing a module-based intranet system using backbone.js and I basically use the following algorithm on document load.

  • Create appController, the singleton controller for the app.
  • The appController creates the mainView, this is the view responsible for rendering the skeleton of the page and handling clicks for persistent items on the page (login/logout buttons, etc)
  • The mainView creates a number of childViews for the different parts of the page, navigation, breadcrumbs, header, toolbar, contentContainer, etc. These are the fixtures of the application and they don't change, although their respective content does. The contentArea in particular can contain any layout.
  • The appController runs through the registered modules, initiating the mainModuleController for each of them. These all have namespaces routing schemas.
  • Backbone.history.start()

The moduleControllers all gain access to the appController on init. When catching a hash-location, they send a pageChange event to the appController containing a pageManifest object. The pageManifest object contains all the information needed to set the respective views, such as breadcrumbs info, header info, and most importantly, a reference to an instantiated contentView. The appController uses the information in the pageManifest to setup the different persistent views, deletes the former contentView in the contentContainer and inserts the contentView provided by the module into the container.

This way, different designers can work on different modules and all they have to know is the specification of the pageManifest object and how the contentView should look. They can set up complex routing systems of their own, use their own models and customized contentViews (though we plan to have a library of listViews, objectViews, etc to inherit from).

We're at the design phase right now, so I can't really guarantee that this is the design we'll finally use or that we don't find any holes in it, but conceptually, we think it's sound. Comments?

I am having the exact same issue regardless of Backbone or any other js framework/library.

Imagine you have a SIGN IN FORM view which requires a single column layout and you inject the view into that one single div.

Then once signed in successfully, somehow another layout is rendered (lets say a HEADER zone, FOOTER zone, LEFT zone and then the MAIN zone (right column) for everything else.

The header might contain a LOGO view (if it has functionality) and a GLOBAL/USER MENU view. The LEFT zone will contain the PRIMARY NAV view.

Then a further complexity.; Each link inside the PRIMARY NAV view loads up a new sub layout ready for further views to inject themselves into.

I don't want the regular controllers/views to care about what layout is currently rendered, just that their container element exists and is ready to be injected into.

I thought about using routes (not in the traditional sense) in a clever way something like:

function LayoutController() {
App.addRouteMatcher("/sign_in/*", this.renderSignInLayout); // single column
App.addRouteMatcher("regex to represent anything but sign_in", this.renderMainLayout); // header, footer, primary nav, main zone
App.addRouteMatcher("/section1/*", this.renderSubLayoutForSection1); // puts a 1 column layout in the main zone
App.addRouteMatcher("/section2/*", this.renderSubLayoutForSection2); // puts a 2 column layout in the main zone 
}

Meaning that if the route was "/section1/whatever/sub/page/within/section/1" the two route matchers above "regex to represent anything but sign_in" and "/section1/*" would both run, meaning that the primary layout would be rendered and then the section1 sub layout would be rendered after if that makes sense.

Then all other normal controllers use routes in the traditional sense.

There needs to be a nice way to manage layouts and ensure those layouts, sub layouts and views are torn down safely to ensure memory leaks are handled amongst other reasons.

Would love to hear someone that has designed and implemented something elegant.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top