A way to organize your javascript code
I while back I got tired of having to include bootstrapping code on all my html (aspx) pages; you know, including some sort of $(document).ready(…) to bind the correct set of handlers for the page, so I came up with a basic routing/bootstrapper to handle this in a more generic way.
The basic idea is to have an object that takes care of calling the correct method on the correct object to initialize the bindings (and anything else that needs to happen on page load) based on the URL of the page, a routing engine :).
Let’s say I’m using ASP.Net MVC, then I could organize my code so that I have one js object per controller and inside it a function for each of the actions to do the actual bootstrapping:
The controller object
1: (function () {
2: myApplication.controllers.home = function() {
3: /* controller initialization */
4: this.init = function() {
5: alert('Hello from the controller.init function for the Home controller');
6: }
7: /* index action init function */
8: this.index = function() {
9: alert('hello from index action in the home controller');
10: }
11: }
12: })();
Now when the user hits my “/Home/Index” URL, I need to call the appropriate methods on the controller object, and here it is:
The basic routing object
1: /*---------------------------------------------------------*/
2: /* The routing strategy */
3: (function (app, $) {
4: var st = {
5: strategies: {
6: mvc: {
7: defaultController: 'home',
8: defaultAction: 'index',
9: controller: function controllerFromUrl() {
10: // match urls in the form ../controllerName/actionName or ../controllerName
11: var ctl = document.location.href.match(/http[s]?:\/\/[\w\:\.\-\d]+\/([\w\d\-]+)[\/|$]/i);
12: return ctl ? ctl[1].toLowerCase() : this.defaultController;
13: },
14: action: function actionFromUrl() {
15: // match urls in the form ../controllerName/actionName or ../controllerName
16: var act = document.location.href.match(/http[s]?:\/\/[\w\:\.\-\d]+\/[\w\d\-]+\/([\w\d\-]+)/i);
17: return act ? act[1].toLowerCase() : this.defaultAction;
18: }
19: }
20: }
21: };
22:
23: window.myApplication = $.extend(app, st);
24: })(window.myApplication || {}, jQuery);
25:
26:
27:
28: /*--------------------------------------------*/
29: /* The basic routing object */
30:
31: (function (app, $) {
32: var opts = {
33: routingStrategy: app.strategies.mvc
34: };
35:
36: var theApp = {
37: activeController: undefined,
38: controllers: {},
39: run: function () {
40: this.initController();
41: this.initAction();
42: },
43: initController: function () {
44: var activeControllerName = opts.routingStrategy.controller(); /*getCurrentControllerName*/
45: if (activeControllerName && this.controllers[activeControllerName]) {
46: this.activeController = new this.controllers[activeControllerName]();
47: this.activeController.init();
48: }
49: },
50: initAction: function () {
51: var activeActionName = opts.routingStrategy.action(); /*getCurrentActionName*/
52: if (activeActionName && this.activeController && this.activeController[activeActionName]) {
53: this.activeController[activeActionName]();
54: }
55: }
56: };
57:
58: /* pusblish the routing object */
59: window.myApplication = $.extend(app, theApp);
60:
61: })(window.myApplication || {}, jQuery);
62:
63:
64: $(function () {
65: myApplication.run();
66: });
The way this works is by keeping a list of controllers and match the current URL’s controller name to the controller object and call the init() function on it, then match the action part of the URL a function inside the controller object and, if it exists, call it.
I’ve separated the part that decides the name of the controller and the action to a different object (routing strategy) so you could create a different strategy to decide the controller and action (ie. if you are not using MVC, you could use the name of the aspx page as the controller and something else for the action).
The advantage of this approach is that I could have one file for each of my js controllers neatly organized and then, during my build process combine all of them into one and minimize it.
After using this approach for a while, someone pointed out to me that this is a similar approach to Paul Irish's unobstructive DOM-ready execution which uses the body id and class attributes as the basis for selecting the what to initialize, it’s also a very good way to handle the same problem, take a look at it also.
So have fun with it!
Source code on GitHub