【问题标题】:How to use durandal router to activate dialogs?如何使用 durandal 路由器激活对话框?
【发布时间】:2014-10-11 19:56:34
【问题描述】:

我想要一个 #signin 路由,它会在之前的任何页面顶部打开一个对话框。

让我们将这个示例应用程序视为以下路线:

router.map([
    {route: '', moduleId: 'vm/home', title: "Home"},
    {route: 'about', moduleId: 'vm/about', title: "About"},
    {route: 'signin', moduleId: 'vm/signin', title: 'Sign In'}
]);

以下是示例用例:

  1. 用户在# 并导航到#signin:我们应该会在主页页面顶部看到一个登录对话框

  2. 用户在#about 并导航到#signin:我们应该会在关于页面顶部看到一个登录对话框

  3. 用户导航到http://localhost:9000/#signin:我们应该会在主页页面顶部看到一个登录对话框

  4. 用户在#signin 并关闭对话框:我们应该看到对话框后面的页面(后面总是有一个页面)。

【问题讨论】:

  • @Tambo,它不是重复的,op 正在寻找一个没有路由的对话框,而我正在寻找一个解决方案,其中对话框类似于page,它有自己的路由。

标签: durandal durandal-2.0 durandal-navigation


【解决方案1】:

对话框和路由器都是插件,彼此之间没有交互。

还有路由器显示对话框会忽略路由器的工作方式 - 它有一个 div 可以将内容转储到其中。对话框存在于所有这些之外。

但是,如果您愿意(我也可以这样做),您可以试试这个。

dialog: true 添加到路线图。

覆盖router.loadUrl 方法。检查路由是否是我们之前标记的对话框路由,并激活对话框。

我会将对话框设为子路由,这样您就可以知道在对话框下方显示哪个视图。否则,您可能只需要在任何内容上显示对话框并完全忽略路由。

编辑:我认为这实际上不会完全有效。 loadUrl 返回一个布尔值。您可以打开对话框并返回 false 以取消导航。

编辑2:

我的尝试

loadUrl 方法循环遍历所有路由,并且每个路由都有一个回调,所以理想情况下我们需要将我们的逻辑插入到这个数组中。

for (var i = 0; i < handlers.length; i++) {
    var current = handlers[i];
    if (current.routePattern.test(coreFragment)) {
        current.callback(coreFragment, queryString);
        return true;
    }
}

这个数组是使用路由器route 方法添加的。当你映射路线时,Durandal 会调用这个方法,所以理想情况下,我们可以在路线配置中添加一些额外的参数,让 Durandal 处理这些。但是configureRoute 函数是路由模块的内部函数,因此我们需要对其进行编辑,并确保在将来更新 Durandal 时复制更改。

我创建了一个新的对话路由列表:

{ route: 'taxcode/add(/:params)', moduleId: 'admin/taxcode/add', title: 'Add Tax Code', hash: '#taxcode/add', nav: false, dialog: true, owner: '#taxcodes' },
{ route: 'taxcode/edit/:id', moduleId: 'admin/taxcode/edit', title: 'Edit Tax Code', hash: '#taxcode/edit', nav: false, dialog: true, owner: '#taxcodes' }

所有者的想法是,如果存在初始路线是这样的情况,我们需要对话框后面的东西。

现在将configureRoute 中的router.route 调用替换为:

router.route(config.routePattern, function (fragment, queryString) {
    if (config.dialog) {
        if (!router.activeInstruction()) {
            // No current instruction, so load one to sit in the background (and go back to)
            var loadBackDrop = function (hash) {
                var backDropConfig = ko.utils.arrayFirst(router.routes, function (r) {
                    return r.hash == hash;
                });
                if (!backDropConfig) {
                    return false;
                }
                history.navigate(backDropConfig.hash, { trigger: false, replace: true });
                history.navigate(fragment, { trigger: false, replace: false });
                queueInstruction({
                    fragment: backDropConfig.hash,
                    queryString: "",
                    config: backDropConfig,
                    params: [],
                    queryParams: {}
                });
                return true;
            };

            if (typeof config.owner == 'string') {
                if (!loadBackDrop(config.owner)) {
                    delete config.owner;
                }
            }
            if (typeof config.owner != 'string') {
                 if (!loadBackDrop("")) {
                      router.navigate("");
                      return; // failed
                 }
            }
        }
        var navigatingAway = false;
        var subscription = router.activeInstruction.subscribe(function (newValue) {
            subscription.dispose();
            navigatingAway = true;
            system.acquire(config.moduleId).then(function (dialogInstance) {
                dialog.close(dialogInstance);
            });
        })
        // Have a route. Go back to it after dialog
        var paramInfo = createParams(config.routePattern, fragment, queryString);
        paramInfo.params.unshift(config.moduleId);
        dialog.show.apply(dialog, paramInfo.params)
            .always(function () {
                if (!navigatingAway) {
                    router.navigateBack();
                }
            });
    } else {
        var paramInfo = createParams(config.routePattern, fragment, queryString);
        queueInstruction({
            fragment: fragment,
            queryString: queryString,
            config: config,
            params: paramInfo.params,
            queryParams: paramInfo.queryParams
        });
    }
});

确保将dialog 导入到模块中。

【讨论】:

  • 我很想看一个例子!我不清楚在loadUrl 中要覆盖什么,因为它基本上只是将路由与模式匹配。
  • 我会在某个时候试一试。我刚开始使用子路由器,所以它对我来说也很新!
  • 另一种可能性是您可以挂接到众多路由器事件之一,例如router:route:activating,并在那里处理对话。
  • @Brett,我已经开始了最大可能大小的赏金,所以欢迎你用一些代码 sn-ps 来回答。
  • 我已经尝试过了。在 webapp 中使用感觉很好,但我真的不喜欢我不得不编辑 durandal 模块本身。
【解决方案2】:

好吧,当您对家庭视图模型的激活数据使用技巧时,可能不需要所有这些。 看看我作为答案创建的Github repo

这个想法是路由接受一个可选的激活数据,你的家庭虚拟机的激活方法可以检查并相应地显示所需的模式。

这种方式的好处是您根本不需要接触现有的 Durandal 插件或核心代码。 我不确定这是否完全符合您的要求,因为要求没有详细说明。

更新: 好的,我现在已经更新了 repo 来满足泛化的额外要求。本质上,现在我们在 shell 内利用 Durandal 的 Pub/Sub 机制,或者将其放置在您想要的任何其他位置。在那里,我们监听路由器 nav-complete 事件。当这种情况发生时,检查指令集并搜索给定的关键字。如果是这样,则触发模态。通过使用 navigation-complete 事件,我们还确保主 VM 正确且完全加载。 对于那些您想要导航到#signin 的黑客,只需手动将它们重新路由到您想要的任何位置。

【讨论】:

  • 回购 +1,但您错过了一点。我真的想要open on top of whatever page there was before 的对话。您的解决方案需要修复您可以登录的每个潜在视图模型。你能想出一个更通用的解决方案吗?
  • 我已更新问题以更好地说明可能的用例,请看一下!
  • 看起来更好。但是您的示例仍然存在一些问题。我注意到signin 路由器绑定到home 路由。那么,如果我要添加路由about,我是否还必须添加about(/:showModal)?这是我要避免的。
  • 另一个关于添加的更新......基本上当你想要另一个路线时,你只添加普通路线,但包括可选的 (/:showModal) 参数。因此,它能够同时服务于模态/非模态视图。没有任何登录绑定,因为现在观察到路由器事件并且只检查当前路由指令。这样您就可以将模式重复用于多条路线
  • 这是不可取的。我真正寻找的是不绑定到任何页面的#signin 路由。任何页面都可能包含它自己的可选参数,并且不应该知道 Sing-in 对话框的模式。
【解决方案3】:

在 cmets 中扩展我的建议,也许这样的事情会奏效。只需在router:route:activating 或其他类似事件之一上配置事件挂钩并拦截/#signin 的激活。然后使用这个钩子作为显示对话框的一种方式。请注意,此示例仅用于说明目的。我在工作时无法提供工作示例。 :/ 我回家后可以完成,但至少这给了你一个想法。

router.on('router:route:activating').then(function (instance, instruction) {
  // TODO: Inspect the instruction for the sign in route, then show the sign in 
  // dialog and cancel route navigation.
});

【讨论】:

  • 这似乎是一个优雅的解决方案。慢慢来,举个例子。
猜你喜欢
  • 2014-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-01
  • 2015-12-19
  • 1970-01-01
  • 2019-12-15
  • 2016-05-23
相关资源
最近更新 更多