Javascript Model-View-Controller example

21 Comments

When I first got into Javascript, it wasn’t obvious to me how best to use the language to do a simple model-view-controller.

I’ve heard of backbone, spine, and some other javascript mvc frameworks, but I just wanted to start with something really simple. My friend Jonas came up with a good example showing how our thinking has evolved after building some stuff.

Option 1: Prototype

In the prototype method you define your functions using the javascript prototype construct and then make use of ‘this’ and ‘self’ to make sure you are calling your methods with the right javascript context.

This is how we first stared doing simple mvc.

FormModel_literal.js

var FormModel = function(){};

FormModel.prototype.getInputText = function(){
   return $('#inputtext').val();
};

FormModel.prototype.setInputText = function(value){
   $('#inputtext').val(value);
};

FormController_prototype.js

var FormController = function(pModel){

    this.model = pModel || new FormModel();
    
    this.fill_clicked = function(){
        this.model.setInputText('Hello World');
    };

    this.clear_clicked = function(){
        this.model.setInputText('');
    }
};

FormController.prototype.init = function(){

    var self = this;

    $('#fillbutton').click(function(){ self.fill_clicked(); });
    $('#clearbutton').click(function(){ self.clear_clicked(); });
};

FormController.prototype.getModel = function(){
    return this.model;
};

It’s nice. It works. You don’t need to worry about state (it’s all in the model). But you do need to watch context (this, that, and self) and it’s easy to get wrong if you aren’t careful.

Option 2: Literal method

In the literal method, you use the javascript literal construct to keep most internal methods private, and then only expose those you want publicly consumed in the return statement.

FormModel_literal.js

var FormModel = function(){

    function getInputText(){
       return $('#inputtext').val();
    }

    function setInputText(value){
       $('#inputtext').val(value);
    }

    return {
        getInputText : getInputText,
        setInputText : setInputText
    }
};

FormController_literal.js

var FormController = function(pModel){

    var model = pModel || new FormModel();

    function fill_clicked(){
        model.setInputText('Hello World');
    }

    function clear_clicked(){
        model.setInputText('');
    }

    function init(){

        $('#fillbutton').click(function(){ fill_clicked(); });
        $('#clearbutton').click(function(){ clear_clicked(); });
    }

    return {
        init: init,
        model : model
    };
};

I think it’s elegant and nice. And I didn’t even know you could use a return statement like that in Javascript.

The advantage of this method is you don’t have to worry so much about getting the context wrong (this, that and self) as it’s all in the same context.

And it’s just easier on the eyes for me as it more closely resembles the classical OO model I am used to in Java and C#.

You can hook these up in view a test page as follows:

<html>

    <head>
        <script type="text/javascript" src="jquery-1.6.1.min.js"></script>

        <!-- Using function returning an Object literal (my preference) -->
        <script type="text/javascript" src="js/controllers/FormController_literal.js"></script>
        <script type="text/javascript" src="js/models/FormModel_literal.js"></script>

        <!-- Using Prototype -->
        <!--<script type="text/javascript" src="js/controllers/FormController_prototype.js" ></script>-->
        <!--<script type="text/javascript" src="js/models/FormModel_prototype.js"></script>-->

        <script type="text/javascript">

            $(document).ready(function() {
                new FormController().init();
            });

        </script>

    </head>

    <body>
        <input type="text" id="inputtext" name="inputtext" value="" />
        <input type="button" id="fillbutton" value="Fill with Text"/>
        <input type="button" id="clearbutton" value="Clear"/>
    </body>

</html>

So while both methods will work, we are currently leaning towards option 2 (the literal method).

It just seems cleaner, less stuff to worry about, and it looks good. Of course being relatively new to hard core javascript, and still looking for better ways, we reserve the right to change our minds tomorrow.

If you have any tips, better ideas, or feedback, we’d love to hear them.

Update:
Nice write up along similar lines from Ola Bini.

How to fake out an ajax call with jasmine

2 Comments

Here’s an example of how to fake out an ajax call, and then test that some methods get called in jasmine.

    it ("should reset the isDirty flag after doing a save", function() {

        spyOn($, "ajax").andCallFake(function(params) {
              params.success('1');
        });
        spyOn(controller, 'resetIsDirtyFlag');

        controller.saveDraft();

        expect(controller.resetIsDirtyFlag).toHaveBeenCalled();
    });

The spyon($, “ajax”).andCallFake takes a fake function and returns it (along with whatever data you pass it) as the response to your ajax call. In this case I am simulating a successful call to the backend, and returning an id.

Then when you call your object under test (controller.saveDraft()) you can assert that certain things happen.

In this case I want to verify that the resetIsDirtyFlag method got called.

DraftController.prototype.saveDraft = function () {

    that = this;

    $.ajax({
        url: that.saveUrl(),
        type: 'POST',
        data: $("#InvoiceForm").serialize(),
        success: function (data) {
            that.resetIsDirtyFlag();
        },
        error: function (data) {
            that.showError();
        }
    });
};

Jasmine example – selectors and textboxes

2 Comments

Here’s a walk through of a Jasmine example for testing the functionality of the selector arrows show below.

Basically I want to test the jQuery selectors and button functionality. When I click the > button, I want to test that the contents of the ‘Default Trading Group’ box slide over to the ‘Available Trading Groups’ text box.

Step 1: Just get it working.

Before I Jasmine up any of my code, I find it useful just to get the UI working. I am not strong enough in Jasmine or Javascipt yet to TDD stuff like this out. So first pass through, I just hack enough to get the basic controls working, the jQuery selectors setup, and the basic functionality working.

Step 2: Define the model.

With the basic UI going, I was now ready to think about the design.

How did I want this thing to work?

I decided to start with a model (ala MVC). I wanted a model that I could talk to to get the state of any control I wanted on the page. I then wanted this model to be used by a controller (which would actually do this to the page).

Because I already got the page going, my model basically consisted of jQuery selectors to return things off the page. It looked something like this:

function UpdateTradingGroupModel() {
}


UpdateTradingGroupModel.prototype.defaultTradingGroupId = function() {
    return $('#TextDefaultTradingGroup').attr("defaultTradingGroupId");
};

UpdateTradingGroupModel.prototype.defaultTradingGroupName = function() {
    return $('#TextDefaultTradingGroup').val();
};

...

There’s more, but I think you get the gist of it. Anything I want to know the state of, I just talk to my model and it gives it to me.

And then here’s sampling of the controller:

function UpdateTradingGroupController(theModel) {
    this.model = theModel;
}

UpdateTradingGroupController.prototype.configureArrowButtons = function() {

    that = this; // important!

    $('#remove').click(function(){
        $('#SelectedTradingGroups option:selected').appendTo('#AvailableTradingGroups');
    });

    $('#add').click(function(){
        $('#AvailableTradingGroups option:selected').appendTo('#SelectedTradingGroups');
    });

...

    $('#save').click(function(){

        if (that.model.defaultTradingGroupIsEmpty()) {
            that.showWarningDialog();
        } else {
            that.updateTradingGroups(that.model);
        }
        
    });

};

UpdateTradingGroupController.prototype.updateTradingGroups = function(model) {

    params = model.saveParameters();

    $.ajax({
        type: "POST",
        traditional: true,
        url: getControllerAddress('User') + "/@Model.User.Id/UpdateTradingGroups2",
        data:params,
        dataType: 'json',
        success: function(result)
        {
            $('#SuccessMessage').html(result.message);
        }
    });
};

The controller basically sets up the buttons, and actually does the work (like saving any changes done to the page).

And I would add this functionality to the javascript header section of my webpage with something like this:

    var controller = new UpdateTradingGroupController(new UpdateTradingGroupModel());
    controller.configureArrowButtons();

Alright. With that out of the way, we can now look at some Jasmine tests.

Step 3: Create your Jasmine tests.

The first test I wanted to write verified that when a user clicked the > button, the text in the default trading group got removed and added to the available trading group.

That test looked something like this:

describe("UpdateTradingGroupSpec - When clicking the #removeDefault button", function(){

    var model;
    var controller;

    beforeEach(function() {

        setFixtures(
        '<input type="text" id="TextDefaultTradingGroup" defaulttradinggroupid="10" value="ctg1">' +
        '<input id="removeDefault" type="button" value=" &gt; ">' +
        '<select id="AvailableTradingGroups" multiple="multiple" name="AvailableTradingGroups"></select>');

        model = new UpdateTradingGroupModel();
        controller = new UpdateTradingGroupController(model);
        controller.configureArrowButtons();
        
    });

    it("should slide selected field from TextDefaultTradingGroup to available trading groups", function(){
        expect(model.defaultTradingGroupId()).toEqual('10');
        expect(model.availableTradingGroups()).toEqual([]);
        $('#removeDefault').click();
        expect(model.defaultTradingGroupId()).toEqual('');
        expect(model.availableTradingGroups()).toEqual(['10']);
    });

    it("should be able to determine if TextDefaultTradingGroup is empty", function(){
        expect(model.defaultTradingGroupIsEmpty()).toBeFalsy();
    });

});

The first thing to note is this setup fixture line:

        setFixtures(
        '<input type="text" id="TextDefaultTradingGroup" defaulttradinggroupid="10" value="ctg1">' +
        '<input id="removeDefault" type="button" value=" &gt; ">' +
        '<select id="AvailableTradingGroups" multiple="multiple" name="AvailableTradingGroups"></select>');

It’s beautiful because it enables us to take just the html we need from our page, and set it up however we want it. Just view source on your page, grab what you need and put it in here. You can also ‘loadFixture‘ which will load an entire html file and set that up as your test harnass if you like.

In this case I want the TextDefaultTradingGroup textbox to have an id and value of 10 and ctg1 respectfully (note this doesn’t match what’s on the screen), with a remove button, and select box called AvailableTradingGroups which is empty.

I can then configure the buttons on my page:

        model = new UpdateTradingGroupModel();
        controller = new UpdateTradingGroupController(model);
        controller.configureArrowButtons();

And always remember to include whatever javascript you use in your app in your jasmine test runner file (else it won’t find your javascript).

Now I am ready to test what happens when someone clicks the remove button.

By simply asking my model I can verify that things start like this:

        expect(model.defaultTradingGroupId()).toEqual('10');
        expect(model.availableTradingGroups()).toEqual([]);

Then they hit the removeDefault button

        $('#removeDefault').click();

And end up like this:

        expect(model.defaultTradingGroupId()).toEqual('');
        expect(model.availableTradingGroups()).toEqual(['10']);

It was a lot of working setting this up, but now that the tracks are laid, we can setup any configuration of buttons and values we want.

You could test every other button combination and configuration of data (we had bugs in there).

Or if you wanted to display a warning dialog in the event someone tries to save a blank default trading group you could write something like this:

describe("UpdateTradingGroupSpec - When clicking the #save button", function(){

    var model;
    var controller;

    beforeEach(function() {

        setFixtures(
        '<input type="text" id="TextDefaultTradingGroup" defaulttradinggroupid="10" value="ctg1">' +
        '<select id="AvailableTradingGroups" multiple="multiple" name="AvailableTradingGroups"></select>' +
        '<select id="SelectedTradingGroups" multiple="multiple" name="SelectedTradingGroups">' +
            '<option value="11">ctg2</option>' +
            '<option value="12">ctg3</option>' +
        '</select>' +
        '<input type="submit" id="save" value="Save">');

        model = new UpdateTradingGroupModel();
        controller = new UpdateTradingGroupController(model);
        controller.configureArrowButtons();

        spyOn(Application, 'rootDir').andReturn("/");
    });

     it ("should display a warning dialog if defaultTradingGroupIsEmpty", function() {
        spyOn(model, 'defaultTradingGroupIsEmpty').andReturn(true);
        spyOn(controller, 'showWarningDialog');
        $('#save').click();
        expect(model.defaultTradingGroupIsEmpty).toHaveBeenCalled();
        expect(controller.showWarningDialog).toHaveBeenCalled();
    });

    it ("should not display a warning dialog if TextDefaultTradingGroup has data", function() {
        spyOn(model, 'defaultTradingGroupIsEmpty').andReturn(false);
        spyOn(controller, 'showWarningDialog');
        $('#save').click();
        expect(model.defaultTradingGroupIsEmpty).toHaveBeenCalled();
        expect(controller.showWarningDialog).not.toHaveBeenCalled();
    });

    it ("should collect parameters for save", function() {
        expect(model.saveParameters()).toEqual({ defaultTradingGroupId : '10', selectedCompanyTradingGroups : [ '11', '12' ] });
    });

});

Or if I wanted to verify an ajax call was made to the backend I could write something like this:

    it ("should make an ajax call to do the save", function() {
        spyOn($, 'ajax').andReturn('Successful save.');
        controller.updateTradingGroups(model);
        expect($.ajax).toHaveBeenCalled();
    });

I’ve only just scratched the surf of what we can do with Jasmine. But I wanted to get a few examples up to show what it can look like.

One thing I still haven’t got a great grasp out yet is Javascript closures and use of ‘this‘ and ‘that‘.

If you are coming from an enterprise language background like C# or Java, just beware that object state and scope is different in Javascript which is why passing in a model to a controller in Javascript has lines like

UpdateTradingGroupController.prototype.configureArrowButtons = function() {

    that = this; // important!

I am still figuring it out and will write more once I do.

But give Jasmine a chance. It’s helped us reduce the number of bugs in our app and I am looking forward to writing many more.

If you are just starting out checkout Evan Hahn’s Jasmine tutorial. It’s easy peasy.

Javascript that you need to know but might not

8 Comments

I stole this title from Bear Bibeault and Yehuda Katz’s excellent book jQuery in Action.

At the back of the book was a really good summary of things you need to know about Javascript but might not (another great reference is Javascript: The Good Parts by Douglas Crawford).

To remind myself what these things are I am listing them here:

Reference identifiers

Normally we reference properties using the dot (period) identifier:

var ride= new Ride();
ride.make = ‘Yamaha’;

But you can also reference properties like this:

ride.make
ride[‘make’]
ride[‘m’ + ‘a’ + ‘k’ + ‘e’]

and like this.

var p = ‘make’;
ride[p];

I didn’t know you could do that in Javascript, but sometimes you have to. Especially if you don’t know the name of the property you are looking for, or it has spaces in it like this:

ride['a property name with spaces']

Object literals

Or can create objects using the new operator (like Java and C#) or you can create them with this convenient shorthand:

var ride = { 
	make: ‘Yamaha’,
	year: 2005,
	owner: {
		name: ‘Biker Bob’
	}
};

This is handy for constructing JSON objects for passing data around (which is the way a lot of communication happens over the web these days – not so much XML anymore).

Properties of windows

var someVariable = ‘Some quote’;

and

someObject.aProperty = ‘Some other quote’;

Are basically the same thing. Each instance assigns a string to a variable and an object.

When you define variables at the top level like this (not inside any function), it’s a programmer friendly way of referencing a property of the predefined JavaScript ‘window’ object. Any reference made in the top-level is implicitly made on the window instance.

Functions are first class citizens

Functions get treated just like variables in JavaScript. Meaning you can assign them to variables and pass them around.

var ride = { 
	make: ‘Yamaha’,
	year: 2005,
	owner: { name: ‘Biker Bob’, occupation: ‘Bounty hunter’ },
	whatAmI: function() {
		return this.year + ‘ ‘ + this.make + ‘ ‘ + this.model;
        }
};

Functions as callbacks

Functions are passed just like variables in Javascript. You can define a function and pass it to a method as a callback like this:

function hello() { alert('Hi there!'); }
setTimeout(hello, 5000);

See – just like any other variable. Very powerful. Or you can inline it all at once like this:

setTimeout(function() { function hello() { alert('Hi there!'); }, 5000);

The functions described here at either top-level functions (which we know are top-level windows properties) or assigned to parameters in function calls. You can also assign functions to properties of objects.

What is ‘this’ all about?

In class-based OO languages, the this pointer generally references the instance of the class for which the method has been declared.

In Javascript, where funcations are first-class ogjects that aren’t declared as part of anything, the object referenced by this (termed the function context) is determined not by how the function is declared but by how it’s invoked.

This means the same function can have different contexts depending on how it’s called.

For example (taken from jQuery in action) say we have the following object:

var ride = {
    make: 'Yamaha',
    whatAmI: function() {
        return this.make;
    }
};

And we call it 4 different ways:

var o1 = { handle: 'o1' };
var o2 = { handle: 'o2' };
var o3 = { handle: 'o3' };
window.handle = 'window';

function whoAmI() {
    return this.handle;
}

o1.identifyMe = whoAmI;

alert(whoAmI());                // window
alert(o1.identifyMe());         // o1
alert(whoAmI.call(o2));         // o2
alert(whoAmI.apply(o3));        // o3

The results are the comments beside the call just above.

So you can see that it’s not how an object is declared that determines it’s context, it’s how it is called.

Closures

A closure is a Function instance coupled with the local variables from its environment that are necessary for it’s execution.

It’s a bundle of Function and variables that execute together, and close out together.

When a function is declared, it has the ability to reference any variables that are in it’s scope at the point of declaration. No surprise’s here, typical C#/Java.

But, with closures, these variables are carried along with the function even after that point of declaration has gone out of scope, closing the declaration.

The ability for callback functions to reference the local variables in effect when they were declared is an essential tool for writing effective Javascript.

Here’s an example:

$(function() {
    var local = 1;
    window.setInterval(function() {
        $('display').append('<div>new Date() + 'at local=' + local + '</div>');
        local++;
    }, 3000);
});

<body>
    <div id="display"></div>
</body>

If you were unfamiliar with closures you might see a few problems with the above code.

We might surmise that, because the callback will fire off three seconds after the page is loaded, the value of local is undefined during the execution of the callback. After all, the block in which local is declared goes out of scope when the ready handler exits.

But when you run it it works!

May 6 ... local = 1
May 6 ... local = 2
May 6 ... local = 3

While it is true that the block in which local is declared goes out of scope when the ready handler exists, the closure created by the declaration of the function, which includes local, stays in scope for the lifetime of the function.

Another important feature of closures is that a function context is never included as part of the closure. For example the following code won’t work as we expect:

this.id = 'someID';
$('*').each(function() {
    alert(this.id);
});

Remember that each function invocation has it’s own function context so that, in the preceding code, the function context is an element from the jQuery wrapped set (not the property of the outer function set to ‘someID’.

When access to the object serving as the function context in the outer function is needed (more akin to classic OO), you need to store the outer context by creating a copy of the this reference in a local variable that will be included as part of the closure.

this.id = 'someID';
var outer = this;
$('*').each(function() {
    alert(outer.id);
});

This local variable outer, which is a part of the outer function’s context, becomes a part of the closure and can be access in the callback function.

Closures are indispensable when creating elegant code using jQuery commands that utilize asynchronous callbacks, which is particularly true in Ajax requests and event handling.

For more excellentness on Javascript and jQuery (where these abbreviated examples comes from) pick up a copy of Bear and Yehuda’s book. You won’t be disappointed

%d bloggers like this: