admin管理员组

文章数量:1386514

I have the following situation:

app.js: Singleton Marionette.Application() where I define a nav, a footer, and a main region. In the initializer I construct Marionette.Contoller's and attach them to the app's this.controller object for later control. I might not construct all the Controller's here, just the ones I want to Eagerly Load. Some are Lazy Loaded later. I also instantiate a Backbone.Router here, and pass in a reference to my app object:

var theApp = new TP.Application();

theApp.addRegions(
{
    navRegion: "#navigation",
    mainRegion: "#main",
    footerRegoin: "#footer"
});

theApp.addInitializer(function()
{
    // Set up controllers container and eagerly load all the required Controllers.
    this.controllers = {};

    this.controllers.navigationController = new NavigationController({ app: this });
    this.controllers.loginController = new LoginController({ app: this });
    this.controllers.calendarController = new CalendarController({ app: this });

    this.router = new Router({ app: this });
});

**Controller.js: this is a general use controller that handles view & model intsantiation and eventing. Each Controller owns its own Marionette.Layout, to be filled into the App.mainRegion. Each Controller binds to the layout's "show" event to fill in the layout's regions with custom views. Each Controller offers a getLayout() interface that returns the controller's associated layout.

Marionette.Controller.extend(
{
    getLayout: function() { return this.layout; },
    initialize: function()
    {
        this.views.myView = new MyView();
        ...
        this.layout.on("show", this.show, this);
        ...
    },
    show: function()
    {
        this.layout.myViewRegion.show(myView);
    }
});

router.js: the router uses the app singleton to load a Controller's layout into the App's main region:

...
routes:
{
    "home": "home",
    "login": "login",
    "calendar": "calendar",
    "": "calendar"  
},
home: function ()
{
    var lazyloadedController = new LazyLoadController();
    this.theApp.mainRegion.show(lazyLoadController.getLayout());
},
login: function (origin)
{
    this.theApp.mainRegion.show(this.theApp.controllers.loginController.layout);
}

As it is, everything works fine except for reloading the same layout / controller twice. What happens is that the DOM events defined in the LoginView do not re-bind on second show. Which is easily solved by moving the LoginView initialization code into the "show" event handler for that Controller:

LoginController = Marionette.Controller.extend(
{
    ...
    show: function()
    {
        if (this.views.loginView)
            delete this.views.loginView.close();

        this.views.loginView = new LoginView({ model: this.theApp.session });
        this.views.loginView.on("login:success", function()
        {
        });

        this.layout.mainRegion.show(this.views.loginView);
    }

Now everything works fine, but it undoes part of the reason I created Controller's to begin with: I want them to own a View and its Models, create them once, and not have to destroy & recreate them every time I switch layouts.

Am I missing something? Is this not how I should be using Layouts? Isn't the whole point of Layouts and Regions that I can switch in & out Views at will?

Obviously I wouldn't jump back to LoginController/Layout often, but what about between a HomeController/Layout, CalendarController/Layout, SummaryController/Layout, etc... in a single page application I might switch between those 'top-level' layouts rather often and I would want the view to stay cached in the background.

I have the following situation:

app.js: Singleton Marionette.Application() where I define a nav, a footer, and a main region. In the initializer I construct Marionette.Contoller's and attach them to the app's this.controller object for later control. I might not construct all the Controller's here, just the ones I want to Eagerly Load. Some are Lazy Loaded later. I also instantiate a Backbone.Router here, and pass in a reference to my app object:

var theApp = new TP.Application();

theApp.addRegions(
{
    navRegion: "#navigation",
    mainRegion: "#main",
    footerRegoin: "#footer"
});

theApp.addInitializer(function()
{
    // Set up controllers container and eagerly load all the required Controllers.
    this.controllers = {};

    this.controllers.navigationController = new NavigationController({ app: this });
    this.controllers.loginController = new LoginController({ app: this });
    this.controllers.calendarController = new CalendarController({ app: this });

    this.router = new Router({ app: this });
});

**Controller.js: this is a general use controller that handles view & model intsantiation and eventing. Each Controller owns its own Marionette.Layout, to be filled into the App.mainRegion. Each Controller binds to the layout's "show" event to fill in the layout's regions with custom views. Each Controller offers a getLayout() interface that returns the controller's associated layout.

Marionette.Controller.extend(
{
    getLayout: function() { return this.layout; },
    initialize: function()
    {
        this.views.myView = new MyView();
        ...
        this.layout.on("show", this.show, this);
        ...
    },
    show: function()
    {
        this.layout.myViewRegion.show(myView);
    }
});

router.js: the router uses the app singleton to load a Controller's layout into the App's main region:

...
routes:
{
    "home": "home",
    "login": "login",
    "calendar": "calendar",
    "": "calendar"  
},
home: function ()
{
    var lazyloadedController = new LazyLoadController();
    this.theApp.mainRegion.show(lazyLoadController.getLayout());
},
login: function (origin)
{
    this.theApp.mainRegion.show(this.theApp.controllers.loginController.layout);
}

As it is, everything works fine except for reloading the same layout / controller twice. What happens is that the DOM events defined in the LoginView do not re-bind on second show. Which is easily solved by moving the LoginView initialization code into the "show" event handler for that Controller:

LoginController = Marionette.Controller.extend(
{
    ...
    show: function()
    {
        if (this.views.loginView)
            delete this.views.loginView.close();

        this.views.loginView = new LoginView({ model: this.theApp.session });
        this.views.loginView.on("login:success", function()
        {
        });

        this.layout.mainRegion.show(this.views.loginView);
    }

Now everything works fine, but it undoes part of the reason I created Controller's to begin with: I want them to own a View and its Models, create them once, and not have to destroy & recreate them every time I switch layouts.

Am I missing something? Is this not how I should be using Layouts? Isn't the whole point of Layouts and Regions that I can switch in & out Views at will?

Obviously I wouldn't jump back to LoginController/Layout often, but what about between a HomeController/Layout, CalendarController/Layout, SummaryController/Layout, etc... in a single page application I might switch between those 'top-level' layouts rather often and I would want the view to stay cached in the background.

Share Improve this question asked Feb 4, 2013 at 2:17 BernardoBernardo 1,0301 gold badge12 silver badges22 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 7

I think your problem is that you don't maintain a single instance of the controller. The remended way to handle routing/controllers (based on Brian Mann's videos) is like this

App.module('Routes', function (Routes, App, Backbone, Marionette, $, _) {

    // straight out of the book...
    var Router = Marionette.AppRouter.extend({
        appRoutes: {
            "home": "home",
            "login": "login",
            "calendar": "calendar"
        }
    });

    var API = {
        home: function () {
            App.Controller.go_home();
        },
        login: function () {
            App.Controller.go_login();
        },
        calendar: function () {
            App.Controller.go_calendar();
        }
    };

    App.addInitializer(function (options) {
        var router = new Router({controller: API});
    });
});

... and the controller:

App.module("Controller", function (Controller, App, Backbone, Marionette, $, _) {
    App.Controller = {
        go_home: function () {
            var layout = new App.Views.Main();
            layout.on('show', function () {
                // attach views to subregions here...
                var news = new App.Views.News();
                layout.newsRegion.show(news);
            });

            App.mainRegion.show(layout);
        },

        go_login: function () {
            ....
        },

        go_calendar: function () {
            ....
        }
    };
});

I suspect your problem is the lazy-loaded controller...

本文标签: javascriptMarionette Layout switching strategyStack Overflow