I am struggling with the navigation in the VS2013 SPA MVC-5
template. I made 2 assumptions (because I couldn't find good references), but it seems I am wrong or it is just not working well:
It thought that the navigation was be based on the typical spa # url encoding
, for example to navigate to the users account management page
directly one should be able to use: http://localhost:18759/#/manage
I also thought that, when navigating across the website (single page) I thought that these # url's
where formed by the default knockout.js
files, which are included in the template. Combined with the previous assumption this would result in a proper history build-up.
http://localhost:18759/#/manage
does not seems to navigate to the manage
page (other # url's
doesn't work either) .
I noticed some that some frameworks are available to handle this ( navrouter
and sammy.js
) but as far as I can tell it takes quite some effort to implement them, especially if it's already in place in the template.
As for the reasons why I made these assumptions, there is an article here , which suggests this is in place due to this part:
// 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;
};
}
But in my own app.viewmodel.js
these lines are without reference to the hash at all:
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () {
self.errors.removeAll();
self.view(viewItem);
};
}
There is a reference to the hash in app.viewmodel.js
here, but this doesn't seem to handle the navigation:
// 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 {};
}
}
My code to navigate looks like this:
<ul class="nav navbar-nav navbar-right">
<li data-bind="with: user"><a href="#" data-bind="click: manage">manage</a></li>
</ul>
and the navigation factory is pretty default like:
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();
};
}
}
});
question(s)
sammy.js
or navrouter
? After a day of struggling I came to the following conclusion:
#
navigation doesn't seem present in the default VS2013 MVC 5 SPA template
. I managed to get it working, so I'll sum up the implementation method here.
Although pagerjs
, suggested by Paul Manzotti, does the job pretty well, I have chosen to use sammy.js
to perform the navigation. Other navigation frameworks should work just as well.
So the first step is to get it from nuget
:
install-package sammy.js
After the installation of sammy.js
we need to alter the default VS2013 MVC 5 SPA template
's javascript
files. I'll sum it up:
First enable sammy.js
. There are various options of where to put the code, but since I want to use it throughout the entire application I putted it in: ~/Scripts/app/_run.js
like this:
//_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
});
Next, I wanted the #
navigation to work "out of the box". A crucial part is that on navigation the #
parameter is added to the url
So I needed to hook in the navigateTo
functions. There is a way in ~/Scripts/app/app.viewmodel.js
:
Change:
//app.viewmodel.js
...
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
self["navigateTo" + options.name] = navigator;
To:
//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();
};
A final detail has to be fixed, and that is when a user refreshes it's page, the default route will go to #Home
. This is due to the following code in ~/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();
};
So add the proper if
statement to this code and the #
is in place.
The proper url to access the manage page will be:
http://localhost:18759/#Manage
I must say, I haven't had time to give it a proper review (the #
url encoding might be used elsewhere and may cause collisions). I will update this post if I find some issues.
Another point: of course there are various alternatives in the way to hook into the routing. I have chosen these steps because they work in the case I am working on.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.