【问题标题】:How to navigate, with history, in MVC 5 SPA template?如何在 MVC 5 SPA 模板中使用历史导航?
【发布时间】:2014-10-15 02:58:09
【问题描述】:

我正在为VS2013 SPA MVC-5 模板中的导航而苦苦挣扎。我做了 2 个假设(因为我找不到好的参考资料),但似乎我错了,或者只是效果不佳:

  • 以为导航是基于典型的spa # url encoding,比如直接导航到users account management page应该可以使用:http://localhost:18759/#/manage

  • 我还认为,在浏览网站(单页)时,我认为这些 # url's 由默认的 knockout.js 文件构成,这些文件包含在模板中。结合前面的假设,这将导致适当的历史积累。

http://localhost:18759/#/manage 似乎没有导航到manage 页面(其他# url's 也不起作用)。

我注意到一些框架可以处理这个问题(navroutersammy.js),但据我所知,实现它们需要相当多的努力,特别是如果它已经在模板中就位。

至于我做出这些假设的原因,有一篇文章here,暗示这是因为这部分:

// app.viewmodel.js - there is a method called "addViewModel()
if (typeof (options.navigatorFactory) !== "undefined") {
        navigator = options.navigatorFactory(self, dataModel);
    } else {
        //suggests "#"-hash navigation
        navigator = function () {
            window.location.hash = options.bindingMemberName;
        };
    } 

但在我自己的app.viewmodel.js 中,这些行根本没有参考哈希:

if (typeof (options.navigatorFactory) !== "undefined") {
        navigator = options.navigatorFactory(self, dataModel);
    } else {
        navigator = function () {
            self.errors.removeAll();
            self.view(viewItem);
        };
    }

app.viewmodel.js 这里有对哈希的引用,但这似乎无法处理导航:

// Private operations
function cleanUpLocation() {
    window.location.hash = "";

    if (typeof (history.pushState) !== "undefined") {
        history.pushState("", document.title, location.pathname);
    }
}

function getFragment() {
    if (window.location.hash.indexOf("#") === 0) {
        return parseQueryString(window.location.hash.substr(1));
    } else {
        return {};
    }
}

我的导航代码如下所示:

<ul class="nav navbar-nav navbar-right">
     <li data-bind="with: user"><a href="#" data-bind="click: manage">manage</a></li>
</ul>

导航工厂非常默认,例如:

app.addViewModel({
    name: "Manage",
    bindingMemberName: "manage",
    factory: ManageViewModel,
    navigatorFactory: function (app) {
        return function (externalAccessToken, externalError) {
            app.errors.removeAll();
            app.view(app.Views.Manage);

            if (typeof (externalAccessToken) !== "undefined" || 
                typeof (externalError) !== "undefined") {
                   app.manage().addExternalLogin(externalAccessToken, externalError);
            } else {
                app.manage().load();
            };
        }
    }
});

问题

  • 这应该有效吗?如果是这样,我该如何解决?
  • 或者我需要像sammy.jsnavrouter 这样的额外组件吗?

【问题讨论】:

  • 我不知道 Knockout 内置了任何路由。我目前正在使用github.com/finnsson/pagerjs,这是一种设计用于与 Knockout 一起使用的路由解决方案。它并不完美,但做得很好。

标签: knockout.js visual-studio-2013 asp.net-mvc-5 single-page-application


【解决方案1】:

经过一天的挣扎,我得出以下结论:

  • # 导航似乎没有出现在默认的 VS2013 MVC 5 SPA template 中。

我设法让它工作了,所以我在这里总结一下实现方法。

虽然 Paul Manzotti 建议的 pagerjs 做得很好,但我选择使用 sammy.js 来执行导航。其他导航框架应该也能正常工作。

所以第一步是从nuget获取:

安装包 sammy.js

安装sammy.js 后,我们需要更改默认VS2013 MVC 5 SPA templatejavascript 文件。我总结一下:

首先启用sammy.js。放置代码的位置有多种选择,但由于我想在整个应用程序中使用它,所以我将其放入:~/Scripts/app/_run.js,如下所示:

//_run.js
$(function () {
    app.initialize();

    // Activate Knockout
    ko.validation.init({ grouping: { observable: false } });
    ko.applyBindings(app);

    //provides basic '#' navigation
    //run this function after the initialization of the
    //default knockout code.
    Sammy(function () {
        //"#:view" is the parameter's name of the data after the hash tag 
        //it is stored in "this.params.view"
        this.get('#:view', function () {
            //call the navigation functions
            //these are created in the default knockout initiallization
            app["_navigateTo" + this.params.view]();
        });
    }).run("#Home");//Specify the starting page of your application
 });

接下来,我希望# 导航“开箱即用”。一个关键部分是在导航时# 参数被添加到url 所以我需要挂钩navigateTo 函数。 ~/Scripts/app/app.viewmodel.js有办法:

变化:

//app.viewmodel.js
...
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
self["navigateTo" + options.name] = navigator;

收件人:

//app.viewmodel.js
...
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
// Override default routing to just set the hash
self["navigateTo" + options.name] = function() {
            window.location.hash = options.name;
    };

    //this one is used by sammy to perform actual default routing
    self["_navigateTo" + options.name] = function() {
        navigator();
    };

最后一个细节必须修复,即当用户刷新其页面时,默认路由将转到#Home。这是由于~/Scripts/app/app.viewmodel.js 中的以下代码:

//app.viewmodel.js
...
self.navigateToLoggedIn = function (userName, accessToken, persistent) {
    self.errors.removeAll();

    if (accessToken) {
        dataModel.setAccessToken(accessToken, persistent)
    }

    self.user(new UserInfoViewModel(self, userName, dataModel));

    //this line only routes to "#Home" when navigation
    //after the login... or register, or something else        
    if (window.location.hash === "#Login" || window.location.hash === "#Register")
        self.navigateToHome();
};

因此,在此代码中添加正确的 if 语句,# 就到位了。

访问管理页面的正确网址是:

http://localhost:18759/#Manage

我必须说,我没有时间对其进行适当的审查(# url 编码可能会在其他地方使用并可能导致冲突)。如果我发现一些问题,我会更新这篇文章。

还有一点:当然,在连接到路由的方式上有多种选择。我选择这些步骤是因为它们适用于我正在处理的案例。

【讨论】:

    猜你喜欢
    • 2016-04-26
    • 2014-11-16
    • 1970-01-01
    • 2012-05-12
    • 2014-09-06
    • 2015-04-22
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多