The JavaScript Module Pattern used with jQuery


CaseStudyImage

I have always been primarily a backend developer, I love OOP, and try my best to follow all the best principles such as Encapsulation, Polymorphism, Separation of Concerns, and even the Law of Demeter when I design and write software. As such, I have fought tooth and nail to avoid writing in-browser apps. I have nothing against them, I believe that’s where the View needs to be... philosophically. I just want someone else to do it, to deal with the JavaScript and CSS because it’s so hard to disciple ourselves to writing good, clean code. OOP code in the browser with JavaScript ES5 isn't difficult to write correctly, it’s just easy not to. (in future articles I’ll discuss how I’ve overcome this with Angular 2, Typescript, and even ES6 features)

Here we introduce the Module Pattern, this gives us a way in JavaScript to introduce private variables and functions, exposing only those parts we need to the outside world. There are several flavors of this available, you can implement it as a JavaScript object, you can use prototypes, or you can write it as an IIFE a JavaScript Immediately Invoked Function Expression. To do this we implement a JavaScript Closure. More about closures here.
Here’s the pattern.

   1:  /// JavaScript source code representing example jQuery aware module pattern
   2:  /// Solution Zero, Inc. Lubbock Texas 
   3:  /// Troy Locke -- troy@slnzero.com
   4:  var myMessageApp = (function() {
   5:      "use strict"
   6:   
   7:      // I avoid these with the bindControls functionality but I show if for example.
   8:      var someElement = $("#foo"); // some element I know I'll use lots
   9:   
  10:      // private variables
  11:      var pvtMessageVal;
  12:      var pvtAdditionalMessageVal;
  13:      
  14:      // we create an object to hold all the jQuery controls, so we can call
  15:      // binding after loading an HTML page dynamically via AJAX
  16:      // see bindControls further down
  17:      var messageCtrls = {};
  18:   
  19:      var config = {
  20:          // *example, this must be passed into init(config)
  21:          fooSelector: null, // $("#foo")
  22:          messageSelector: null, // $(".message")
  23:          additionalMessageSelector: null, // $(".additional_message")
  24:          options: {
  25:              showOK: true,
  26:              showCancel: true,
  27:              warningLevel: 1,
  28:          }
  29:      }
  30:   
  31:      // AJAX calls
  32:      var getMessage = function(message) {
  33:          $.ajax({
  34:              url: '/getMessagePage',
  35:              type: 'POST',
  36:              dataType: "json",
  37:              data: {'message' : message},
  38:              success: function(data) {
  39:                  // ...
  40:                  messageCtrls.mainMessageDiv.html(data.message);
  41:                  // call bind controls to bind to the newly introducted dom elements
  42:                  messageCtrls = bindMessageControls();
  43:                  },
  44:              error: function() {
  45:                  // ...
  46:                  }
  47:          });
  48:      };
  49:   
  50:      var inputClick = function(event) {
  51:          event.preventDefault();
  52:          // depending on if you'll reuse these selectors throughout the app I might have these as variables
  53:          $('.loading').html('<img class="remove_loading" src="/graphics/loading.gif" />');
  54:   
  55:          // try to avoid these
  56:          var msg = $(".additionalMessage").val();
  57:          // and use this 
  58:          var msg = config.additonalMessageSelector.val();
  59:          // or
  60:          var msg = pvtAdditionalMessageVal;
  61:   
  62:          if (msg == ""){
  63:              $("#message_empty").jmNotify();
  64:              $('.remove_loading').remove();
  65:          } else {
  66:              getMessage(msg);
  67:          }
  68:      };
  69:   
  70:      var bindMessageControls = function () {
  71:          var self = {};
  72:   
  73:          // Modal
  74:          self.thisModal = $(".MessageModal");
  75:   
  76:          // CheckBoxs
  77:          self.fooCb = $(".foo_checkbox");
  78:          
  79:          // Buttons
  80:          self.okBtn = $(".btnOk");
  81:          self.cancelBtn = $(".btnCancel");
  82:   
  83:          // Divs
  84:          self.mainMessageDiv = $(".main_message");
  85:          self.additionalMessageDiv = $(".addtional_message");
  86:   
  87:          //Help Icons
  88:          self.HelpIcon = $(".help-icon");
  89:   
  90:          return self;
  91:      };
  92:   
  93:      var bindVals = function () {
  94:          //check to make sure we have a valid config passed in before we set the values
  95:          if (!config.messageSelector) throw "Invalid configuration object passed in init()";
  96:          
  97:          //bind the values to "private variables"
  98:          pvtMessageVal = config.messageSelector.val();
  99:          
 100:          //this control is optional, test existence
 101:          if(config.additionalMessageSelector.length)
 102:              pvtAdditionalMessageVal = config.additionalMessageSelector.val();
 103:      };
 104:   
 105:      var bindFunctions = function() {
 106:          // you can use jQuery
 107:          $("btnOk").on("click", inputClick)
 108:          // but we have the controls object to use, so instead
 109:          messageCtrls.okBtn.on('click, inputClick')
 110:      };
 111:   
 112:      var init = function () {
 113:          messageCtrls = bindMessageControls();
 114:          bindFunctions();
 115:      };
 116:   
 117:      var showMessage = function (cfg) {
 118:          config = cfg;
 119:          bindVals();
 120:          messageCtrls.thisModal.modal({
 121:              show: true,
 122:              keyboard: false,
 123:              backdrop: "static"
 124:          });
 125:      };
 126:      
 127:      return {
 128:          init: init,
 129:          show: showMessage,
 130:          getMessage: getMessage
 131:          //anything else you want available
 132:          //through myMessageApp.function()
 133:          //or expose variables here too
 134:      };
 135:   
 136:  })();
 137:   
 138:  //usage
 139:  $("document").ready(function () {
 140:      myMessageApp.init();
 141:  });

Enjoy the example, and remember, it’s just an example as each case may call for something a little different. For example, I’ve separated Init() and showMessage() functionality which in many cases can be combined.

This is the first in a series that will explore the Module Pattern in JavaScript. In the next part I will break down the code in this example and explain in detail the whats and whys. Then I hope to show examples of other implementation of the this pattern using objects, prototypes, and other variations such as the Revealing Module Pattern.

I welcome any question and comments.


Back