Your First JavaScript App – IV

Read the first part of this series.

In the previous post, we had initialised the contacts manager module, but we haven’t created it yet. We will create one now. We already know that we should add one init function to contactsManager object and this object should be available on the window object.

We will create an object called contactsManager and add it to global scope, i.e. window.

// scripts/modules/contacts/contacts.js
var contactsManager = {
  init: function () {
    console.log ('Contacts Manager Initialised.');
  }
};

window.contactsManager = contactsManager;

You can get rid of the console.log statement later. If you open your index.html in browser, and if you look at the console, you’ll see this message printed there.

We will return to this file and update it, because we don’t have anything (model, view, and, controller) to use. In the next bit, we will just create the skeleton for all the other files, and return to complete the initialisation.

// scripts/modules/contacts/contactsModel.js
var ContactsModel = (function () {
  function CModel () {}
  return CModel;
}) ();
// scripts/modules/contacts/contactsView.js
var ContactsView = (function () {
  function CView () {}
  return CView;
}) ();
// scripts/modules/contacts/contactsController.js
var ContactsController = (function () {
  function CCtrl (model, view) {}
  return CCtrl;
}) ();

These three files are almost the same, except the controller has model and view parameters in constructor functions.

Why this structure?

This is one way of creating a module, you may find a different one in a different place.

We are using IIFEs to create our constructor functions and assign it to a variable. For example, ContactsView gets the CView which is returned by the IIFE. Note that CView is also a function, and we will use it to create an instance/object of CView.

Though in later parts you will see and understand why we need this extra layer for creating a constructor function inside IIFE, instead of writing something like this,

// not using it, read above paragraph.
var ContactsView = function () {}

The reason is, we want to hide the implementation from the global scope. Now this ContactsView is in global scope and, we need it to be in global scope. But what about some function, let’s say, renderMessage for example. If this function is also in global scope, anyone can execute it from console. You must have read in past that we create scopes using function and outer scope cannot scope inner scope’s data.

Compare following codes:

// method 1
// begin

var ContactsView = function () {}
function renderMessage () {}

// end

In the above code, both ContactsView and renderMessage is global. Now check this,

// method 2
// begin

var ContactsView = (function () {
  function CView () {}
  function renderMessage () {}
  return CView;
})();
// end

While in this code, ContactsView which is global, has the value and access of CView. renderMessage is hidden from global scope.

Initialise Module

Now that we have mode, view, and, controller available. Let’s complete the initialisation.

// scripts/modules/contacts/contacts.js
var contactsManager = {
  init: function () {
    new ContactsController (new ContactsModel (), ContactsView ());
  }
};

window.contactsManager = contactsManager;

Alternatively, we could do this, but we really don’t need a reference for created model and view.

// Not using this
var contactsManager = {
  init: function () {
    var model = new ContactsModel ();
    var view = new ContactsView ();
    var ctrl = new ContactsController (model, view);
  }
};

window.contactsManager = contactsManager;

Note: You may try and print some message in all the constructor functions to see if all of them got initialised.

Creating a simple MVC example

Before moving forward, let’s create a small example, through which we will try to use the MVC structure. This example can be a base for all the other parts and functionality of the application.

We already know that the model is responsible for providing data, the view is for rendering that data on a webpage and the controller is the one which controls this flow. So let’s go ahead and code this.

Model

function CModel () {
    this.message = 'Hello from Model!'
}

In the constructor function of the model, we have created and added a property model to this. So whenever we will create an object of CModel, a property message will be added to the object.

View

CView.prototype.render = function (message, parentNode) {
    var div = document.createElement ('div'),
        text = document.createTextNode (message);
      
    div.appendChild (text);
    parentNode.appendChild (div);
}

In general everything that can be shared, gets added to prototype. We do not want to create multiple instances of render function and allocate memory for it. The idea of creating a function is to re-use. So every object of  CView will share the same function instance.

Controller

function CCtrl (model, view) {
    this.model = model;
    this.view = view;

    var rootNode = document.getElementById ('root');
    this.view.render (this.model.message, rootNode);
}

During initialisation, we had passed an object of the model and an object of the view to the constructor function of the controller. We have used them and assigned to this.model and this.view. Since this.view is an object of CView, it contains the render function that we defined in the view layer. To this function we are passing the message from model and the root node which we had created in the HTML page.

Here is the summary of what we did until now,

  • create a model, add some data.
  • create a view, add a render function.
  • create a controller by passing an instance of the model and view.
  • initialise the module by creating the controller in the initialiser [entry point function].

In the next post, we will discard this example and start building our application.