0
votes

I'm currently working on a large scale Backbone application (using marionetteJS) and having some architecture troubles with the best practices/patterns involved in switching views or changing the "page".

Marionette's layouts & regions provide some really great tools for doing the view switching, such as the .show() and the .empty() methods. In the application I am working on, I've used a layout view as the container of all other views, and this allows me to switch views in and out, essentially navigating within the app.

Thats easy. The hard part for me is understanding what the best practices are for triggering those changes.

Here are some of the solutions I've come up with:

  1. A page:change:x event is fired when a user clicks a certain part of the ui (x being the view to change to). The controller that manages that view listens for that event and shows it in the apps main layout view.

  2. When a user clicks the ui to change the page, the view triggers a command, and passes in the view it would like the ui to change to, like App.execute('page:change', view). The application object listens for that that trigger, and shows the view that is passed into the apps main layout view.

The problem with the first method is that the controller has to know about every page in its scope (maybe thats not a bad thing, I don't know). For example: users-list-view, users-edit-view, users-profile-view etc etc. It also makes the code somewhat harder to keep track of since there are events being triggered all over the place.

The issue with the second method is that the current view has to know about the view that the application will change too, which doesn't seem very scalable to me.

Is there any best practice for doing this kind of thing?

EDIT:

I should clarify that this is a problem associated with navigating around within an application. Not the initial load using the router.

1
Derick recommends an application level event aggregator, that said, this question is not a good fit for SO and would be better asked at programmers. Voting to close.steveax
when I refer to page:change:x or App.execute I am actually talking about the application level event aggregator implemented by default in marionettejs.Tom

1 Answers

1
votes

Faced very close issue few times, my solution is following

1) Main - the only script in html page, loads app + router + controller 1) Application - define root regions, starts history, 2) AppRouter - watch for url match and call controllers actions 3) AppController - keeps actions hash, hook up events subscribe (like App:switchpage) with own actions, manage models and views.

In code (i use requireJS for loading on demand):

main.js

define(
    [
        'application',
        'appRouter',
        'appController'
    ], 
    function(Application, Router, Controller) {
        var App = new Application;

        App.addInitializer(function() {
            this.controller = App.Controller;
            this.router = App.Router({
                controller: this.controller
            })
        })
    }
);

application

define(
    [
        'marionette',
        'backbone'
    ], 
    function(Marionette, Backbone) {
        return Marionette.Application.extend({
            regions: {
               'main': '#body'
            },

            onStart: function() {
                Backbone.history.start()   
            }
        })
    }
);

appRouter

define(
    [
        'application'
    ], 
    function(Application) {

        Application.module('AppRouter', function(Module, App, Backbone, Marionette) {
            App.Router = Marionette.AppRouter.extend({
                //for example : page/home page/goods page/user
                appRouter: {
                    'page/*': 'switchPage'
                }
            })
        }) 
    }
);

appController

define(
    [
        'marionette'
    ], 
    function(Marionette) {

       Application.module('AppRouter', function(Module, App, Backbone, Marionette) {
            App.Controller = Marionette.Controller.extend({
                switchPage: function(path) {
                //path (user || home || some)
                    require(['views/'+path], function(pageView) {
                        App.main.show(new pageView)
                    })
                }
            })
        })
    }
);

pages/home

define(
    [
        'marionette',
        'underscore'
    ], 
    function(Marionette, _) {
        return Marionette.ItemView.extend({
            template: _.template("home page")
        })
    }
);

Its just a mockup, i've removed some parts of code to keep only idea of application. To make it more scalable you may define pages as modules with its own controls and deps.

Nightly recommend you to checkout Brian Mann screencast at http://www.backbonerails.com/, especially Engineering Rich, Single Page Apps And checkout David Sulc GitHub with project example