Backbone route on IE - javascript

I'm trying use the history backbone root but it doesn't work fine on IE (or other browsers which don't support history api).
My webapp has this map, where each module makes a request, but actions should call a function:
site/moduleA/
site/moduleA/action1/ID
site/moduleB/
site/moduleB/action1/ID
mapping:
var MyRouter = Backbone.Router.extend({
routes: {
"moduleA/": "homeA",
"moduleA/action1/:id": "action1",
// ...
}
}
var app = new MyRouter();
Backbone.history.start({pushState: true});
I'm navigating using this:
app.navigate('moduleA/',{trigger:true});
or
app.navigate('/moduleA/action1/4334',{trigger:true});
(I'm getting links click events and calling navigate(link.href,{trigger:true}) )
Every is working fine on Chr/FF (browsers with history api support), and the url is updated in the browser and the function is call.
However, in IE the url is replaced by this hash format: site/#moduleA/
In order to solve that I've tried set the root in history.start
Backbone.history.start({pushState: true, root:'/moduleA/'});
But, now IE replace the url using this format: site/moduleA/#moduleA/ or site/moduleA/#moduleA/action1/432432.
So, Why IE is repeating the root in the url ?
How can I solve this?
Thanks in advance

By setting root to '/moduleA/' you are telling backbone to use site/moduleA as the root, this is the expected behavior.
Rememeber in backbone
routes: {
"moduleA/": "homeA", // #moduleA/
"moduleA/action1/:id": "action1" // #moduleA//action1/:id
}
is different from
routes: {
"/moduleA/": "homeA", // #/moduleA/
"/moduleA/action1/:id": "action1" // #/moduleA//action1/:id
}
its good to keep this in mind when using app.navigate.

Related

Ember.js re-render links on locale change

My application uses the ember-i18n addon. For SEO purposes, I wanted to put a /[lang-code]/ in the URL.
When changing the language, history.pushState can change the URL and its alright, but the links on the page do not change for the new language, even though router.rootURL is changed.
So far I can just change the window.location.pathname, but thus a new network request is being made.
I tried overriding the href-to helper (from ember-href-to addon) and added this:
_recomputeOnLocaleChange: Ember.observer('i18n.locale', function() {
console.log("here?");
this.recompute();
})
It executes on locale change, but apparently recompute does nothing. Even with setTimeout (because perhaps rootURL didn't change yet). Links are all the same.
Can you suggest anything? Would be greatly appreciated! Thank you!!
In the didTransition hook of your router.js file, check if the locale has changed and set a property on a service to the new locale if it has. Then in your helper, you can check that property.
In router.js:
oldI18nLocale: null,
didTransition() {
this._super(...arguments);
// ...
let i18nLocale = this.get('locale.i18n');
if (i18nLocale !== this.get('oldI18nLocale')) {
this.set('someService.i18nlocale', this.get('locale.i18n');
}
this.set('oldI18nLocale', i18nLocale));
}
In your helper:
recomputeOnLocaleChange: Ember.observer('someService.i18nLocale', function() {
this.recompute();
})
Hope this works.

What is the best practice for maintain a menu after a page reload? I have this custom code which feels wrong

I have a custom Menu which loads a new MVC View for each click as I want.
I load the new View by setting window.location.href. To make it work I have to set the baseURL (the name of the website) each time. To Store the state of the menu I use URL's querystring.
My concerns is in the use of:
'/WebConsole53/' // hardcode baseurl i have to apply each time manually
Setting window.location.href to load the new View from JavaScript // Is this the best way or should I use some URL/Html helpers instead?
I store the state of the selected menuItem in the querystring ("menu") // Is it more common to store that kind in Session/Cookie?
Any thoughts, corrections and suggestions would be much appriciated - thanks.
_Layout.cshtml
var controller = $self.data('webconsole-controller');
var action = $self.data('webconsole-action');
var menu = "?menu=" + $self.attr('id');
var relUrl = controller + "/" + action + menu;
var url = urlHelper.getUrl(relUrl);
window.location.href = url;
UrlHelper.js
var urlHelper = function () {
var getBaseUrl = '/WebConsole53/',
buildUrl = function(relUrl) {
return getBaseUrl + relUrl;
};
var getUrl = function(relUrl) { // relUrl format: 'controller/action'
return buildUrl(relUrl);
};
return {
getUrl: getUrl
};
}();
I Use MVC 5.
You can save this problem using Route. Through the route you know exactly where you are located in you application.
_Layout.cshtml is definetely not the place to have this javascript. Maybe you are missing some MVC concepts, I would recommend you to read a bit more about routes and routelinks
I hope this helps you a bit: RouteLinks in MVC
'/WebConsole53/' // hardcode baseurl I have to apply each time manually
sometimes you need to access your root from javascript where you don't have access to server-side code (eg #Html). While refactoring may be the best option you can get around this by storing the baseurl once, using server-side code, eg in _layout.cshtml:
<head>
<script type="text/javascript">
var basePath = '#Url.Content("~")'; // includes trailing /
</script>
... load other scripts after the above ...
</head>
you can then reference this everywhere and it will always be valid even if you move the base / migrate to test/live.
Setting window.location.href to load the new View from JavaScript // Is this the best way or should I use some URL/Html helpers instead?
Depends on your requirements - you could use $.ajax (or shortcuts $.get or $.load) to load PartialViews into specific areas on your page. There's plenty of examples on SO for this and the jquery api help.
Or just use <a> anchors or #Html.ActionLink as already suggested. Without needing menu= (see next) you don't need to control all your links.
I store the state of the selected menuItem in the querystring ("menu") // Is it more common to store that kind in Session/Cookie?
If you change the page, then you could query the current url to find which menu item points to it and highlight that one (ie set the menu dynamically rather than store it).
This would also cover the case where you user enters the url directly without the menu= part ... or where your forget to add this... not that that would happen :)
Additional: You can specify which layout to use in your view by specifying the Layout at the top of the view, eg:
#{
Layout = "~/Views/Shared/AltLayout.cshtml";
}
(which is also one of the options when you right click Views and Add View in visual studio)
Without this, MVC uses a configuration-by-convention and looks at Views/_ViewStart.cshtml which specifies the default _layout.cshtml.
If you don't want a layout at all, then just return PartialView(); instead

Allowing href links with hashes on them to bypass SammyJS routing

I have the following server-side URL mappings defined:
/main/item1
/main/item2
I've added SammyJS routing support so that I am able to do the following:
/main/item1#/ /* main view */
/main/item1#/topups /* topup view */
I've set up SammyJS like so:
s.app = Sammy(function() {
this.get('#/topups', function() {
console.log('Initializing topups view.');
});
this.get('#/', function() {
console.log('Initializing main view.');
});
});
The problem is, I have a summary section in my page that redirects to the topup view of a different "item". E.g., I am at the url /main/item1#/, and in this page, there exists a tag item 2's topups.
I expect to be redirected (page refresh) to the new URL, however, it seems like SammyJS is intercepting the /main/item2#/topups call and simply running the this.get('#/topups') route I've defined.
I expect that since the URL paths before the hash, /main/item1 and /main/item2 are different, the SammyJS routing won't be triggered.
Is there a way to prevent this behavior from happening in SammyJS?
I don't know much about Sammy but I can tell you from the way any router behaves, is that it catches the first match in the routing possibilities, and so, anything that ends with #/topups will be considered the same as long as it's after the hash sign.
so you better define the router this way:
this.get('#/topups/:item', function() {
console.log('Initializing topups view for item: '+ item);
})
and then call the pages with URLs like:
item 2's topups
I hope this is what you're looking for
I'm pretty sure using the full URL redirect you.
Item 2 top ups for the lazy coder
However, that will cause the page to reload. If you modify Labib's answer you can have an even better solution:
this.get('#/topups/:item', function () {
console.log('Doing some post processing on current item');
console.log('Now redirecting you to ' + this.params.item);
window.location.href = 'http://example.com/menu/# + this.params.item +#/topups';
});
Again this will cause the page to reload, but if you do not mind that, then just either method.
NOTE: Sammy will also check form submission for you. This trips me up EVERY time I use it. This post has the solution.

Sammyjs and pagerjs routes

I'm started using a combination of KnockoutJS (2.2.1), SammyJS (0.7.4) and PagerJS (latest from github with jquery hashchange) to create a single page app and I've run into a problem with the routes as they do not work in Chrome Version 24.0.1312.57 m or Firefox 16.0 (for some reason it actually works in IE7).
Using sammyjs I've specified the routes that the app should react on and their corresponding actions, for example loading user data. The same routes are used in pagerjs to specify which page to display. For some reason the sammyjs code is executed but not the pagerjs code.
When updating the route, for example going from #!/ to #!/user, pagerjs doesn't switch to the new page, but data is updated as expected when switching between #!/user?uid=123 and #!/user?uid=321 . However, when removing the sammyjs code it works - the switch between pages works but data will of course not update properly.
It seems like SammyJS terminates further execution by pagerjs, but as I'm quite new to these libraries it might very well be my code misbehaving. Greatful for any insights.
The javascript code looks something like this:
var UserModel = function () {
var self = this;
self.userId = null;
self.user = ko.observable();
self.userid = ko.observable();
// Load
self.load = function(userId) {
self.loadUser(userId);
};
// Load user data
self.loadUser = function(userId) {
console.log('Loading user data');
};
// Client-side routes
Sammy(function () {
// Overview - datatables in tabs
this.get('#!/', function () {
console.log('Start page');
});
// User - details
this.get('#!/user', function () {
console.log('user page');
self.userId = this.params.uid;
self.load(self.userId);
});
}).run();
}
// Renders user info
$(document).ready(function () {
if ($('#user-info').length) {
var userModel = new UserModel();
pager.extendWithPage(userModel);
ko.applyBindings(userModel);
// Load initial data via ajax
userModel.load();
pager.Href.hash = '#!/';
pager.startHashChange();
}
$('.dropdown-toggle').dropdown();
});
And here goes the HTML with the pagerjs data-bindings:
<div class="container">
<div data-bind="page: {id: 'start'}">
Startpage
</div>
<div data-bind="page: {id: 'user', params: ['uid']}">
User page
</div>
</div>
I think I got it.
You need to add
this.get(/.*/, function() {
console.log("this is a catch-all");
});
after your last this.get. Then Sammy doesn't stop the event.
I've got it working by changing PagerJS to use the naïve history manager instead of jQuery hashchange. In other words this line:
pager.startHashChange();
was changed to:
pager.start();
As of magic it also works in IE7 even if the docs at http://pagerjs.com states it doesn't. Well, for me it does work
// 1. iff using naïve hashchange - wont work with IE7
pager.start();
As long as you include the hashchange plugin pager.start() will use it.
Is the same as the naïve, but you need to include the jQuery hashchange plugin first.
http://pagerjs.com/demo/#!/navigation/setup

Getting URL fragment instead of hash in Backbone.js

I have struggled with this for quite a long time. I'm using Backbone.js along with Require.js. I'm trying to tell Backbone to display a specific view according to the current URL (not hash). My code looks like this:
define(['jquery','underscore','backbone', 'views/manage_pages'], function($, _, Backbone, ManagePages) {
var AppRouter = Backbone.Router.extend({
routes:{
'/new_page': "showPageForm",
'/edit_page': "showPageEditForm",
'*actions': "showPageForm"
},
});
var initialize = function(){
appRouter = new AppRouter;
appRouter.on("route:showPageForm", function(){
console.log('hej');
});
appRouter.on("route:showPageEditForm", function(){
console.log('ho');
});
var page_form = new ManagePages();
Backbone.history.start();
}
return {
initialize: initialize
}
});
So basically when the user goes to http://example.com/new_page it should log <code>hej</code> and when he goes to http://example.com/editpage it should log <code>ho</code>. I don't know how to accomplish this or even if it is possible. Can anyone help me?
You could use Backbone's support for HTML5 push-state, which uses the URL path by default (instead of the hash fragment). Just specify it as an option when calling history.start:
Backbone.history.start({ pushState: true });
(Note that if your application is located in a sub-path under the domain, then you can tell Backbone to use that as the root -- Backbone.history.start({pushState: true, root: "/myapproot/"}) -- although it looks like that's not a concern in your case.)

Categories