木偶:根据路由正则表达式启动和停止模块
Marionette: Starting and stopping modules based on route regexp
我正在实现一个应用程序,该应用程序在顶级应用程序模块中有两个单独的子模块。我有一个管理模块,其中包含以/admin
开头的路由约定和具有以/user
开头的路由的用户模块。顶级应用程序定义了一个rootRoute
以便当您导航到http://url/
时,系统会根据权限将您重定向到管理员或用户页面。我试图了解的是是否可以根据路由启动和停止特定模块。以下是我的意思的一个例子:
假设我有一个顶级应用程序(在 coffeescript 中)
@ClientApp = do (Backbone, Marionette) ->
App = new Marionette.Application
navigate: (route, options = {}) ->
Backbone.history.navigate route, options
App.on "start", (options) ->
if Backbone.history
Backbone.history.start()
App.on "stop", ->
App.module("AdminApp").stop()
App.module("UserApp").stop()
class App.Router extends Marionette.AppRouter
initialize: (options) ->
@route /^admin(.*)/, 'startAdminApp', options.controller.startAdminApp
@route /^user(.*)/, 'startUserApp', options.controller.startUserApp
appRoutes:
"": "redirectToRoot"
App.addInitializer ->
new App.Router
controller: API
API =
redirectToRoot: ->
# some redirect logic that will lead you to either /admin or /user
startAdminApp: ->
App.mainRegion.show new App.Layouts.Admin
App.module("AdminApp").start()
startUserApp: ->
App.mainRegion.show new App.Layouts.User
App.module("UserApp").start()
App
在管理员和用户子模块中,我也定义了路由
@ClientApp.module "AdminApp.DashboardApp", (DashboardApp, App, Backbone, Marionette, $, _) ->
_.extend DashboardApp, Backbone.Wreqr.radio.channel("dashboard")
class DashboardApp.Router extends Marionette.AppRouter
appRoutes:
"admin/dashboard": "statistics"
API =
getLayout: ->
new DashboardApp.Layout.View
statistics: ->
DashboardApp.StatisticsAp.start()
DashboardApp.on "start", ->
@layout = API.getLayout().render()
API.statistics()
App.addInitializer ->
new DashboardApp.Router
controller: API
如果我导航到应用程序/
按预期工作,我将被重定向到必要的命名空间并启动特定的子模块。但是,如果我在子模块中定义其他一些路由,它们似乎覆盖了现有的正则表达式匹配器。因此,如果我打开浏览器并导航到/admin/statistics
它将不会启动管理应用程序,并且/admin/statistics
的回调将失败并出错。这是因为管理应用程序不会启动,并且主区域没有填充相应的布局。请注意,在任何子模块之前都需要包含顶级应用程序定义的文件(我想这就是路由被覆盖的原因)。我也明白,当遇到第一个匹配项时,骨干路由器会调用路由回调。
所以问题是是否有可能实现一种路由管理器,它将使用正则表达式检查当前路由并启动或停止相应的应用程序(管理员或用户),所有定义的子路由都是持久和可书签的?
有接近的任务要解决,没有找到任何现有的解决方案,所以这里有一个小存根 - 我创建的项目
要解决此类任务,需要解决的问题很少:
1) 异步路由。类似于rootRouter
应该加载应用程序模块,moduleRouter
应该调用控制器方法
2) 在模块停止时清除主干历史记录处理程序。问题是即使在模块停止后,路由和处理程序仍然存在于BB历史记录中
所以我的黑客,我的意思是解决方案:)
我们需要一些路由器来监视URL更改并加载模块,让它ModuleManager
define(
[
'application'
],
function(App) {
App.module('ModuleManager', function(ModuleManager, Application, Backbone, Marionette) {
var currentPageModule = false,
stopModule = function(name) {
name && Application.module(name).stop();
},
startModule = function(name) {
Application.module(name).start();
};
ModuleManager.getModuleNameByUrl = function() {
var name = Backbone.history.getHash().split('/')[0];
return name ? (name.charAt(0).toUpperCase() + name.slice(1)) : 'Home'
};
ModuleManager.switchModule = function(name) {
if (!name) return;
stopModule(currentPageModule);
startModule(name);
currentPageModule = name;
};
ModuleManager.requireModule = function(name, callback) {
require(['apps/pages/' + name + '/index'],
callback.bind(this),
function() {
require(['apps/pages/404/index'], function() {
ModuleManager.switchModule('404');
})
}
);
};
/*
* this is key feature - we should catch all routes
* and load module by url path
*/
ModuleManager.FrontRouter = Marionette.AppRouter.extend({
routes: {
'*any': 'loadModule'
},
loadModule: function() {
var name = ModuleManager.getModuleNameByUrl();
ModuleManager.requireModule(name, function() {
ModuleManager.switchModule(name);
})
}
});
ModuleManager.addInitializer(function() {
new ModuleManager.FrontRouter;
});
ModuleManager.addFinalizer(function() {
delete ModuleManager.FrontRouter;
});
});
}
);
太好了,这将加载带有内部路由的模块。但是我们会得到另一个问题 - 在子模块启动时,我们初始化它的路由器,但我们已经路由到子路由器 init 上的页面,URL 仍然相同。因此,在下次导航之前不会调用子路由器。因此,我们需要特殊的路由器来处理这种情况。这是"模块路由器":
App.ModuleRouter = Marionette.AppRouter.extend({
forceInvokeRouteHandler: function(routeRegexp, routeStr, callback) {
if(routeRegexp.test(Backbone.history.getHash()) ) {
this.execute(callback, this._extractParameters(routeRegexp, routeStr));
}
},
route: function(route, name, callback) {
var routeString = route,
router = this;
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
// проблема - RouterManager уже стригерил событие route, загрузил саб-роутер.
// при создании саб роутера его колбэк уже вызван не будет, поскольку адрес страницы не изменился
// при добавлении роутов используется нативный ВВ route - который вещает колбэк на указанный фрагмент
// расширяем - если мы уже находимся на фрагменте на который устанавливается колбэк - принудительно вызвать
// выполнение обработчика совпадения фрагмента
/*
* PROBLEM : AppsManager already triggered 'route' and page fragments still same,
* so module router will not be checked on URL matching.
*
* SOLUTION : updated route method, add route to Backbone.history as usual, but also check if current page
* fragment match any appRoute and call controller callback
* */
this.forceInvokeRouteHandler(route, routeString, callback);
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
router.execute(callback, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
});
return this;
},
// implementation destroy method removing own handlers anr routes from backbone history
destroy: function() {
var args = Array.prototype.slice.call(arguments),
routKeys = _.keys(this.appRoutes).map(function(route) {
return this._routeToRegExp(route).toString();
}.bind(this));
Backbone.history.handlers = Backbone.history.handlers.reduce(function(memo, handler) {
_.indexOf(routKeys, handler.route.toString()) < 0 && memo.push(handler)
return memo;
}, []);
Marionette.triggerMethod.apply(this, ['before:destroy'].concat(args));
Marionette.triggerMethod.apply(this, ['destroy'].concat(args));
this.stopListening();
this.off();
return this;
}
})
请填写自由提问或聊天,我想有些地方可能需要澄清。
- Javascript,访问一个主要对象模块模式中的每个对象
- 我的jQuery插件参数没有正确启动,遇到了问题
- chrome扩展:尽管运行了at:documentidle,js脚本还是过早启动
- 节点Js:How to catch a“;没有这样的文件或目录“;读取线模块出错
- 如何从模块链中调用函数.导出到节点中
- 尽管链接成功并已成功下载,但未找到NPM模块
- 从控制器返回后Ajax启动事件激发
- 节点是否需要模块传递带有方括号的arg?这是个错误吗
- Meteor上的启动页面
- 如何防止网页加载后自动启动功能
- 从模块内部访问Express装载路径
- RequireJS向模块传递配置总是返回undefined
- Node.js正在更改应用程序以使用集群模块
- 在Meteor项目中安装并包含npm模块后出错
- 如何从JavaScriptInterface启动Navigation Drawer
- 模块模式和这个
- 检查AngularJS模块是否已启动
- 木偶:根据路由正则表达式启动和停止模块
- 找不到'.组件'和'.服务'“模块异常”;npm启动”;
- npm启动错误"模块未找到;错误:无法解析目录"