my router not working with back / forward click - backbone.js

My router looks like this:
define(function(require) {
var _ = require('underscore'),
Backbone = require('backbone'),
homeStageView,
homeSidebarView,
yxyStageView;
return Backbone.Router.extend({
routes: {
'/web/:route' : 'viewLoader'
},
initialize: function() {
//this._bindRoutes();
$('.link').click(function(e) {
e.preventDefault();
Backbone.history.navigate($(this).attr('href'), true);
});
},
viewLoader: function(route) {
switch(route) {
case 'home':
this.homeHandler();
break;
case 'yxy':
this.yxyHandler();
break;
}
},
// navigation handlers
homeHandler: function() {
if ( !homeStageView ) {
require(['views/home-stage-view'], function(HomeStageView) {
homeStageView = new HomeStageView();
homeStageView.render();
});
}
else {
homeStageView.render();
}
this.renderHomeSidebarView();
},
yxyHandler: function() {
if ( !yxyStageView ) {
require(['views/yxy-stage-view'], function(YxyStageView) {
yxyStageView = new YxyStageView();
yxyStageView.render();
});
}
else {
yxyStageView.render();
}
this.renderHomeSidebarView();
},
});
});
and in my main view I init the router like this:
appRouter = new AppRouter();
if ( history && history.pushState ) {
Backbone.history.start({pushState: true});
console.log('has pushState');
}
else {
Backbone.history.start();
console.log('no pushState');
}
This works fine for loading all my views but it works not when user clicks the back forward button.
The url in the address bar chances accordingly but that's it. Obviously, I'm missing something.
Could someone please help out?

From the fine manual:
extend Backbone.Router.extend(properties, [classProperties])
[...] Note that you'll want to avoid using a leading slash in your route definitions:
Your route is /web/:route so try removing the leading slash:
routes: {
'web/:route': 'viewLoader'
}

Related

Execute an Angular function only if the current tab is active in the browser

I have an angular js function which should be called for every 2 seconds only when the current tab is open in the browser. Is there any way to check whether the current page is active in the browser.
$scope.callAtInterval = function() {
var url = "http://127.0.0.1/test";
$http.get(url).success( function(response) {
$scope.initial = response;
},
function errorCallback(response) {
$scope.result=response;
});
}
$interval( function(){ $scope.callAtInterval(); }, 5000);
}
I think below piece of code is self-explanatory
import { HostListener} from "#angular/core";
#HostListener("window:visibilitychange", ["$event"])
onVisibilityChange($event) {
const isVisible = $event.target.visibilityState === 'visible';
this.logger.info(isVisible);
if (isVisible) {
// tab is visible
} else {
// tab is not-visible
}
}
You would use the focus and blur events of the window:
$(window).on("blur focus", function(e) {
var prevType = $(this).data("prevType");
if (prevType != e.type) { // reduce double fire issues
switch (e.type) {
case "blur":
// cancel your interval function
break;
case "focus":
// emit your interval function
break;
}
}
$(this).data("prevType", e.type);
})

How to fix the Back Button Behavior (should be clicked Twice) in backnone?

I am working on a backbone application with multiple views. The navigation between the view is done by calling router.navigate('view', {trigger:true, replace:false}). I have a login page that has it own structure and the other views has another structure (very different). When I click on the login button the view changes along with the hash. The problem is when I click on the back button of the browser it doesn't go back to the login page but load the home page again, then I am required to click again on the back button in order to go back to the login page. In addition, when navigating to other pages sometimes the view handler isn't called when I click on the back button. I always use the router.navigate('view', {trigger:true, replace:false}) function to navigate between the views.
Here is my Router :
define([ "jquery", "backbone", 'views/header', 'views/sidePanel' ], function($,
Backbone, Header, sidePanel) {
var ApplicationRouter = Backbone.Router.extend({
_header : null,
_sidePanel : null,
routes : {
"" : "login",
"home" : "home",
"perspectives" : "perspectives"
},
initialize : function() {
this.firstPage = true;
Backbone.history.start();
},
login : function() {
var self = this;
require([ 'views/loginPageView' ], function(loginView) {
self.changePage(new loginView(), true);
});
},
home : function() {
var self = this;
require([ 'views/homePageView' ], function(homeView) {
self.changePage(new homeView(), false);
});
},
perspectives : function() {
var self = this;
require([ 'views/treePerspectivesView' ],
function(perspectivesView) {
self.changePage(new perspectivesView(), false);
});
},
changePage : function(page, noPanel) {
var deferred = $.Deferred();
console.log(page);
$page = $(page.el);
if (this.firstPage) {
if (!noPanel) {
self._sidePanel = new sidePanel({
el : ".left_col"
});
self._header = new Header({
el : '.top_nav'
})
$page.attr('class', 'right_col');
$page.attr('role', 'main');
$('.main_container').append($page);
} else {
$('body').append($page);
}
page.render();
} else {
if (!noPanel) {
$('.right_col').remove();
$('.right_col').unbind();
$page.attr('class', 'right_col');
$page.attr('role', 'main');
$('.main_container').append($page);
}else{
$('body').html($page);
}
page.render();
}
if (this.firstPage) {
this.firstPage = false;
}
deferred.resolve();
return deferred;
}
});
return ApplicationRouter;
})
This is how I navigate to the home view :
login:function(){
console.log("Login Clicked");
this.remove();
this.unbind();
router.fromBack=true;
router.navigate('home', {trigger: false, replace: false});
//router.home();
},
Is there a better way to navigate between the views to fix this problem (Maybe calling the changePage function with the has as a parameter) ?
How t fix the issue of the Back Button ?
Thank You.
I'd recommend you to use the count navigation hits for a router. Also you should create a back route for this job. Like this:
AppRouter.prototype.initialize = function() {
this.routesHits = 0;
Backbone.history.on('route', (function() {
this.routesHit++;
}), this);
};
AppRouter.prototype.back = function() {
if (this.routesHits > 1) {
this.routesHits = this.routesHits - 2;
window.history.back();
} else {
if (Backbone.history.getFragment() !== '/app') {
this.routesHits = 0;
}
this.navigate('/', {
trigger: true,
replace: true
});
}
};
Your changePage function:
// ...
changePage : function(page, noPanel) {
this.routesHit++;
// ...
}
And for using go back functionality you can use your back function:
appRouter.back()
For using this approach you should use a singleton object of the AppRouter.

Backbone Modal with Q.promise issue

We have a method (onOpenNotitiesClicked) for showing a Modal view for entering notes. We have implemented the Backbone Modal plugin for this (https://github.com/awkward/backbone.modal).
There are two situations:
There are not yet notes in the backend: initialize and render the
modal
There are already notes in the backend => first collect them and
then pass the notes to the modal (initialize) and then render
In the first situation it works fine! The modal is shown.
In the second situation, the modal is not shown.
I have debugged both situations and in both situations, alle methods are executed and in the elements, I see the HTML of the modal view.
I suspect this looses some data during the Q/promise data get, but I can't see what/where/how/why....
Anyone any idea what I am doing wrong? Or what I am missing?
The creation of the modal:
onOpenNotitieClicked: function (event) {
var $element, taak, taakId, id, options = {};
$element = this.$(event.currentTarget).closest("li");
id = $element.data("id");
taakId = $element.data("taak-id");
taak = this.getCurrentTask(event);
options.taakKey = id;
options.taakId = taakId;
options.heeftNotities = taak.heeftNotities;
options.datacontroller = this.datacontroller;
this.notitiesOptions = options;
// this.renderNotitieModal(options);
if (taak.heeftNotities) {
this.getActiviteitNotities(taakId).then(_.bind(this.onSuccessGetNotities, this), _.bind(this.onErrorGetNotities, this));
} else {
this.renderNotitieModal(this.notitiesOptions);
}
},
In case there are notes to be collected:
getActiviteitNotities: function (taakId) {
return this.datacontroller.getNotities(taakId);
},
onSuccessGetNotities: function (notities) {
this.notitiesOptions.notities = notities;
this.renderNotitieModal(this.notitiesOptions);
},
onErrorGetNotities: function () {
this.renderNotitieModal(this.notitiesOptions);
},
To get the notes from the backend, Q/promises is used.
getNotities: function (taakId, refresh, klantorderId) {
return Q.promise(function (resolve, reject) {
var options = {};
if (!this.notitiesCollection || this.taakId !== taakId || refresh) {
delete this.notitiesCollection;
this.notitiesCollection = this.createCollection("NotitiesCollection", {
id: this.taakId,
resource: this.NOTITIES_RESOURCE
});
if (taakId) {
this.taakId = taakId;
options = {
data: {
parentId: this.taakId
}
};
} else if (klantorderId) {
options = {
data: {
klantorderId: klantorderId
}
};
}
resolve(this.notitiesCollection.fetch(options));
} else if (this.notitiesCollection) {
resolve(this.notitiesCollection.toJSON());
} else {
reject("ERROR");
}
}.bind(this));
},
Notities.js (the modal view):
(function () {
"use strict";
App.Bewaking.modals.BewakingNotitieModal = Backbone.Modal.extend({
template: JST.bewaking_notitie_modal, //jshint ignore:line
title: "Notities",
formatter: new App.Main.helpers.Formatter(),
events: {
"click #save-notitie": "onSaveNotitieClicked"
},
initialize: function (options) {
this.taakId = options.taakId;
this.taakKey = options.taakKey;
this.datacontroller = options.datacontroller;
this.notities = options.notities;
},
afterRender: function () {
console.log("afterRender");
this.$notitieModal = this.$("#notitieModal");
this.$nieuweNotitie = this.$("#nieuwe-notitie");
this.$notitieErrorTekst = this.$("#notitie-error-tekst");
this.$notitieModal.on("shown.bs.modal", function () {
this.$nieuweNotitie.focus();
}.bind(this));
},
render: function () {
console.log(this.notities);
this.$el.html(this.template({
formatter: this.formatter,
notities: this.notities
}));
return this;
}
});
}());

What is the proper way to allow for "subdirectories" in Backbone Marionette routers?

Here is my current router:
CRM.Router = Marionette.AppRouter.extend({
appRoutes: {
"customers" : "listCustomers",
"customers/:id" : "showCustomer",
"customers/add" : "newCustomer",
"customer/search" : "showCustomerSearch"
}
});
CRM.navigate = function (route, options) {
options || (options = {});
Backbone.history.navigate(route, options);
}
CRM.getCurrentRoute = function () {
return Backbone.history.fragment;
}
CRM.addInitializer(function () {
var router = new CRMApp.Router({
controller: API
});
});
CRM.on("initialize:after", function () {
if (Backbone.history) {
Backbone.history.start({ pushState: true, root: '/app/' });
if (this.getCurrentRoute() === "") {
CRM.trigger("customers:list");
}
}
});
Going to customers works wonderfully, but going to customers/add seems to want to load the customers content. Not sure why. Is there a different way I should be handling customers to allow for subsections?
Suggestions?
Just reordered the routes and it worked:
CRM.Router = Marionette.AppRouter.extend({
appRoutes: {
"customers" : "listCustomers",
"customers/add" : "newCustomer",
"customers/:id" : "showCustomer",
"customer/search" : "showCustomerSearch"
}
});

How can I run a "middleware" function before a route method is executed?

Say I have a backbone router like:
routes:
"" : "homepage"
"catalog/:id" : "catalogPage"
"catalog/:id/products/:id2" : "productPage"
homepage : -> doStuff()
catalogPage: (id) -> doOtherStuff()
productPage: (id, id2) -> doEvenMoreStuff()
and a function:
executeBefore = -> console.log("hello")
If I want executeBefore to be called and executed each time a route is called and before the corresponding route method, is there a simple way to do it apart from inserting a call to executeBefore at the beginning of every route method ?
You can override the route function in your router class to intercept the route calls :
var Router = Backbone.Router.extend({
routes: {
"" : "homepage",
"catalog/:id" : "catalogPage"
},
route: function(route, name, callback) {
var router = this;
if (!callback) callback = this[name];
var f = function() {
console.log('route before', route);
callback.apply(router, arguments);
console.log('route after', route);
};
return Backbone.Router.prototype.route.call(this, route, name, f);
},
homepage: function() {
console.log("homepage");
},
catalogPage: function(id) {
console.log("catalogPage "+id);
}
});
var r = new Router();
Backbone.history.start();
And a demo http://jsfiddle.net/nikoshr/EdLzh/
Since Feb, 13, 2014, you can use router.execute(callback, args, name) (http://backbonejs.org/#Router-execute) for this.
So your interceptor will be looked something like this
routes: {
'': 'homepage',
'catalog/:id': 'catalogPage',
'catalog/:id/products/:id2': 'productPage'
},
execute: function(callback, args, name) {
// Do your stuff here
executeBefore();
if (callback) callback.apply(this, args);
}
https://github.com/jashkenas/backbone/commit/791033dc1aab53e9e5c9366f64a854224f851231
you can also use backbone.routefilter plugin.
you'll be able to set a filter for all routes
var Router = Backbone.Router.extend({
routes: {
"": "index",
"page/:id": "page"
},
before: function( route, params ) { ... },
after: function( route, params ) { ... },
index: function(){ ... },
page: function( route ){ ... }
});
Or select some routes
var Router = Backbone.Router.extend({
routes: {
"": "index",
"page/:id": "page"
},
before: {
"": function( route ) { ... },
"page/:id": function( route ) { ... }
},
after: function( route ) { ... },
index: function(){ ... },
page: function( route ){ ... }
});
You can play with the Events :
Backbone.history.on('route', function () {
// Do your stuff here.
executeBefore();
});
visiting #help/uploading will fire a route:help event from the router.
Apparently, this would be triggered before the redirection :
https://github.com/documentcloud/backbone/blob/master/backbone.js#L1243
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
callback && callback.apply(router, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
});

Resources