Backbone.Marionette Change Region when Route chang

2019-07-13 11:09发布

My app has a main region and some times there will be sub regions in the main region that should be accessible by an URL. The main region content is changed by a function the app router cause he knows the main region. But whats about the temporary regions in sub views?

So for example the url /docs will show a list of links to documents and /doc/:id should show the content of the doc beside the list. So how can /doc/:id could render the content when some one click on the list and render both list and content when some opens the url in a new tab for example.

As far as I can see there are two options having a router for every region, or the region manager fires events with route and region that should change. Any hints for the best way to solve this problem.

1条回答
等我变得足够好
2楼-- · 2019-07-13 11:39

Ok I came up with a one router for every region solution. The router is easily configurable by a map of routes and views. When ever a route match the initially passed region will be shown a new instance of the view.

Here is an advanced version of the router where the route parameter will passed into the view.

Update

The solution above only works as long every route is registered only once. If you register the same route for the second time the callback for the first one will be overridden. So I came up with a solution where the region controller register a route not directly on the router but listen to an route:change event on a global eventbus (Marionette.Application.vent), and the router triggers an route:change event on this event bus.

RouterController:

// The problem with backbone router is that it can only register one function per route
// to overcome this problem every module can register routes on the RouterController
// the router will just trigger an event on the `app.vent` event bus when ever a registered routes match
define(function() {

  function RouterController(vent) {
    this.vent = vent;
    this.router = new Backbone.Router();
  }

  RouterController.prototype = _.extend({
      //just pass the route that change you wanna listen to
      addRoutes: function(routes) {
        _.each(routes, function(route) {
          this.router.route(
            route,
            _.uniqueId('e'),
            //create a closure of vent.trigger, so when ever the route match it simply trigger an event passing the route
//            _.partial(_.bind(this.vent.trigger, this.vent), 'route:change', route)
            _.bind(function() {
              this.vent.trigger.apply(this.vent, ['route:change', route].concat(_.toArray(arguments)));
            }, this)
          );
        }, this);

      }
    },
    Backbone.Events);

  return RouterController;
});

RegionRouter:

define(['common/App'], function(app) {

    function RegionRouter(region, routerSettings) {
      app.router.addRoutes(_.keys(routerSettings));

      this.listenTo(app.vent, 'route:change', function() {
        var route = arguments[0];
        var View = routerSettings[route];

        if (!View) {
          return;
        }

        var params;

        if (arguments.length > 1) {
          params = computeParams(arguments, route);
        }
        region.show(new View(params));
      });
    }

    RegionRouter.prototype = _.extend(
      {
        onClose: function() {
          this.stopListening(app.vent);
        }
      }, Backbone.Events
    );

    function computeParams(args, route) {
      args = Array.prototype.slice.call(args, 1);

      //map the route params to the name in route so /someroute/:somevalue will become {somevalue: passedArgs}
      //this object will be passed under the parameter key of the options
      var params = {};
      var regEx = /(?::)(\w+)/g;
      var match = regEx.exec(route);
      var count = 0;
      while (match) {
        params[match[1]] = args[count++];
        match = regEx.exec(route);
      }

      return {params: params};
    }

    return RegionRouter;
  }
);
查看更多
登录 后发表回答