backbone.js router route not trigger for the first time - backbone.js

I am using backbone 1.1.2, my router:
var App = Backbone.Router.extend({
views : {},
routes : {
"profile" : "profile",
"members" : "members",
"member/:id" : "loadMember",
'invite' : 'invite',
},
...... my handlers
and the router init code:
var app = new FW.Daily.App();
Backbone.history.start();
Backbone.history.loadUrl(Backbone.history.fragment);
when some one click an url like "www.domain.com/#profile" on mobile ( tried on android 4.2 and iphone 4 & 5), SOMETIMES the handler function does not call for the first time, only when opening the page one more time, it works and the subsequence page view works fine.
Desktop browsers do not have this problems so far (tried on ie 9 - 10, firefox, chrome, opera)
What is the problem here?

Related

Marionette/backbone app, href in a tag doesn't go to defined route

Here is my A tag in index.html.
<div class="menu-item">Login in
If I click it, it should go to '/login' route. But URL correctly changed to localhost:3333/#login in browser address input bar, but the page content shows no change, still in landing page.
Here is my code for starting Backbone history:
new Router();
Backbone.history.start({pushState: true, root: '/'});
Here is my code for router:
var Backbone = require('backbone');
var $ = require('jquery');
Backbone.$ = $;
var _ = require('lodash');
var Marionette = require('backbone.marionette');
var OuterLayout = require('../layout/outerLayout/outerLayout');
var ol = new OuterLayout();
var AppRouter = Backbone.Marionette.AppRouter.extend({
routes : {
'': 'index',
'signup' : 'signup',
'login' : 'login'
},
index : function () {
if(_.isEmpty(ol.el.innerHTML)) {
ol.render();
}
// outerLayout.footer.show();
},
signup : function () {
if(_.isEmpty(ol.el.innerHTML)) {
ol.render();
}
var ContentSignup = require('../layout/outerLayout/view/contentSignup/contentSignup');
ol.content.show(new ContentSignup());
},
login: function () {
if(_.isEmpty(ol.el.innerHTML)) {
ol.render();
}
var ContentLogin = require('../layout/outerLayout/view/contentLogin/contentLogin');
ol.content.show(new ContentLogin());
}
});
module.exports = AppRouter;
The result is that URL changed in the browser address input field, but the page content doesn't change. Then if I hit CMD + R to refresh the page, then the content will change, correctly reflecting the route.
Also the go back button on browser doesn't work, url changes, but the content doesn't change. I think I forget to call sth in my code to "refresh" the browser?
oh, I am using httpster to start a mini http server for this front-end development.
Have you tried this:
new AppRouter();
instead of this
new Router();
Unless you actually want to hit the server (which would just be /login and you'd deal with it on the server side). You should take the pub sub approach.
So in your view you would say:
triggers
"click .menu-item": "loginClicked"
Then in your controller you can listen to that event (if it's a composite view's childview that you're in you may have to prefix this with childview:):
#listenTo loginView, "login:button:clicked", (args) ->
App.vent.trigger "login:clicked"
Then in the router
API =
login: ->
new LoginsApp.Show.Controller
App.vent.on "login:clicked", ->
App.navigate "/login"
API.login()
So you end up navigating/hitting the same action that you would by going through the router, but you don't have to rely on the routes.
If you don't want to go that route I imagine the problem is that you need to say Backbone.history.navigate({trigger: true}) to get it to actually trigger the route in the approuter.
The best approach I've found is the approuter is there when the user clicks refresh or navigates directly to the page. But everything else should be handled in app with the pub sub approach. It gives you the most control that way.
Remove the slash (/) and use only "#route" on your hrefs, to avoid the browser from fetching the default document served at "/" from the backend.
By the way, watch your use of require, you should require the constructors at the top so the requirejs optimizer can fill in the dependencies on build time.
Something like:
//this before the component definition
var MyView = require("views/myview"),
AppLayout = require("views/layout");
//... later on your view/app/model definition
function foo(){
var view = new MyView();
}
I also think having a list of required stuff at the top of any file helps understanding it later on. ;)

Backbone.history is not updating in IE 9. Back button broken

In our app, we actually have two Backbone SPA applications. The first one is for login, registration and other features for unauthenticated users. The URL for this would be something like http://www.example.com/registration#signin. Once you login, you are redirected to our main Backbone app at http://www.example.com/ui#home.
In my main UI app, I am using Backbone.history without pushState. The App file looks something like:
define(function (require) {
var App = new Marionette.Application();
App.addInitializer(function (options) {
...
});
...
App.on('initialize:after', function () {
$(function(){
if (Backbone.history) {
Backbone.history.start({ root: '/ui' });
}
});
$.log("**WebApp**: Marionette app started.");
});
return App;
});
Of course, everything works flawlessly in any browser except IE 9 (and maybe 10, I need to check). In IE 9, all the routing works fine. Clicking links such as http://www.example.com/ui#anotherpage works. However, when the user clicks the Back button in their browser, they are not sent back to the last route fired. Instead, they are sent to http://www.example.com/registration#signin, which is the last page served by Node, our web server. As I click through links, I can see that history.length and Backbone.history.history.length are not updating.
All routes are fired from links/URL's. I'm not using router.navigate() within the code. Here are examples of our Router:
define(function (require) {
var Backbone = require('backbone'),
Marionette = require('marionette');
return Backbone.Marionette.AppRouter.extend({
appRoutes: {
"": "showHome",
"home": "showHome",
"foo": "showFoo"
}
});
});
And Controller:
define(function (require) {
var Backbone = require('backbone'),
Marionette = require('marionette');
return Backbone.Marionette.Controller.extend({
showHome: function () {
require(['webapp','modules/home'], function (WebApp) {
WebApp.module("Home").start();
WebApp.module("Home").controller.showModule();
});
},
showFoo: function () {
require(['webapp', 'modules/foo'], function (WebApp) {
WebApp.module("Foo").start();
WebApp.module("Foo").controller.showModule();
});
}
});
});
UPDATE:
On further research, it turns out the problem is that older versions of IE don't record hash changes in their history. See - Change location.hash and then press Back button - IE behaves differently from other browsers. But I'm still not sure what the workaround for this would be. I'm guessing it would somehow involve manually handling hash change events with a plugin such as jQuery Hashchange and doing... something? Manually setting IE's history? Or crafting a custom history object and using it when we detect a Back button in IE?
I was having the same problem in one of our apps for IE.
Starting backbone history like below works.
Backbone.history.start({
pushState: true,
hashChange: false
});
Update: As mentioned By T Nguyen,
When you set pushState to true, hash URL's no longer trigger routes. Unless you add server-side support for all your Backbone routes, you need to add an event handler on the client side which captures appropriate links and calls .navigate() on the route

Backbone.js - using browser back button router not initiating

In my app..
While i load the page or refresh the page my router not properly responding and calling the default view method. in case if i use other link to vist other hash url... works as well the back button doing fine.
Even while i load my page, my initiate function as well works fine. but the default method not triggering while i load the page and use the back button of the browser.
what cause this.. here is my code:
define([
"backbone",
"../model/model",
"../collection/collection",
"../views/appView",
"../views/listView",
"../views/appViews"],
function(Backbone,appModel,collection,appView,listView,appViews){
var appRouter = Backbone.Router.extend({
routes:{
"":"defaultView",
"list":"listAItem",
"add":"listViewIt"
},
initialize:function(){
console.log("initiated....."); //works properly
},
defaultView:function(){
new appViews(); // not working..
},
listAItem:function(){
console.log("from listAItem");
// on click and using back button woks fine
},
listViewIt:function(){
new listView();
// on click and using back button woks fine
}
});
Backbone.history.start(); // removed and updated to where i call my app...
return appRouter;
})
Now i updated my Backbone.history to call after initiating my app.. now my initial method (defual view called) but still while i use my back / font buttons in my browser not working.. but reset are works fine..
how to fix it..
here is my updated code:
$.when.apply(null,requests).done(function(){
var app = new router();
//on refresh works, but not working while use back / front button of browser use.
Backbone.history.start();
});
It works...
It was my mistake to other view to set the initial element removed. now i realized that. now works fine. thank for all..
update code :
this.$el.find("ul").html("<h1>New html has to come here</h1>");
it was been wrongly as:
this.$el.html("<h1>New html has to come here</h1>");

Meteor using backbone for routing always loading one route

I'd appreciate any insight into whether this is a "correct" way of doing things and also what's causing the error I'm seeing.
I have added backbone to my base meteor install meteor add backbone
Then I set up a router.js file as follows (just showing 2 pages as example);
var Router = Backbone.Router.extend({
routes: {
"": "index",
"help": "help",
...
},
index: function() {
Session.set('currentPage', 'homePage');
},
login: function() {
Session.set('currentPage', 'loginPage');
},
...
Then for the pages I have html files with templates looking something like this...
<template name="homepage">
{{#if route}}
You're at the Home Page!
{{/if}}
</template>
Then for the main page I have an html file that contains the following;
<body>
...
{{> homepage}}
{{> loginpage}}
{{> helppage}}
...
</body>
This works for all of the pages except the one designated 'homepage', this template is always rendered regardless of where I am on the site. e.g. myapp/ as the root page just displays the homepage template, but myapp/loginpage displays the loginpage template and the homepage template. So every single page displays the contest of the homepage template.
Any insight? (or better ways to structure).
Thank you
Finally a question that I can answer because it's what I've been doing 60 hours a week at work for the last few months :-P
You're doing a few things wrong here, and they're extremely simple fixes that will get you fired up in no time.
First thing
You need to instantiate all of your routers and then initialize Backbone.pushState().
// This is just how I have been doing it, there are many correct ways.
var LoadedRouters = {
Module1 : new Module1Router(),
Module2 : new Module2Router(),
...
};
// After all routes instantiated, we tell Backbone to start listening.
Backbone.history.start({ pushState : true, root : "/" });
It's very important that you set the root property correctly, or else weird things start happening.
Second thing
You HAVE TO list your routers from the most-specific to the least-specific, top-down. The URL structure you outlined above will ALWAYS match the first route and trigger it.
routes : {
"help/phones/newservice" : HandleNewServiceHelp(),
"help/phones/oldservice" : HandleOldServiceHelp(),
"help/phones(/:any)" : HandleAllPhoneHelp(),
"help(/:any)" : HandleAllHelp(),
"" : HandleAllUnmatchedRoutes()
};
Backbone.router can be a tricky thing to learn.

Trigger and backbone router with file://

I'm working with trigger and backbone, and am trying to programmatically navigate to a url. This is all happening using the file:// protocol, as everything in running inside trigger io only.
This manual navigate though doesn't trigger the function associated with the route.
My router looks like this
var BARouter = Backbone.Router.extend({
routes: {
"users/sign_in": "userSignin",
"users/sign_up": "userSignup",
"": "catchAll"
},
userSignin: function(){
},
userSignup: function(){
forge.logging.info("in user signup----");
},
catchAll: function(){
}
});
var app_router = new BARouter();
BA.router = app_router;
Backbone.history.start({pushState: true});
and I'm manually navigating
BA.router.navigate(navigate_to("users/sign_up"), {trigger:true});
The navigate_to method just returns the full url in the form "file://users/sign_up".
But nothing is logged to the console, and the execution flows normally. Am I missing something here ?
Using pushState with file urls probably doesn't make sense, I'm also not sure why you need the navigate_to function.
Try setting pushState to false and navigate using the string of the route, i.e.:
BA.router.navigate("users/sign_up", {trigger:true});

Resources