Keep track of history outside of Backbone Router - backbone.js

Is there a way to keep track of history outside of a backbone router. I have a pagination service provided by Laravel, the links between pages are like the following "#leads?page=7". Everything works, but I have no clue right now on how to apply track of history outside of the router. The router works for the dashboard options but not for the pagination.

I am not using Backbone.Marionette for a single page app, but more for varius tools on my site. I wanted to have Laravel render the initial pages for SEO reasons. I had Laravel write the first pagination block then I have Marionette rewrite it after any interaction. You have to love twice the amount of work, but I like the fact that I don't have to worry about search engine not seeing the page.
In order to do this, I ended up not using the backbone router for this tool at all. I used javascript history. It is only available in newish browsers, but that is what our site supports. If you do need to support older browsers there is a shim, but I have not used it.
// add to history
history.pushState('', 'Search: ' + tags, url);
// get current history
updateUrl: function(){
var currentState = history.state;
return (currentState) ? currentState : $(this)[0].baseURI;
}
It definitely was more work, but I was able to get it working the way I wanted.

Related

Browser history doubled in angularjs app

So I am using a web app which has an iframe pointing to my angularjs application. If I navigate around the parent web app, I notice that the browser history is doubled up. For example, if I navigate to contacts > about us > home, then the history will be:
- home
- about us
- about us
- contacts
- contacts
If I point the iframe to a simple web page with no angular, then the parent app history works fine. I came across this bug on the angularjs website with a similar issue which appears to have been resolved in an earlier version of angular: https://github.com/angular/angular.js/issues/1054 but it doesn't seem to be resolved for some people. I'm using v1.2.26. Has anyone else experienced this issue?
So it turns out my assumptions about routing as the potential cause were correct, and I can't believe I didn't realize this sooner. Essentially my iframe app routes to different locations through some sort of event. Each route, while it does not impact the parent url, will add a new entry to the browser history (which is normal browser behaviour). The iframe app is designed upon page load to run through a couple routes, thus adding to the browser history, and making it appear like each parent page navigation was doubled.
The question now becomes, is there a way to prevent each route execution from adding the browser history?
UPDATE:
$location.replace() seems to be the only solution I can find. The app makes use of $location.path() a lot to trigger a new route; so I've modified it to be $location.path().replace(); This will replace the current history entry instead of adding a new one.

Backbone router: Use hashbangs

I was setting up a simple router in Backbone.js while I noted an issue, I googled for a long time and found some solutions.
The problem is that whenever I navigate to a url, this url will work while I'm inside the app itself, but once you bookmark it and return it will obviously return a 404 since that page doesn't exist.
Backbone.history.navigate('test', true);
This will navigate to http://www.domain.com/test, which is great, but once you refresh the same URL it returns a 404.
There are ways to get this to work with using a specific .htaccess file, but I'm building this inside an existing website, so I can't use this.
Is there a way in Backbone.js to revert to just using hashbangs? My url would then look like this `http://www.domain.com/#!/test
This might not be the cleanest URL, but at least it will work.
Or am I just missing something obvious here? It can't be that I'm the only person with this problem.
You're using pushState according to your url, so you've got 2 main options:
stop using pushState when starting Backbone history (which would be the equivalent of using a hashbang, except for SEO)
have your server return a page for every URL your app generates (it can be the same exact page)
In the second case, if you don't care about SEO, you can have a catch all rule always returning (e.g.) index.html containing your Backbone app. When you start Backbone's history, it will lokk at the URL and navigate to the proper page (although you might have a "double render" effect).
If you're worried about SEO, you need to return page content for SEO along with your Backbone app.

Alternative to the Backbone router without hash fallbacks and better back/forward state support?

I am currently using the Backbone router for my SPA. It sort of works OK, but I am having a number of small issues:
The problem is - I want to either have pushState navigation or none, meaning - app state gets executed (when I pass the "initial" URL to the router or I trigger a route via JS), but the address bar doesn't change. I don't want the hashChange fallbacks for IE9. It will work just fine if IE9 does not get the back button and the changing address bar, but still retains navigation state within the application. I can then show the users a URL which they can bookmark and the server will route that page and the app state will bootstrap based on the initial URL. The address bar not changing in IE during in-app navigation is a compromise I am willing to accept.
Another problem that I have is that I am using navigate(url, {trigger: true}) for my intercepted HREFs, and the back button doesn't work (does nothing). But I really need to change app state on forward/back navigation, even if it gets rebuilt for that particular URL - I'd rather rebuild the state.
What are my options in terms of routers that I could use? Like I said, I don't want to have hashbang fallbacks (meaning - I only want to have ONE way of representing URLs in the application, period).
What should I use? Director.js? History.js? There seems to be quite a number of router/history libraries out there but which one is the closest to what I'm looking for?..
Supposing you have Modernizr around scanning the HTML5 history support, if I understand well wouldn't a fast solution be, in your main layout js file, to add as event
'click a' : "navigate"
and add the function navigate to this layout as follow
navigate : function(e){
if(!Modernizr.history) document.location.href = $(e.currentTarget).attr("href");
},
to optimize, you could of course bind that only if history is not supported,don't forget to include "modernizr" in your layout. Hope this is an interesting answer for you..

Using Backbone.js Router with No Hash

I am building a multi-page application and would like to leverage Backbone's Router to initialize my views on page load. I haven't been able to find a way to leverage routers without using hashes and I don't need or want to use push state.
Basically, all I want to be able to do is use the Routers URL pattern matching to match the url and initialize my views depending on which page is loaded from the server.
Perhaps I am thinking about this all wrong or maybe there is a way to do this natively in Backbone Routers that I am missing. Any suggestions are greatly appreciated.
From the Backbone documentation (emphasis mine):
Backbone.Router provides methods for routing client-side pages, and connecting them to actions and events.
In other words, the Backbone Router was only designed to handle client-side URLS (the part after the hash), not server-side ones (the part before the hash). There might be a way to hack the Router and Backbone.History to use full URLs instead of just the hash, but it would not be easy and I'd recommend against it.
One alternative you could consider is some sort of onDocumentReady logic that checks the URL of the page and, if it's hash doesn't match it's URL, adds a hash. This would make it so that if someone visits "/foo" your code would convert it to "/foo#foo", and the Backbone Router could be used normally.
Another option though would just be to write your own "router" of sorts, which will actually be simpler than Backbone's because it only needs to work once per page load. Here's a simple example:
var mockRouter = {
foo: function() {
// do stuff for page "foo"
},
bar: ...
}
$(function() {
mockRouter[window.location.pathname]();
});

Keeping state when rendering page from the server side

I'm currently building a single page app using backbone.js
In order to keep all application pages accessible and crawl-able I made sure that the server side can also render the pages when accessing them directly.
The problem is as follows:
When pushState is not available it initiates the router using the current URL (e.g. if I accessed a url with http://example.com/example the router will build the hash fragment on top of that url)
So:
Is there any way of handling this (besides redirecting the use)
If you are redirecting as soon as the JS (using pushState feature detection) you still have a problem of urls not having hash signs.
Generally asking, is there a better approach of designing this kind of application?
Thanks!
I think the evolving consensus is pushstate or nothing (ie to degrade web 1.0 and drop hash-bang routing all together) if SEO-friendly browsing matters to you.
Its one of the reasons I don't use Backbone.js and just use PJAX is that pushstate and DOM rendering times are so good you can be single page with very little JS and hash-bang routing has always been rather hackish.
Thus an option is to not use Backbone's router all together and just let something like PJAX (or DJAX or something similar) do the routing work and let Backbone just do the inner page event/rendering stuff (ie validating forms, modal windows, etc..).

Resources