RSS

Category Archives: Javascript

ERB Delimiters in Template Underscore

Now that we know how to create a generic template let’s look at some ERB delimiters.

The _.template function has 3 arguments:

  1. String text : the template string
  2. Object data : the evaluation data
  3. Object settings : local settings, the _.templateSettings is the global settings object

If no data (or null) given, than a render function will be returned. It has 1 argument:

  1. Object data : same as the data above

There are 3 regex patterns and 1 static parameter in the settings:

  1. RegExp evaluate : “<%code%>” in template string
  2. RegExp interpolate : “<%=code%>” in template string
  3. RegExp escape : “<%-code%>”
  4. String variable : optional, the name of the data parameter in the template string

The code in an evaluate section will be simply evaluated. You can add string from this section with the__p+=”mystring” command to the evaluated template, but this is not recommended (not part of the templating interface), use the interpolate section instead of that. This type of section is for adding blocks like if or for to the template.

The result of the code in the interpolate section will added to the evaluated template. If null given back, then empty string will added.

The escape section escapes html with _.escape on the return value of the given code. So its similar than an _.escape(code) in an interpolate section, but it escapes with \ the whitespace characters like\n before it passes the code to the _.escape. I don’t know why is that important, it’s in the code, but it works well with the interpolate and _.escape – which doesn’t escape the white-space characters – too.

By default the data parameter is passed by a with(data){…} statement, but this kind of evaluating is much slower than the evaluating with named variable. So naming the data with the variable parameter is something good…

For example:

var html = _.template(
    "<pre>The \"<% __p+=_.escape(o.text) %>\" is the same<br />" +
        "as the  \"<%= _.escape(o.text) %>\" and the same<br />" +
        "as the \"<%- o.text %>\"</pre>",
    {
        text: "<b>some text</b> and \n it's a line break"
    },
    {
        variable: "o"
    }
);

$("body").html(html);

results

The "<b>some text</b> and 
 it's a line break" is the same
as the "<b>some text</b> and 
 it's a line break" and the same
as the "<b>some text</b> and 
 it's a line break"

You can find here more examples how to use the template and override the default settings:http://underscorejs.org/#template

By template loading you have many options, but at the end you always have to convert the template into string. You can give it as normal string like the example above, or you can load it from a script tag, and use the .html() function of jquery, or you can load it from a separate file with the tpl plugin of require.js.

Another option to build the dom tree with laconic instead of templating.

 
Leave a comment

Posted by on August 22, 2014 in Javascript, Underscore

 

Prepire Environment for RequireJS, Backbone, and Bower Starter Template on Windows

By Sochinda Tith,

Required application:

  1. Nodejs installer (http://nodejs.org/download/)
  2. Msysgit (http://msysgit.github.io/)

Download both application and install into Windows OS

Open “CMD” as administrator and run:

$ npm install bower-installer
$ npm install -g requirejs
del %HOMEDRIVE%%HOMEPATH%\AppData\Roaming\npm\r.js
DOSKEY r.js=r.js.cmd $*

*** Notation: for new version bower, we have to change component.js to bower.js

If there any error’s called javascript source map, please go to setting in browser and un-check this option.

Please follow instruction in bellow video:

 
Leave a comment

Posted by on June 6, 2014 in Backbone, Javascript, RequireJs

 

Building Mobile Apps with HTML and a Local Database

by 

After my recent post, Crafting Native Looking iOS Apps with HTML, a number of you asked for an offline version that would use a Local Database (instead of the simple in-memory store) and provide a mechanism to automatically keep the local database in sync with a server database.

I’ll save automatic data synchronization strategies for a future post, but here is the first step: an “offline” version of the application that uses the device’s or the browser’s local database as its data provider. This version still uses Backbone.js as its architectural framework. Backbone.js makes it easy to change its default data access mechanism (which assumes RESTful services). You just replace the default Backbone.sync implementation and provide your own data access logic: in this case, some local SQL logic.

Web SQL vs IndexedDB

As you probably know, there have been two competing database APIs for HTML. From the W3C web site:

  • The Web SQL specification defines an API for storing data in databases that can be queried using a variant of SQL. This specification is no longer in active maintenance and the Web Applications Working Group does not intend to maintain it further.
  • The Indexed Database specification defines APIs for a database of records holding simple values and hierarchical objects. It is a working draft, and “work in progress”.

Even though the W3C is no longer actively maintaining the spec, this application uses the Web SQL API because, as a mobile app, its two main target platforms are iOS and Android, which both currently support Web SQL but not IndexedDB. More detailed platform support information can be found on caniuse.com (Web SQL and IndexedDB).

Chrome, Safari, and Opera on the desktop also support Web SQL, which means that you can run the application in these browsers. Try it here. For example, using the Chrome Developer Tools you could debug the application and inspect the database as shown in this screenshot:

Firefox and IE don’t support Web SQL. You could easily create an alternative version of EmployeeDAO (described below) that uses IndexedDB instead. You could also create a version of the application that uses either Web SQL or IndexedDB depending on the platform it’s running on.

Code Highlights

The source code is available in the localdb folder of the backbone-directory repository on GitHub. Here is a quick walkthrough…

The data access logic is encapsulated in EmployeeDAO, which also has a “populate” function to populate the employee table with sample data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
directory.dao.EmployeeDAO = function(db) {
    this.db = db;
};
_.extend(directory.dao.EmployeeDAO.prototype, {
    findByName: function(key, callback) {
        this.db.transaction(
            function(tx) {
                var sql = "SELECT e.id, e.firstName, e.lastName, e.title, count(r.id) reportCount " +
                    "FROM employee e LEFT JOIN employee r ON r.managerId = e.id " +
                    "WHERE e.firstName || ' ' || e.lastName LIKE ? " +
                    "GROUP BY e.id ORDER BY e.lastName, e.firstName";
                tx.executeSql(sql, ['%' + key + '%'], function(tx, results) {
                    var len = results.rows.length,
                        employees = [],
                        i = 0;
                    for (; i < len; i = i + 1) {
                        employees[i] = results.rows.item(i);
                    }
                    callback(employees);
                });
            },
            function(tx, error) {
                alert("Transaction Error: " + error);
            }
        );
    },
    findById: function(id, callback) {
        // removed for brevity
    },
    findByManager: function(managerId, callback) {
        // removed for brevity
    },
    populate: function(callback) {
        // removed for brevity
    }
});

Models are annotated with a “dao” attribute to indicate which data object to use to access their underlying data.

1
2
3
4
5
6
7
8
9
10
11
12
13
directory.models.Employee = Backbone.Model.extend({
    dao: directory.dao.EmployeeDAO,
});
directory.models.EmployeeCollection = Backbone.Collection.extend({
    dao: directory.dao.EmployeeDAO,
    model: directory.models.Employee,
});

With that infrastructure in place, you can then override Backbone.sync to access data from the local database instead of RESTful services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Backbone.sync = function(method, model, options) {
    var dao = new model.dao(directory.db);
    if (method === "read") {
        if (model.id) {
            dao.findById(model.id, function(data) {
                options.success(data);
            });
        } else if (model.managerId) {
            dao.findByManager(model.managerId, function(data) {
                options.success(data);
            });
        }
        // removed for brevity
    }
};

Source Code

The source code is available in the localdb folder of the backbone-directory repository on GitHub.

 

Copy from: http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/

 
Leave a comment

Posted by on June 4, 2014 in Javascript

 

RequireJS and Windows: how to invoke the r.js command

By: Hajime Branko Yamasaki Vukelic

For Install r.js

npm install -g requirejs

 

If you’ve been frustrated to hell using RequireJS on Windows, and the r.js optimizerin particular, you might have witnessed things like WSH errors or your editor opening the r.js file. I’ve seen a few frustrated people post bug reports on GitHub, so I decided to post a few solutions to the issue.

Before we begin, let me note one thing. The optimizer works just fine on Windows. The documented way of running the optimizer is to use r.js.cmd command instead of just r.js, and if you stick to that recommendation you shouldn’t have any issues. In the following paragraphs, I will describe a few ways to invoke r.jswithout involving the (slightly) awkward .cmd extension.

Remove r.js file

When you install the requirejs package using npm, it will install two files in %HOMEDRIVE%%HOMEPATH%\AppData\Roaming\npm directory. One is called r.jsand another is r.js.cmd. The problem is the former. Although it has a .jsextension, it’s actually a shell script (for the uninitiated, it’s a UNIX/Linux thing), and not meant to be run with NodeJS at all.

Removing the r.js file allows you to run the r.js.cmd file without the extension. Use the following command to delete it:

del %HOMEDRIVE%%HOMEPATH%\AppData\Roaming\npm\r.js

Be sure to leave the .cmd file intact.

DOSKEY trickery

If you haven’t used DOSKEY before, you should give it a try. An article by Dan Fabulich gives you a condensed version of how to set up permanent DOSKEY macros that will load each time you open command prompt.

To set up a macro for r.js use the following command:

DOSKEY r.js=r.js.cmd $*

This will override the r.js file in the %HOMEDRIVE%%HOMEPATH%\AppData\Roaming\npm directory, and allow you to user.js without the .cmd extension. Be sure to save the macro as per Dan’s instructions.

Use a build system

This may be quite obvious, but you can use a proper build system that would call the RequireJS optimizer on your behalf. This does away with the poblem of how to invoke r.js, and also gives you other goodies commonly available in such systems. Try volo or Grunt.

volo deserves a special recommendation since it’s maintained by James Burke, author of RequireJS, and comes with a great project template that sets things up for requiring and building, and the ability to convert modules to AMD when installing from online repositories or other locations.

 

Copy From: http://www.brankovukelic.com/2013/10/requirejs-and-windows-how-to-invoke-rjs.html,

 
Leave a comment

Posted by on February 14, 2014 in Javascript, RequireJs

 

Uncaught TypeError: undefined is not a function

I had the exact same problem and I was struggling around many hours until I saw your entry here. Then I started over from scratch and now it works for me at least.

requirejs.config({
    baseUrl:'/js/',
    paths:{
      jquery:'vendor/jquery',
      handlebars: 'vendor/handlebars',
      text: 'vendor/require-text',
      chaplin:'vendor/chaplin',
      underscore:'vendor/underscore',
      backbone:'vendor/backbone',
      highcharts: 'vendor/highcharts'
    },

    shim: {
      backbone: {
        deps: ['underscore', 'jquery'],
        exports: 'Backbone'
      },
      underscore: {
        exports: '_'
      },    
      highcharts: {
        exports: 'Highcharts'
      }    
    },
});

Since I use Chaplin on top of Backbone, I am including some more files in my paths attribute. Highcharts has a similar structure to Backbone so I thought I could load it the same way. It works for me now. As you can see, I am introducing highcharts in the paths attribute already to export it as a shim afterwords.

 
Leave a comment

Posted by on January 31, 2013 in JQuery, RequireJs

 

Unit Testing Backbone.js Apps With QUnit And SinonJS

This article will be appearing in my forthcoming book on Backbone.js and continues the section on unit testing. We previously looked at Jasmine and will now look at QUnit and SinonJS.

QUnit is a powerful JavaScript test suite written by jQuery team member Jörn Zaefferer and used by many large open-source projects (such as jQuery and Backbone.js) to test their code. It’s both capable of testing standard JavaScript code in the browser as well as code on the server-side (where environments supported include Rhino, V8 and SpiderMonkey). This makes it a robust solution for a large number of use-cases.

 

Quite a few Backbone.js contributors feel that QUnit is a better introductory framework for testing if you don’t wish to start off with Jasmine and BDD right away. As we’ll see later on in this article, QUnit can also be combined with third-party solutions such as SinonJS to produce an even more powerful testing solution supporting spies and mocks, which some say is preferable over Jasmine.

My personal recommendation is that it’s worth comparing both frameworks and opting for the solution that you feel the most comfortable with.

QUnit

Getting Setup

Luckily, getting QUnit setup is a fairly straight-forward process that will take less than 5 minutes.

We first setup a testing environment composed of three files:

  • A HTML structure for displaying test results,
  • The qunit.js file composing the testing framework and,
  • The qunit.css file for styling test results.

The latter two of these can be downloaded from the QUnit website.

If you would prefer, you can use a hosted version of the QUnit source files for testing purposes. The hosted URLs can be found here.

Sample HTML with QUnit-compatible markup:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>QUnit Test Suite</title>
  5.      <link rel=”stylesheet” href=”qunit.css”>
  6.      <script src=”qunit.js”></script>
  7.      <!– Your application –>
  8.      <script src=”app.js”></script>
  9.      <!– Your tests –>
  10.      <script src=”tests.js”></script>
  11. </head>
  12. <body>
  13.     <h1 id=”qunit-header”>QUnit Test Suite</h1>
  14.     <h2 id=”qunit-banner”></h2>
  15.     <div id=”qunit-testrunner-toolbar”></div>
  16.     <h2 id=”qunit-userAgent”></h2>
  17.     <ol id=”qunit-tests”>test markup, hidden.</ol>
  18. </body>
  19. </html>

Let’s go through the elements above with qunit mentioned in their ID. When QUnit is running:

  • qunit-header shows the name of the test suite
  • qunit-banner shows up as red if a test fails and green if all tests pass
  • qunit-testrunner-toolbar contains additional options for configuring the display of tests
  • qunit-userAgent displays the navigator.userAgent property
  • qunit-tests is a container for our test results

When running correctly, the above test runner looks as follows:

screenshot 1

The numbers of the form (a, b, c) after each test name correspond to a) failed asserts, b) passed asserts and c) total asserts. Clicking on a test name expands it to display all of the assertions for that test case. Assertions in green have successfully passed.

screenshot 2

If however any tests fail, the test gets highlighted (and the qunit-banner at the top switches to red):

screenshot 3

Assertions

QUnit supports a number of basic assertions, which are used in testing to verify that the result being returned by our code is what we expect. If an assertion fails, we know that a bug exists. Similar to Jasmine, QUnit can be used to easily test for regressions. Specifically, when a bug is found one can write an assertion to test the existence of the bug, write a patch and then commit both. If subsequent changes to the code break the test you’ll know what was responsible and be able to address it more easily.

Some of the supported QUnit assertions we’re going to look at first are:

  • ok ( state, message ) – passes if the first argument is truthy
  • equal ( actual, expected, message ) – a simple comparison assertion with type coercion
  • notEqual ( actual, expected, message ) – the opposite of the above
  • expect( amount ) – the number of assertions expected to run within each test
  • strictEqual( actual, expected, message) – offers a much stricter comparison than equal() and is considered the preferred method of checking equality as it avoids stumbling on subtle coercion bugs
  • deepEqual( actual, expected, message ) – similar to strictEqual, comparing the contents (with ===) of the given objects, arrays and primitives.

Creating new test cases with QUnit is relatively straight-forward and can be done using test(), which constructs a test where the first argument is the name of the test to be displayed in our results and the second is a callback function containing all of our assertions. This is called as soon as QUnit is running.

Basic test case using test( name, callback ):

  1. var myString = “Hello Backbone.js”;
  2. test( “Our first QUnit test – asserting results”, function(){
  3.     // ok( boolean, message )
  4.     ok( true, “the test succeeds”);
  5.     ok( false, “the test fails”);
  6.     // equal( actualValue, expectedValue, message )
  7.     equal( myString, “Hello Backbone.js”, “The value expected is Hello Backbone.js!”);
  8. });

What we’re doing in the above is defining a variable with a specific value and then testing to ensure the value was what we expected it to be. This was done using the comparison assertion, equal(), which expects its first argument to be a value being tested and the second argument to be the expected value. We also used ok(), which allows us to easily test against functions or variables that evaluate to booleans.

Note: Optionally in our test case, we could have passed an ‘expected’ value to test()defining the number of assertions we expect to run. This takes the form: test( name, [expected], test ); or by manually settings the expectation at the top of the test function, like so: expect( 1 ). I recommend you to make it a habit and always define how many assertions you expect. More on this later.

As testing a simple static variable is fairly trivial, we can take this further to test actual functions. In the following example we test the output of a function that reverses a string to ensure that the output is correct using equal() and notEqual():

Comparing the actual output of a function against the expected output:

  1. function reverseString( str ){
  2.     return str.split(“”).reverse().join(“”);
  3. }
  4. test( “reverseString()”, function() {
  5.     expect( 5 );
  6.     equal( reverseString(“hello”), “olleh”, “The value expected was olleh” );
  7.     equal( reverseString(“foobar”), “raboof”, “The value expected was raboof” );
  8.     equal( reverseString(“world”), “dlrow”, “The value expected was dlrow” );
  9.     notEqual( reverseString(“world”), “dlroo”, “The value was expected to not be dlroo” );
  10.     equal( reverseString(“bubble”), “double”, “The value expected was elbbub” );
  11. })

Running these tests in the QUnit test runner (which you would see when your HTML test page was loaded) we would find that four of the assertions pass whilst the last one does not. The reason the test against "double" fails is because it was purposefully written incorrectly. In your own projects if a test fails to pass and your assertions are correct, you’ve probably just found a bug!

Adding structure to assertions

Housing all of our assertions in one test case can quickly become difficult to maintain, but luckily QUnit supports structuring blocks of assertions more cleanly. This can be done using module() – a method that allows us to easily group tests together. A typical approach to grouping might be keeping multiple tests testing a specific method as part of the same group (module).

Basic QUnit Modules:

  1. module( “Module One” );
  2. test( “first test”, function() {} );
  3. test( “another test”, function() {} );
  4. module( “Module Two” );
  5. test( “second test”, function() {} );
  6. test( “another test”, function() {} );
  7. module( “Module Three” );
  8. test( “third test”, function() {} );
  9. test( “another test”, function() {} );

We can take this further by introducing setup() and teardown() callbacks to our modules, where setup() is run before each test whilst teardown() is run after each test.

Using setup() and teardown() :

  1. module( “Module One”, {
  2.     setup: function() {
  3.         // run before
  4.     },
  5.     teardown: function() {
  6.         // run after
  7.     }
  8. });
  9. test(“first test”, function() {
  10.     // run the first test
  11. });

These callbacks can be used to define (or clear) any components we wish to instantiate for use in one or more of our tests. As we’ll see shortly, this is ideal for defining new instances of views, collections, models or routers from a project that we can then reference across multiple tests.

Using setup() and teardown() for instantiation and clean-up:

  1. // Define a simple model and collection modeling a store and
  2. // list of stores
  3. var Store = Backbone.Model.extend({});
  4. var StoreList = Backbone.Collection.extend({
  5.     model: store,
  6.     comparator: function( store ) { return store.get(“name”) }
  7. });
  8. // Define a group for our tests
  9. module( “StoreList sanity check”, {
  10.     setup: function() {
  11.         this.list = new StoreList;
  12.         this.list.add(new Store({ name: “Costcutter” }));
  13.         this.list.add(new Store({ name: “Target” }));
  14.         this.list.add(new Store({ name: “Walmart” }));
  15.         this.list.add(new Store({ name: “Barnes & Noble” });
  16.     },
  17.     teardown: function() {
  18.         window.errors = null;
  19.     }
  20. });
  21. // Test the order of items added
  22. test( “test ordering”, function() {
  23.     expect( 1 );
  24.     var expected = [“Barnes & Noble”, “Costcutter”, “Target”, “Walmart”];
  25.     var actual = this.list.pluck(“name”);
  26.     deepEqual( actual, expected, “is maintained by comparator” );
  27. });

Here, a list of stores is created and stored on setup(). A teardown() callback is used to simply clear our a list of errors we might be storing within the window scope, but is otherwise not needed.

Assertion examples

Before we continue any further, let’s review some more examples of how QUnits various assertions can be correctly used when writing tests:

equal – a comparison assertion. It passes if actual == expected

  1. test( “equal”, 2, function() {
  2.   var actual = 6 – 5;
  3.   equal( actual, true,  “passes as 1 == true” );
  4.   equal( actual, 1,     “passes as 1 == 1” );
  5. });

notEqual – a comparison assertion. It passes if actual != expected

  1. test( “notEqual”, 2, function() {
  2.   var actual = 6 – 5;
  3.   notEqual( actual, false, “passes as 1 != false” );
  4.   notEqual( actual, 0,     “passes as 1 != 0” );
  5. });

strictEqual – a comparison assertion. It passes if actual === expected.

  1. test( “strictEqual”, 2, function() {
  2.   var actual = 6 – 5;
  3.   strictEqual( actual, true,  “fails as 1 !== true” );
  4.   strictEqual( actual, 1,     “passes as 1 === 1” );
  5. });

notStrictEqual – a comparison assertion. It passes if actual !== expected.

  1. test( “notStrictEqual”, 2, function() {
  2.   var actual = 6 – 5;
  3.   notStrictEqual( actual, true,  “passes as 1 !== true” );
  4.   notStrictEqual( actual, 1,     “fails as 1 === 1” );
  5. });

deepEqual – a recursive comparison assertion. Unlike strictEqual(), it works on objects, arrays and primitives.

  1. test( “deepEqual”, 4, function() {
  2.   var actual = {q: “foo”, t: “bar”};
  3.   var el =  $(“div”);
  4.   var children = $(“div”).children();
  5.   equal( actual, {q: “foo”, t: “bar”},   “fails – objects are not equal using equal()” );
  6.   deepEqual( actual, {q: “foo”, t: “bar”},   “passes – objects are equal” );
  7.   equal( el, children, “fails – jQuery objects are not the same” );
  8.   deepEqual(el, children, “fails – objects not equivalent” );
  9. });

notDeepEqual – a comparison assertion. This returns the opposite of deepEqual

  1. test( “notDeepEqual”, 2, function() {
  2.   var actual = {q: “foo”, t: “bar”};
  3.   notEqual( actual, {q: “foo”, t: “bar”},   “passes – objects are not equal” );
  4.   notDeepEqual( actual, {q: “foo”, t: “bar”},   “fails – objects are equivalent” );
  5. });

raises – an assertion which tests if a callback throws any exceptions

  1. test( “raises”, 1, function() {
  2.   raises(function() {
  3.     throw new Error( “Oh no! It’s an error!” );
  4.   }, “passes – an error was thrown inside our callback”);
  5. });

Fixtures

From time to time we may need to write tests that modify the DOM. Managing the clean-up of such operations between tests can be a genuine pain, but thankfully QUnit has a solution to this problem in the form of the #qunit-fixture element, seen below.

Fixture markup:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>QUnit Test</title>
  5.     <link rel=”stylesheet” href=”qunit.css”>
  6.     <script src=”qunit.js”></script>
  7.     <script src=”app.js”></script>
  8.     <script src=”tests.js”></script>
  9. </head>
  10. <body>
  11.     <h1 id=”qunit-header”>QUnit Test</h1>
  12.     <h2 id=”qunit-banner”></h2>
  13.     <div id=”qunit-testrunner-toolbar”></div>
  14.     <h2 id=”qunit-userAgent”></h2>
  15.     <ol id=”qunit-tests”></ol>
  16.     <div id=”qunit-fixture”></div>
  17. </body>
  18. </html>

We can either opt to place static markup in the fixture or just insert/append any DOM elements we may need to it. QUnit will automatically reset the innerHTML of the fixture after each test to its original value. In case you’re using jQuery, it’s useful to know that QUnit checks for its availability and will opt to use $(el).html() instead, which will cleanup any jQuery event handlers too.

Fixtures example:

Let us now go through a more complete example of using fixtures. One thing that most of us are used to doing in jQuery is working with lists – they’re often used to define the markup for menus, grids and a number of other components. You may have used jQuery plugins before that manipulated a given list in a particular way and it can be useful to test that the final (manipulated) output of the plugin is what was expected.

For the purposes of our next example, we’re going to use Ben Alman’s $.enumerate()plugin, which can prepend each item in a list by its index, optionally allowing us to set what the first number in the list is. The code snippet for the plugin can be found below, followed by an example of the output is generates:

  1. $.fn.enumerate = function( start ) {
  2.       if ( typeof start !== “undefined” ) {
  3.         // Since `start` value was provided, enumerate and return
  4.         // the initial jQuery object to allow chaining.
  5.         return this.each(function(i){
  6.           $(this).prepend( “<b>” + ( i + start ) + “</b> ” );
  7.         });
  8.       } else {
  9.         // Since no `start` value was provided, function as a
  10.         // getter, returing the appropriate value from the first
  11.         // selected element.
  12.         var val = this.eq( 0 ).children( “b” ).eq( 0 ).text();
  13.         return Number( val );
  14.       }
  15.     };
  16. /*
  17.     <ul>
  18.       <li>1. hello</li>
  19.       <li>2. world</li>
  20.       <li>3. i</li>
  21.       <li>4. am</li>
  22.       <li>5. foo</li>
  23.     </ul>
  24. */

Let’s now write some specs for the plugin. First, we define the markup for a list containing some sample items inside our qunit-fixture element:

<div id="qunit-fixture">
    <ul>
      <li>hello</li>
      <li>world</li>
      <li>i</li>
      <li>am</li>
      <li>foo</li>
    </ul>
  </div>

Next, we need to think about what should be tested. $.enumerate() supports a few different use cases, including:

  • No arguments passed – i.e $(el).enumerate()
  • 0 passed as an argument – i.e $(el).enumerate(0)
  • 1 passed as an argument – i.e $(el).enumerate(1)

As the text value for each list item is of the form “n. item-text” and we only require this to test against the expected output, we can simply access the content using$(el).eq(index).text() (for more information on .eq() see here).

and finally, here are our test cases:

  1. module( “jQuery#enumerate” );
  2. test( “No arguments passed”, 5, function() {
  3.   var items = $(“#qunit-fixture li”).enumerate();
  4.   equal( items.eq(0).text(), “1. hello”, “first item should have index 1” );
  5.   equal( items.eq(1).text(), “2. world”, “second item should have index 2” );
  6.   equal( items.eq(2).text(), “3. i”, “third item should have index 3” );
  7.   equal( items.eq(3).text(), “4. am”, “fourth item should have index 4” );
  8.   equal( items.eq(4).text(), “5. foo”, “fifth item should have index 5” );
  9. });
  10. test( “0 passed as an argument”, 5, function() {
  11.   var items = $(“#qunit-fixture li”).enumerate( 0 );
  12.   equal( items.eq(0).text(), “0. hello”, “first item should have index 0” );
  13.   equal( items.eq(1).text(), “1. world”, “second item should have index 1” );
  14.   equal( items.eq(2).text(), “2. i”, “third item should have index 2” );
  15.   equal( items.eq(3).text(), “3. am”, “fourth item should have index 3” );
  16.   equal( items.eq(4).text(), “4. foo”, “fifth item should have index 4” );
  17. });
  18. test( “1 passed as an argument”, 3, function() {
  19.   var items = $(“#qunit-fixture li”).enumerate( 1 );
  20.   equal( items.eq(0).text(), “1. hello”, “first item should have index 1” );
  21.   equal( items.eq(1).text(), “2. world”, “second item should have index 2” );
  22.   equal( items.eq(2).text(), “3. i”, “third item should have index 3” );
  23.   equal( items.eq(3).text(), “4. am”, “fourth item should have index 4” );
  24.   equal( items.eq(4).text(), “5. foo”, “fifth item should have index 5” );
  25. });

Asynchronous code

As with Jasmine, the effort required to run synchronous tests with QUnit is fairly straight-forward. That said, what about tests that require asynchronous callbacks (such as expensive processes, Ajax requests and so on)? When we’re dealing with asynchronous code, rather than letting QUnit control when the next test runs, we can inform that we need it to stop running and wait until it’s okay to continue once again.

Remember: running asynchronous code without any special considerations can cause incorrect assertions to appear in other tests, so we want to make sure we get it right.

Writing QUnit tests for asynchronous code is made possible using the start() and`stop() methods, which programmatically set the start and stop points during such tests. Here’s a simple example:

  1. test(“An async test”, function(){
  2.    stop();
  3.    expect( 1 );
  4.    $.ajax({
  5.         url: “/test”,
  6.         dataType: “json”,
  7.         success: function( data ){
  8.             deepEqual(data, {
  9.                topic: “hello”,
  10.                message: “hi there!”
  11.             });
  12.             start();
  13.         }
  14.     });
  15. });

A jQuery $.ajax() request is used to connect to a test resource and assert that the data returned is correct. deepEqual() is used here as it allows us to compare different data types (e.g objects, arrays) and ensures that what is returned is exactly what we’re expecting. We know that our Ajax request is asynchronous and so we first callstop(), run the code making the request and finally at the very end of our callback, inform QUnit that it is okay to continue running other tests.

Note: rather than including stop(), we can simply exclude it and substitute test()with asyncTest() if we prefer. This improves readability when dealing with a mixture of asynchronous and synchronous tests in your suite. Whilst this setup should work fine for many use-cases, there is no guarantee that the callback in our $.ajax()request will actually get called. To factor this into our tests, we can use expect()once again to define how many assertions we expect to see within our test. This is a healthy safety blanket as it ensures that if a test completes with an insufficient number of assertions, we know something went wrong and fix it.

SinonJS

Similar to the section on testing Backbone.js apps using the Jasmine BDD framework, we’re nearly ready to take what we’ve learned and write a number of QUnit tests for our Todo application.

Before we start though, you may have noticed that QUnit doesn’t support test spies. Test spies are functions which record arguments, exceptions and return values for any of their calls. They’re typically used to test callbacks and how functions may be used in the application being tested. In testing frameworks, spies can usually be either anonymous functions or wrap functions which already exist.

What is SinonJS?

In order for us to substitute support for spies in QUnit, we will be taking advantage of a mocking framework called SinonJS by Christian Johansen. We will also be using theSinonJS-QUnit adapter which provides seamless integration with QUnit (meaning setup is minimal). Sinon.JS is completely test-framework agnostic and should be easy to use with any testing framework, so it’s ideal for our needs.

The framework supports three features we’ll be taking advantage of for unit testing our application:

  • Anonymous spies
  • Spying on existing methods
  • A rich inspection interface

Using this.spy() without any arguments creates an anonymous spy. This is comparable to jasmine.createSpy() and we can observe basic usage of a SinonJS spy in the following example:

Basic Spies:

  1. test(“should call all subscribers for a message exactly once”, function () {
  2.     var message = getUniqueString();
  3.     var spy = this.spy();
  4.     PubSub.subscribe( message, spy );
  5.     PubSub.publishSync( message, “Hello World” );
  6.     ok( spy1.calledOnce, “the subscriber was called once” );
  7. });

We can also use this.spy() to spy on existing functions (like jQuery’s $.ajax) in the example below. When spying on a function which already exists, the function behaves normally but we get access to data about its calls which can be very useful for testing purposes.

Spying On Existing Functions:

  1. test( “should inspect jQuery.getJSON’s usage of jQuery.ajax”, function () {
  2.     this.spy( jQuery, “ajax” );
  3.     jQuery.getJSON( “/todos/completed” );
  4.     ok( jQuery.ajax.calledOnce );
  5.     equals( jQuery.ajax.getCall(0).args[0].url, “/todos/completed” );
  6.     equals( jQuery.ajax.getCall(0).args[0].dataType, “json” );
  7. });

SinonJS comes with a rich spy interface which callows us to test whether a spy was called with a specific argument, if it was called a specific number of times and test against the values of arguments. A complete list of features supported in the interface can be found here (http://sinonjs.org/docs/), but let’s take a look at some examples demonstrating some of the most commonly used ones:

Matching arguments: test a spy was called with a specific set of arguments:

  1. test( “Should call a subscriber with standard matching”: function () {
  2.     var spy = sinon.spy();
  3.     PubSub.subscribe( “message”, spy );
  4.     PubSub.publishSync( “message”, { id: 45 } );
  5.     assertTrue( spy.calledWith( { id: 45 } ) );
  6. });

Stricter argument matching: test a spy was called at least once with specific arguments and no others:

  1. test( “Should call a subscriber with strict matching”: function () {
  2.     var spy = sinon.spy();
  3.     PubSub.subscribe( “message”, spy );
  4.     PubSub.publishSync( “message”, “many”, “arguments” );
  5.     PubSub.publishSync( “message”, 12, 34 );
  6.     // This passes
  7.     assertTrue( spy.calledWith(“many”) );
  8.     // This however, fails
  9.     assertTrue( spy.calledWithExactly( “many” ) );
  10. });

Testing call order: testing if a spy was called before or after another spy:

  1. test( “Should call a subscriber and maintain call order”: function () {
  2.     var a = sinon.spy();
  3.     var b = sinon.spy();
  4.     // This would fail as b was called after a
  5.     PubSub.subscribe( “message”, a );
  6.     PubSub.subscribe( “event”, b );
  7.     PubSub.publishSync( “message”, { id: 45 } );
  8.     PubSub.publishSync( “event”, [1, 2, 3] );
  9.     assertTrue( a.calledBefore(b) );
  10.     assertTrue( b.calledAfter(a) );
  11. });

Match execution counts: test a spy was called a specific number of times:

  1. test( “Should call a subscriber and check call counts”, function () {
  2.     var message = getUniqueString();
  3.     var spy = this.spy();
  4.     PubSub.subscribe( message, spy );
  5.     PubSub.publishSync( message, “some payload” );
  6.     // Passes if spy was called once and only once.
  7.     ok( spy.calledOnce ); // calledTwice and calledThrice are also supported
  8.     // The number of recorded calls.
  9.     equal( spy.callCount, 1 );
  10.     // Directly checking the arguments of the call
  11.     equals( spy.getCall(0).args[0], message );
  12. });

Stubs and mocks

SinonJS also supports two other powerful features which are useful to be aware of: stubs and mocks. Both stubs and mocks implement all of the features of the spy API, but have some added functionality.

Stubs

A stub allows us to replace any existing behaviour for a specific method with something else. They can be very useful for simulating exceptions and are most often used to write test cases when certain dependencies of your code-base may not yet be written.

Let us briefly re-explore our Backbone Todo application, which contained a Todo model and a TodoList collection. For the purpose of this walkthrough, we want to isolate our TodoList collection and fake the Todo model to test how adding new models might behave.

We can pretend that the models have yet to be written just to demonstrate how stubbing might be carried out. A shell collection just containing a reference to the model to be used might look like this:

  1. var TodoList = Backbone.Collection.extend({
  2.     model: Todo
  3. });
  4. // Let’s assume our instance of this collection is
  5. this.todoList;

Assuming our collection is instantiating new models itself, it’s necessary for us to stub the models constructor function for the the test. This can be done by creating a simple stub as follows:

  1. this.todoStub = sinon.stub( window, “Todo” );

The above creates a stub of the Todo method on the window object. When stubbing a persistent object, it’s necessary to restore it to its original state. This can be done in ateardown() as follows:

  1. this.todoStub.restore();

After this, we need to alter what the constructor returns, which can be efficiently done using a plain Backbone.Model constructor. Whilst this isn’t a Todo model, it does still provide us an actual Backbone model.

  1. teardown: function() {
  2.     this.todoStub = sinon.stub( window, “Todo” );
  3.     this.model = new Backbone.Model({
  4.       id: 2,
  5.       title: “Hello world”
  6.     });
  7.     this.todoStub.returns( this.model );
  8. });

The expectation here might be that this snippet would ensure our TodoList collection always instantiates a stubbed Todo model, but because a reference to the model in the collection is already present, we need to reset the model property of our collection as follows:

  1. this.todoList.model = Todo;

The result of this is that when our TodoList collection instantiates new Todo models, it will return our plain Backbone model instance as desired. This allows us to write a spec for testing the addition of new model literals as follows:

  1. module( “Should function when instantiated with model literals”, {
  2.   setup:function() {
  3.     this.todoStub = sinon.stub(window, “Todo”);
  4.     this.model = new Backbone.Model({
  5.       id: 2,
  6.       title: “Hello world”
  7.     });
  8.     this.todoStub.returns(this.model);
  9.     this.todos = new TodoList();
  10.     // Let’s reset the relationship to use a stub
  11.     this.todos.model = Todo;
  12.     this.todos.add({
  13.       id: 2,
  14.       title: “Hello world”
  15.     });
  16.   },
  17.   teardown: function() {
  18.     this.todoStub.restore();
  19.   }
  20. });
  21. test(“should add a model”, function() {
  22.     equal( this.todos.length, 1 );
  23. });
  24. test(“should find a model by id”, function() {
  25.     equal( this.todos.get(5).get(“id”), 5 );
  26.   });
  27. });

Mocks

Mocks are effectively the same as stubs, however they mock a complete API out and have some built-in expectations for how they should be used. The difference between a mock and a spy is that as the expectations for their use are pre-defined, it will fail if any of these are not met.

Here’s a snippet with sample usage of a mock based on PubSubJS. Here, we have aclearTodo() method as a callback and use mocks to verify its behavior.

  1. test(“should call all subscribers when exceptions”, function () {
  2.     var myAPI = { clearTodo: function () {} };
  3.     var spy = this.spy();
  4.     var mock = this.mock( myAPI );
  5.     mock.expects( “clearTodo” ).once().throws();
  6.     PubSub.subscribe( “message”, myAPI.clearTodo );
  7.     PubSub.subscribe( “message”, spy );
  8.     PubSub.publishSync( “message”, undefined );
  9.     mock.verify();
  10.     ok( spy.calledOnce );
  11. });

Practical

We can now begin writing test specs for our Todo application, which are listed and separated by component (e.g Models, Collections, Views, Routers). It’s useful to pay attention to the name of the test, the logic being tested and most importantly the assertions being made as this will give you some insight into how what we’ve learned can be applied to a complete application.

To get the most out of this section, I recommend grabbinghttps://github.com/addyosmani/backbone-koans-qunit – this is a port of the Backbone.js Jasmine Koans over to QUnit that I converted for this post.

In case you haven’t had a chance to try out one of the Koans kits as yet, they are a set of unit tests using a specific testing framework that both demonstrate how a set of specs for an application may be written, but also leave some tests unfilled so that you can complete them as an exercise.

Models

For our models we want to at minimum test that:

  • New instances can be created with the expected default values
  • Attributes can be set and retrieved correctly
  • Changes to state correctly fire off custom events where needed
  • Validation rules are correctly enforced
  1. module( “About Backbone.Model”);
  2. test(“Can be created with default values for its attributes.”, function() {
  3.     expect( 1 );
  4.     var todo = new Todo();
  5.     equal( todo.get(“text”), “” );
  6. });
  7. test(“Will set attributes on the model instance when created.”, function() {
  8.     expect( 3 );
  9.     var todo = new Todo( { text: “Get oil change for car.” } );
  10.     equal( todo.get(“text”), “Get oil change for car.” );
  11.     equal( todo.get(“done”), false );
  12.     equal( todo.get(“order”), 0 );
  13. });
  14. test(“Will call a custom initialize function on the model instance when created.”, function() {
  15.     expect( 1 );
  16.     var toot = new Todo({ text: “Stop monkeys from throwing their own crap!” });
  17.     equal( toot.get(“text”), “Stop monkeys from throwing their own rainbows!” );
  18. });
  19. test(“Fires a custom event when the state changes.”, function() {
  20.     expect( 1 );
  21.     var spy = this.spy();
  22.     var todo = new Todo();
  23.     todo.bind( “change”, spy );
  24.     // How would you update a property on the todo here?
  25.     // Hint: http://documentcloud.github.com/backbone/#Model-set
  26.     todo.set( { text: “new text” } );
  27.     ok( spy.calledOnce, “A change event callback was correctly triggered” );
  28. });
  29. test(“Can contain custom validation rules, and will trigger an error event on failed validation.”, function() {
  30.     expect( 3 );
  31.     var errorCallback = this.spy();
  32.     var todo = new Todo();
  33.     todo.bind(“error”, errorCallback);
  34.     // What would you need to set on the todo properties to cause validation to fail?
  35.     todo.set( { done: “not a boolean” } );
  36.     ok( errorCallback.called, “A failed validation correctly triggered an error” );
  37.     notEqual( errorCallback.getCall(0), undefined );
  38.     equal( errorCallback.getCall(0).args[1], “Todo.done must be a boolean value.” );
  39. });

Collections

For our collection we’ll want to test that:

  • New model instances can be added as both objects and arrays
  • Changes to models result in any necessary custom events being fired
  • url property for defining the URL structure for models is correctly defined
  1. module( “About Backbone.Collection”);
  2. test( “Can add Model instances as objects and arrays.”, function() {
  3.     expect( 3 );
  4.     var todos = new TodoList();
  5.     equal( todos.length, 0 );
  6.     todos.add( { text: “Clean the kitchen” } );
  7.     equal( todos.length, 1 );
  8.     todos.add([
  9.         { text: “Do the laundry”, done: true },
  10.         { text: “Go to the gym” }
  11.     ]);
  12.     equal( todos.length, 3 );
  13. });
  14. test( “Can have a url property to define the basic url structure for all contained models.”, function() {
  15.     expect( 1 );
  16.     var todos = new TodoList();
  17.     equal( todos.url, “/todos/” );
  18. });
  19. test(“Fires custom named events when the models change.”, function() {
  20.     expect(2);
  21.     var todos = new TodoList();
  22.     var addModelCallback = this.spy();
  23.     var removeModelCallback = this.spy();
  24.     todos.bind( “add”, addModelCallback );
  25.     todos.bind( “remove”, removeModelCallback );
  26.     // How would you get the “add” event to trigger?
  27.     todos.add( {text:”New todo”} );
  28.     ok( addModelCallback.called );
  29.     // How would you get the “remove” callback to trigger?
  30.     todos.remove( todos.last() );
  31.     ok( removeModelCallback.called );
  32. });

Views

For our views we want to ensure:

  • They are being correctly tied to a DOM element when created
  • They can render, after which the DOM representation of the view should be visible
  • They support wiring up view methods to DOM elements

One could also take this further and test that user interactions with the view correctly result in any models that need to be changed being updated correctly.

  1. module( “About Backbone.View”, {
  2.     setup: function() {
  3.         $(“body”).append(“<ul id=”todoList”></ul>”);
  4.         this.todoView = new TodoView({ model: new Todo() });
  5.     },
  6.     teardown: function() {
  7.         this.todoView.remove();
  8.         $(“#todoList”).remove();
  9.     }
  10. });
  11. test(“Should be tied to a DOM element when created, based off the property provided.”, function() {
  12.     expect( 1 );
  13.     equal( this.todoView.el.tagName.toLowerCase(), “li” );
  14. });
  15. test(“Is backed by a model instance, which provides the data.”, function() {
  16.     expect( 2 );
  17.     notEqual( this.todoView.model, undefined );
  18.     equal( this.todoView.model.get(“done”), false );
  19. });
  20. test(“Can render, after which the DOM representation of the view will be visible.”, function() {
  21.    this.todoView.render();
  22.     // Hint: render() just builds the DOM representation of the view, but doesn’t insert it into the DOM.
  23.     //       How would you append it to the ul#todoList?
  24.     //       How do you access the view’s DOM representation?
  25.     //
  26.     // Hint: http://documentcloud.github.com/backbone/#View-el
  27.     $(“ul#todoList”).append(this.todoView.el);
  28.     equal($(“#todoList”).find(“li”).length, 1);
  29. });
  30. asyncTest( “Can wire up view methods to DOM elements.” , function() {
  31.     expect( 2 );
  32.     var viewElt;
  33.     $(“#todoList”).append( this.todoView.render().el );
  34.     setTimeout(function() {
  35.         viewElt = $(“#todoList li input.check”).filter(“:first”);
  36.         equal( viewElt.length > 0, true);
  37.         // Make sure that QUnit knows we can continue
  38.         start();
  39.     }, 1000, “Expected DOM Elt to exist”);
  40.     // Hint: How would you trigger the view, via a DOM Event, to toggle the “done” status.
  41.     //       (See todos.js line 70, where the events hash is defined.)
  42.     //
  43.     // Hint: http://api.jquery.com/click
  44.     $(“#todoList li input.check”).click();
  45.     expect( this.todoView.model.get(“done”), true );
  46. });

Events

For events, we may want to test a few different use cases:

  • Extending plain objects to support custom events
  • Binding and triggering custom events on objects
  • Passing along arguments to callbacks when events are triggered
  • Binding a passed context to an event callback
  • Removing custom events

and a few others that will be detailed in our module below:

  1. module( “About Backbone.Events”, {
  2.     setup: function() {
  3.         this.obj = {};
  4.         _.extend( this.obj, Backbone.Events );
  5.         this.obj.unbind(); // remove all custom events before each spec is run.
  6.     }
  7. });
  8. test( “Can extend JavaScript objects to support custom events.”, function() {
  9.     expect( 3 );
  10.     var basicObject = {};
  11.     // How would you give basicObject these functions?
  12.     // Hint: http://documentcloud.github.com/backbone/#Events
  13.     _.extend( basicObject, Backbone.Events );
  14.     equal( typeof basicObject.bind, “function” );
  15.     equal( typeof basicObject.unbind, “function” );
  16.     equal( typeof basicObject.trigger, “function” );
  17. });
  18. test( “Allows us to bind and trigger custom named events on an object.”, function() {
  19.     expect( 1 );
  20.     var callback = this.spy();
  21.     this.obj.bind( “basic event”, callback );
  22.     this.obj.trigger( “basic event” );
  23.     // How would you cause the callback for this custom event to be called?
  24.     ok( callback.called );
  25. });
  26. test( “Also passes along any arguments to the callback when an event is triggered.”, function() {
  27.     expect( 1 );
  28.     var passedArgs = [];
  29.     this.obj.bind(“some event”, function() {
  30.             [].push.apply( passedArgs, arguments );
  31.     });
  32.     this.obj.trigger( “some event”, “arg1”, “arg2” );
  33.     deepEqual( passedArgs, [“arg1”, “arg2”] );
  34. });
  35. test( “Can also bind the passed context to the event callback.”, function() {
  36.     expect( 1 );
  37.     var foo = { color: “blue” };
  38.     var changeColor = function() {
  39.         this.color = “red”;
  40.     };
  41.     // How would you get “this.color” to refer to “foo” in the changeColor function?
  42.     this.obj.bind( “an event”, changeColor, foo );
  43.     this.obj.trigger( “an event” );
  44.     equal( foo.color, “red” );
  45. });
  46. test( “Uses “all” as a special event name to capture all events bound to the object.” , function() {
  47.     expect( 2 );
  48.     var callback = this.spy();
  49.     this.obj.bind( “all”, callback );
  50.     this.obj.trigger( “custom event 1” );
  51.     this.obj.trigger( “custom event 2” );
  52.     equal( callback.callCount, 2 );
  53.     equal( callback.getCall(0).args[0], “custom event 1” );
  54. });
  55. test( “Also can remove custom events from objects.”, function() {
  56.     expect( 5 );
  57.     var spy1 = this.spy();
  58.     var spy2 = this.spy();
  59.     var spy3 = this.spy();
  60.     this.obj.bind( “foo”, spy1 );
  61.     this.obj.bind( “bar”, spy1 );
  62.     this.obj.bind( “foo”, spy2 );
  63.     this.obj.bind( “foo”, spy3 );
  64.     // How do you unbind just a single callback for the event?
  65.     this.obj.unbind( “foo”, spy1 );
  66.     this.obj.trigger( “foo” );
  67.     ok( spy2.called );
  68.     // How do you unbind all callbacks tied to the event with a single method
  69.     this.obj.unbind( “foo” );
  70.     this.obj.trigger( “foo” );
  71.     ok( spy2.callCount, 1 );
  72.     ok( spy2.calledOnce, “Spy 2 called once” );
  73.     ok( spy3.calledOnce, “Spy 3 called once” );
  74.     // How do you unbind all callbacks and events tied to the object with a single method?
  75.     this.obj.unbind( “bar” );
  76.     this.obj.trigger( “bar” );
  77.     equal( spy1.callCount, 0 );
  78. });

App

It can also be useful to write specs for any application bootstrap you may have in place. For the following module, our setup initiates and appends a TodoApp view and we can test anything from local instances of views being correctly defined to application interactions correctly resulting in changes to instances of local collections.

  1. module( “About Backbone Applications” , {
  2.     setup: function() {
  3.         Backbone.localStorageDB = new Store(“testTodos”);
  4.         $(“#qunit-fixture”).append(“<div id=”app”></div>”);
  5.         this.App = new TodoApp({ appendTo: $(“#app”) });
  6.     },
  7.     teardown: function() {
  8.         $(“#app”).remove();
  9.     }
  10. });
  11. test( “Should bootstrap the application by initializing the Collection.”, function() {
  12.     expect( 2 );
  13.     notEqual( this.App.todos, undefined );
  14.     equal( this.App.todos.length, 0 );
  15. });
  16. test( “Should bind Collection events to View creation.” , function() {
  17.       $(“#new-todo”).val( “Foo” );
  18.       $(“#new-todo”).trigger(new $.Event( “keypress”, { keyCode: 13 } ));
  19.       equal( this.App.todos.length, 1 );
  20.  });

Further Reading & Resources

That’s it for this section on testing applications with QUnit and SinonJS. I encourage you to try out the QUnit Backbone.js Koans and see if you can extend some of the examples. For further reading consider looking at some of the additional resources below:

I hope this post was useful. I would like to thank the very clever Andrée Hansson and Sindre Sorhus for their technical reviews. Any corrections or feedback on the post is always welcome as I would like to ensure it’s as accurate as possible. Thanks!

 

Referenced by: http://addyosmani.com/blog/unit-testing-backbone-js-apps-with-qunit-and-sinonjs/

 
Leave a comment

Posted by on May 18, 2012 in Javascript, JQuery

 

ExtJS and ASP.NET MVC 3: CRUD DataGrid

This short tutorial will walk though the implementation of DataGrid using ExtJS 3.3.1 andASP.NET MVC 3. In this tutorial I focus more on the integration of the front-end, so you will not find any database code. Whenever we deal with data we usually create, read/retrieve, update or delete them. ExtJS provides a great data grid component and the ability to perform CRUD operation with very little coding. I used JSON as data format in the example below.

ExtJS DataGridExtJS DataGrid

Let’s start with the server-side by developing the data model and controller.

Controller:

01 public class ContactController : Controller
02 {
03     //
04     // GET: /Contact/
05
06     public ActionResult Index()
07     {
08         return View();
09     }
10
11     public JsonResult Load()
12     {
13         var contact = new List<Contact> {
14             new Contact("Smith","95746325","smith@me.com"),
15             new Contact("Adam","87291034","adam@me.com"),
16             new Contact("Eve","98271345","eve@me.com"),
17             new Contact("Chun Li","81728312","chun.li@me.com")
18         };
19         return Json(new
20         {
21             total = contact.Count,
22             data = contact,
23         }, JsonRequestBehavior.AllowGet);
24     }
25
26     [HttpPost]
27     public JsonResult Create(List<Contact> data)
28     {
29         //insert Create code
30         return Json(new
31         {
32             data = new Contact(data[0].Name, data[0].Phone, data[0].Email),
33             success = true,
34             message = "Create method called successfully"
35         });
36     }
37
38     [HttpPost]
39     public JsonResult Update(List<Contact> data)
40     {
41         //insert Update code
42         return Json(new
43         {
44             success = true,
45             message = "Update method called successfully"
46         });
47     }
48
49     [HttpPost]
50     public JsonResult Delete(List<string> data)
51     {
52         //insert Delete code
53         return Json(new
54         {
55             success = true,
56             message = "Delete method called successfully"
57         });
58     }
59 }

Data Model:

01 public class Contact
02 {
03     public string Id { getset; }
04     public string Name { getset; }
05     public string Phone { getset; }
06     public string Email { getset; }
07
08     public Contact(string pName, string pPhone, string pEmail)
09     {
10         this.Id = System.Guid.NewGuid().ToString();
11         this.Name = pName;
12         this.Phone = pPhone;
13         this.Email = pEmail;
14     }
15
16     public Contact() { }
17 }

ExtJS:

Now, you move on to the view and work on the ExtJS. First, you define the record type which matches the server-side object.

01 var Contact = Ext.data.Record.create([
02     {
03         name: 'Id',
04         type: 'string'
05     }, {
06         name: 'Name',
07         type: 'string'
08     }, {
09         name: 'Phone',
10         type: 'string'
11     }, {
12         name: 'Email',
13         type: 'string'
14     }
15 ]);

Now you need to setup the Writer and Reader

01 var writer = new Ext.data.JsonWriter({
02     encode: false,
03     listful: true,
04     writeAllFields: true
05 });
06
07 var reader = new Ext.data.JsonReader({
08     totalProperty: 'total',
09     successProperty: 'success',
10     idProperty: 'Id',
11     root: 'data',
12     messageProperty: 'message'  // <-- New "messageProperty" meta-data
13 }, Contact);

Then, setup a proxy to define the connection to the controller.

1 var proxy = new Ext.data.HttpProxy({
2     api: {
3         read: '/Contact/Load',
4         create: '/Contact/Create',
5         update: '/Contact/Update',
6         destroy: '/Contact/Delete'
7     },
8     headers: { 'Content-Type''application/json; charset=UTF-8' }
9 });

Hooks the above components (reader, writer, proxy) to the store.

1 var store = new Ext.data.Store({
2     id: 'user',
3     proxy: proxy,
4     reader: reader,
5     writer: writer,
6     autoSave: false
7 });

Add the data grid declaration

01 var grid = new Ext.grid.GridPanel({
02     store: store,
03     columns: [
04         { header: "Name",
05             width: 170,
06             sortable: true,
07             dataIndex: 'Name',
08             editor: {
09                 xtype: 'textfield',
10                 allowBlank: false
11             }
12         },
13         { header: "Phone No.",
14             width: 160,
15             sortable: true,
16             dataIndex: 'Phone',
17             editor: {
18                 xtype: 'textfield',
19                 allowBlank: false
20             }
21         },
22         { header: "EMail",
23             width: 170,
24             sortable: true,
25             dataIndex: 'Email',
26             editor: {
27                 xtype: 'textfield',
28                 allowBlank: false
29             }
30         }
31     ],
32     plugins: [editor],
33     title: 'Contacts DataGrid',
34     height: 300,
35     width: 510,
36     tbar: [{
37         iconCls: 'icon-user-add',
38         text: 'Add Contact',
39         handler: function () {
40             var e = new Contact({
41                 Name: 'New Friend',
42                 Phone: '(65) 89182736',
43                 Email: 'new@google.com'
44             });
45             editor.stopEditing();
46             store.insert(0, e);
47             grid.getView().refresh();
48             grid.getSelectionModel().selectRow(0);
49             editor.startEditing(0);
50         }
51     }, {
52         ref: '../removeBtn',
53         iconCls: 'icon-user-delete',
54         text: 'Remove Contact',
55         handler: function () {
56             editor.stopEditing();
57             var s = grid.getSelectionModel().getSelections();
58             for (var i = 0, r; r = s[i]; i++) {
59                 store.remove(r);
60             }
61         }
62     }, {
63         iconCls: 'icon-user-save',
64         text: 'Save All Modifications',
65         handler: function () {
66             store.save();
67         }
68     }]
69 });

Some observations:

  • When submitting data as JSON, set the headers “Content-Type” to “application/json” in the proxy object and set the encoding to false in the JsonWriter. If not, it will be treated as form submission.
  • Since I set the auto save to false. DataGrid will submit list of contact when there are 2 or more changes, however it will send a single object when there is 1 change. In order to make it consistent to send as list, set list full to true in the JsonWriter.
  • ASP.NET MVC 3 able to recognize the JSON without additional coding.

If you want to see the complete source code, download it from here. Hope it’s useful. :)

Happy hacking!

Referenced by: http://myxaab.wordpress.com/2011/02/20/extjs-and-asp-net-mvc-3-crud-datagrid/

 
1 Comment

Posted by on October 23, 2011 in Javascript, Web Application