Backbone.js PushStates: Fallback for Internet Explorer not working - javascript

My site has just implemented pushstates in Backbone.js and the entire site breaks for IE. How should I create a fallback for IE?
What I am trying to achieve
Main URL: http://mydomain.com/explore
Another URL: 'http://mydomain.com/explore/1234
The main page of the site is http://mydomain.com/explore which triggers the router function explore.
When a user visits http://mydomain.com/explore/1234, Backbone's router will trigger the function viewListing, which is the same as function explore, but also includes details for item id 1234.
Backbone.js Router
// Router
var AppRouter = Backbone.Router.extend({
routes: {
'explore': 'explore',
'explore/:id': 'viewListing',
},
explore: function() {
this.listingList = new ListingCollection();
// More code here
},
viewListing: function(listing_id) {
this.featuredListingId = listing_id; // Sent along with fetch() in this.explore()
this.explore();
}
});
App = new AppRouter();
// Enable pushState for compatible browsers
var enablePushState = true;
// Disable for older browsers (IE8, IE9 etc)
var pushState = !!(enablePushState && window.history && window.history.pushState);
if(pushState) {
Backbone.history.start({
pushState: true,
root: '/'
})
} else {
Backbone.history.start({
pushState: false,
root: '/'
})
}
Problem: As you can see in the above code, I tried to disable pushstates with pushState: false if it is an incompatible browser.
However for IE to access what would normally work for a normal browser (http://mydomain.com/explore), IE will need to go to http://mydomain.com/explore/#explore which is making things confusing! Further more to access http://mydomain.com/explore/1234 IE needs to go to http://mydomain.com/explore/#explore/1234
How should this be fixed?

If you don't want http://mydomain.com/explore/#explore url, then you have to redirect to
http://mydomain.com/#explore so Backbone will start with it instead.
if(!pushState && window.location.pathname != "/") {
window.location.replace("/#" + window.location.pathname)
}
UPD: you'll probably have to remove the leading slash when setting path as a hash window.location.pathname.substr(1)
UPD2: if you want /explore/ to be the root for your backbone routes then you have to exclude it from routes and set as a root in History.start({root: "/explore/"})
routes: {
'': 'explore',
':id': 'viewListing',
}

Related

Backbone Marionette is not firing routes

I'm creating a simple application using Backbone and Marionette. It's just to fetch a list of Wordpress posts (using an API) and display it.
It's a very simple app so it's not modularized.
I have the following (it's all placed in the same file):
if ( Backbone.history )
Backbone.history.start({ pushState: false });
if ( Backbone.history.fragment === '' )
API.listAllPosts();
else
API.listSinglePost( Backbone.history.fragment );
// Is not firing anything from here...
MyBlog.Router = Marionette.AppRouter.extend({
appRoutes: {
'': 'listPosts',
':post_name': 'listSingle'
},
listPosts: function() {
console.log('router');
API.listAllPosts();
},
listSingle: function(model) {
console.log('router, single');
API.listSinglePost(model);
}
});
// ...to here
var API = {
listAllPosts: function() {
// Fetch all posts and display it. It's working
},
listSinglePost: function(model) {
// Fetch a single post and display it. It's working
}
}
MyBlog.addInitializer(function() {
console.log('initializer'); // It's firing
new MyBlog.Router({
controller: API
});
});
As Derick Bailey, Marionette's creator, said about using triggers on naviagate:
it encourages bad app design and it is strongly recommended you don’t
pass trigger:true to Backbone.history.navigate.
What I'm missing here?
You start the Backbone history before creating the router instance.
Just move that to after the router is created.
MyBlog.addInitializer(function() {
new MyBlog.Router({ controller: API });
// should be started after a router has been created
Backbone.history.start({ pushState: false });
});
The other thing is that the callbacks should be defined inside of a controller or you should change appRoutes to routes.
The major difference between appRoutes and routes is that we provide
callbacks on a controller instead of directly on the router itself.
[...]
As the AppRouter extends Backbone.Router, you can also define a routes
attribute whose callbacks must be present on the AppRouter.
Move this
if ( Backbone.history )
Backbone.history.start({ pushState: false });
if ( Backbone.history.fragment === '' )
API.listAllPosts();
else
API.listSinglePost( Backbone.history.fragment );
after the point where your App gets started or inside an initialize:after event handler.
Check this previous question: Marionette.js appRouter not firing on app start

backbone.dualStorage load from localStorage after refresh

I am trying to build an offline capable app with Backbone and dualStorage. I am having a problem where if I go offline and refresh the app 404's.
This is my current start up code. It checks if a cookie is present and if so tries to instantiate that session again. The problem is with the refresh fetch() errors out and I get a 404. How do I make dualStorage check the localStorage if online endpoint isn't available?
The localStorage is populated with the right data, and is made so by calling Session.save(); after successful login.
// Start App
Router = new AppRouter({});
var cookieSessionName = window.location.hostname.replace(/[^A-z0-9]/ig, '-') + '-cisession';
if($.cookie(cookieSessionName) !== null) {
Session.set({ id: $.cookie(cookieSessionName) });
Session.fetch({
success: function()
{
$('.user-name').html('Hi, '+Session.get('first_name'));
$('.logged-in').show();
$('.logged-out').hide();
property.collection.fetch();
owners.collection.fetch();
}
});
} else {
$('.logged-out').show();
$('.logged-in').hide();
}
Backbone.history.start({ pushState: true, root: '/' });

Main Router appends a # at the end of route "" cause in dom referencing anchors not work in Backbone/Require app

In a Backbone.Router main router, I have "" route for my beginning route on app load. Its method decides to whether to create and render login view first or home view. When localhost:8080/indexapp is visited with no route it triggers the first route below, the empty "" route. After login and arriving homeview, url in address bar looks like localhost:8080/indexapp#. Form this point ordinary anchor hrefs referencing some dom elements currently available in the document does not lead to them. If I delete the hash at the end of the url and refresh the page, anchors work as intended.
What is the reason for that? What is the way to solve this issue?
mainrouter:
define([
'jquery',
'ratchet',
'underscore',
'backbone',
'forms/formsdatamodel',
'login/loginview',
'register/registerview',
'home/homeview',
],
function($, Ratchet, _, Backbone, formsDataModelInst, LoginView, RegisterView, HomeView){
var MainRouter = Backbone.Router.extend({
routes: {
"": "render_home",
"login": "render_login",
"register": "render_register",
},
initialize: function(){
this.listenTo( formsDataModelInst, 'sync', this.render_home );
},
render_home: function(){
if( formsDataModelInst.get('loginp') == true ){ //show you app view for logged in users!
this.homeView = new HomeView();
this.homeView.render();
}
else if ( formsDataModelInst.get('loginp') == false ){ //not logged in!
Backbone.history.navigate("/login", {trigger:true});
}
},
render_login: function(){ //display your login view
this.loginView = new LoginView;
this.loginView.render();
},
render_register: function(){ //display your register view
this.registerView = new RegisterView;
this.registerView.render();
},
});
return MainRouter;
});
My idea is to avoid empty url after # in address bar to overcome not working anchors. I do not know to make address bar url without # in hitting empty route. I have modified the main router to avoid empty url after # by:
associating "" route to a method, gate, that navigates to a route, "/home", that is associated with the method render_home_or_login that decides to render which.
routes now after modification:
routes: {
"": "gate",
"home": "render_home_or_login",
"login": "render_login",
"register": "render_register",
}
gate method added:
gate: function() {
Backbone.history.navigate("/home", {trigger:true});
},
This way the route will show like localhost:8080/indexapp#home at its bares case.
Avoiding emty url in address bar has solved the problem.

Nested resources and path

I'd like to nest resources in Ember, but to be able to access them with a short URL.
For example: mysite.com/admin will open the route: /routes/profiles/settings/admin
Is it possible to do something like that using Ember?
I'm currently using Ember 1.7 with Ember App Kit.
I tried the following but it doesn't work:
var Router = Ember.Router.extend();
Router.map(function () {
this.resource('profile', function () {
this.resource('profile.settings', { path: '/settings' }, function () {
this.resource('profile.settings.admin', { path: '/admin' });
);
});
Thanks.
Your code doesn't work because your inner most resource is inheriting the /profile path from the outer most resource and /settings from the middle resource. If you want it to be just plain /admin, you'd have to do something like this:
this.resource('profile', { path: '' }, function() {
this.resource('profile.settings', { path: '' }, function() {
this.resource('profile.settings.admin', { path: '/admin' });
});
});
However, this is going to get pretty hairy when you have more routes that each want top-level paths. You might find it easier to just declare a admin route at the top level, then redirect using the redirect hook in the route.

Backbone route matching doesn't work

I have got three routes like this:
var appRouter = Backbone.Router.extend({
routes: {
"": "index",
"questionnaire/info/id/:id": "questionnaireInfo",
"questions/edit/*params": "questionEdit"
},
questionnaireInfo: function(id) {
$('#app-body').load('/dashboard/questionnaire/info/id/' + id);
},
questionEdit: function(questionnaireId) {
console.log(questionnaireId, params);
},
index: function() {
console.log('index');
}
});
And i initialize them like this:
var appRouting = new appRouter;
Backbone.history.start({
pushState: true,
silent: false,
root: '/dashboard/'
});
On first page load the route matches, it even console.log the proper messages. But i have got a link element like this:
Home Page
It doesn't match the "" route. And this href element doesn't match the "questionnaire/info/id/:id" route:
Load
How can i make this working? Thanks.
Maybe you're missing the '/' between '/dashboard' and each route
is index firing? in general the backbone docs recommend listing your routes from most specific to least specific. It's possible that the other roots aren't being called because "":"index" catches everything.
You need a trailing slash in your Backbone.history.start call for the root argument.
var appRouting = new appRouter;
Backbone.history.start({
pushState: true,
silent: false,
root: '/dashboard/'
});
You can see this in the example for Backbone.history in the docs.
I had the same problem. It seems that the links that call the routes have to be prepended by a "#" character. So if the root of the router is
/dashboard/
and your route is
"questionnaire/info/id/:id": "questionnaireInfo"
than the link triggering the route should be for example
Load
Hope this helps.

Categories

Resources