Custom Router for Firebase jQuery App

The Routing is showing different information in the single webpage with the help of URL or URL hash value. Let's build a a custom router with jQuery and integrate it in Firebase Project

custom-router-for-firebase-jquery-app


The Routing is showing different information in the single webpage with the help of URL or URL hash value.

In this post, we are going to do a custom router for Firebase jQuery App that we had started in earlier post: Auth Schemes in Google Firebase.

In Front End, generally based on browser support we can leverage URL hash in almost all cases and full URLs in latest browsers.

So for now, to cover “almost all cases”, we will use the URL hashes for our router.

For URL hashes, the primary thing is window.location.hash and we would look for its change do changes in our application.

For this, browsers have the HashChangeEvent which allows to watch the changes in hash changes. You can read more about the event and it’s support in various browsers over here: https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent.

It can be be bound in following three ways as:

window.onhashchange = function(e) {
  //handle hash change console.log(e)
}

or

window.addEventListener("hashchange", function(e){
  //handle hash change console.log(e);
}, false);

And we will be using the addEventListner method on window object to bind our listeners for HashChangeEvent.

Moving further, we would need a place on the page where our router can put the child pages as per the active route.


Basic Router

So we will create a mount point in our page where our router will fetch and replace the contents. To fetch the child page, we are going to use the jQuery’s get() function as we already have jQuery in our page. But you can any other library like qwest, fetch or axios can be used to do the AJAX to make it stand alone router.

Let’s take #root is out mount point and render is the name of the function which will do the fetch and replace of child pages. The child pages are in the partials directory parallel to our js directory and are named same as the hash with .html as an extension. Following is the code for that:

var mountPoint = '#root';
window.addEventListener('hashchange', function (e) {
  console.log(e);
  render(window.location.hash.replace(/[#]/, ''));
}, false);

var render = function(hashValue){
  $.get('partials/'+hashValue+'.html', function(data){
    $(mountPoint).empty().html(data);
  })
}

Controller Function for setup

The above code will work fine if we have data for view purpose only, but what if we want to have interactive child pages like forms. For such cases, the child pages will have their own event bindings that will need to be enforced when any child page is loaded.

So for that case, let’s make a binder function which will bind event after the fetch and replace has been done. And call that binder in render function. So render function will now look like:

var render = function(hashValue){
  $.get('partials/'+hashValue+'.html', function(data){
    $(mountPoint).empty().html(data);
    registerFormBindings();
  })
}

var registerFormBindings = function() {
  $('#submit').click(function(e){
    //validations
    $(form, mountPoint).validate();
  });
}

Controllers for States

Now our code is ready for a state/hash change. But it will do same registerFormBindings on all states. But child pages will obviously be different. So let’s assume that there are login, register, add and list states. So we can have an object; say controllers; which will have keys as state names and the value will hold the bindings function. This will look like as follows:

var render = function(hashValue){
  $.get('partials/'+hashValue+'.html', function(data){
    $(mountPoint).empty().html(data);
    controllers[hashValue]();
  })
}

var controllers = {
  login: function(){ /* ... */ },
  register: function() {
    $('#submit').click(function(e){
      //validations
      $(form, mountPoint).validate();
    });
  },
  add: function(){ /* ... */ },
  list: function(){ /* ... */ },
}

Looks good so far. But we can optimize the path of template and controller function for a state in following way:

var render = function(hashValue){
  $.get( routes[hashValue].path, function(data){
    $(mountPoint).empty().html(data);
    routes[hashValue].controller();
  })
}

var routes = {
  login: {
    path: 'partials/login.html',
    controller: function(){ /* ... */ },
  },
  register: {
    path: 'partials/register.html',
    controller: function() {
      $('#submit').click(function(e){
        //validations
        $(form, mountPoint).validate();
      });
    },
  },
  add: {
    path: 'partials/add.html',
    controller: function(){ /* ... */ },
  },
  list: {
    path: 'partials/list.html',
    controller: function(){ /* ... */ },
  },
}

After putting all of the above code together and giving it a form of an exportable module, the script can be crafted as follows:

See full code on https://raw.githubusercontent.com/time2hack/movie-db/master/src/js/router.js 

Final Code

And the above code can be imported and used as follows in the required application.

var $ = require('jquery');
var Router = require('./router');

var appRouter = new Router({
  mountPoint: '#root',
  indexRoute: 'index',
  routes: {
    login : {
      path: 'login',
      template: 'partials/login.html',
      controller: function() {
        console.log('login controller function loaded');
      }
    },
    index : {
      path: 'index',
      template: 'partials/index.html',
      controller: function() {
        console.log('index controller function loaded');
      }
    },
    add : {
      path: 'add',
      template: 'partials/add.html',
      controller: function() {
        console.log('add controller function loaded');
      }
    }
  }
});

$(document).ready(function() {
  appRouter.listen();
});

We will be using the same for movie-db firebase app.


Let me know through comments ? or on Twitter at @heypankaj_ and/or @time2hack

If you find this article helpful, please share it with others ?

Subscribe to the blog to receive new posts right to your inbox.