Comparing YUI - Dojo - Ext JS - jQuery

Table of contents

Comparing YUI - Dojo - Ext JS - jQuery
Dojo - Down for the Count
Ext JS - Hopeless
jQuery - Best in Show, after a tacky start
YUI's Ajax code - very, very verbose
jQuery's Ajax code using tablesorter - concise is nice
YUI's Tab control - All Perl, and a bit verbose
jQuery's Tab control - HTML and Perl, and concise, again
jQuery - Switching from tablesorter to DataTables
Setting up the Ajax response
References
Author
Copyright

Comparing YUI - Dojo - Ext JS - jQuery

I use YUI V 2.8.1 and V 3.5.1, and here I'm reviewing Dojo V 1.7.2, Ext JS V 4.1.0, jQuery 1.7.2, together with DataTable V 1.9.2.

I've just converted an app (a home-grown search engine) from YUI [1] to jQuery [2], as an experiment to familiarize myself with the latter.

Later, I'll convert it to use Ext JS. Don't you just love git [11]?

There are of course various other JS libraries around, and I am sure experts will let me know about them via the comments feedback mechanism.

My app provides a tab control with 3 tabs, Search, Add and About, which I use to update my wine database, on line at [3].

Searching and adding use Ajax, so it's a simple but good test of a small set of features. The source is structured as MVC, which helps. And, yes, I know some people think MVC is not the best approach. Certainly it has some awkwardness which makes me too question its appropriateness.

Anyway, the output from the search is a table, and the output from the add is a 2-line (header, new data) table too, so I'm interested in the support for tables, but don't need anything too fancy.

Dojo - Down for the Count

At first, I spent a day or so with Dojo [4], but it is still pathetic, documentation-wise - as it was years ago, the last time I did such a comparison [5] - so I deleted all my work and tried jQuery.

Ext JS - Hopeless

Since in this case I'll build the tabs from HTML markup, it's very similar to jQuery.

Their Getting Started page [8], is far too brief for beginners. It falls for the classic mistake of rushing thru the most introductory information, and getting too soon on to running the 'sencha' script to customize your selection of JS code.

Worse, this 'sencha' script is not shipped in the distro. Oh dear. It's a seperate 51 Mb (sic) download, of a beta SDK (Software Development Kit). Even worse, that turns out to be an executable, a big executable, with a '.run' suffix.

Even more ridiculously, apparently you have to download their HTML5 mobile app SDK to get this 'sencha' script. See [9]. At least this is a zip file.

But wait, 'sencha' is not in this download either! I give up, at least on customization. I'll just try to get my app working.

Amongst all this, be sure to read (if not understand) the Missing Docs page [10].

The next problem is that when you unpack the Ext JS distro itself, you find a few Neptune (theme) scripts and 8 (sic) versions of the code.

        -rw-r--r--  1 ron ron 2877916 Apr 20 14:14 ext-all-debug.js
        -rw-r--r--  1 ron ron 5345656 Apr 20 14:14 ext-all-debug-w-comments.js
        -rw-r--r--  1 ron ron 5386270 Apr 20 14:14 ext-all-dev.js
        -rw-r--r--  1 ron ron 1264053 Apr 20 14:14 ext-all.js
        -rw-r--r--  1 ron ron  501463 Apr 20 14:14 ext-debug.js
        -rw-r--r--  1 ron ron  933728 Apr 20 14:14 ext-debug-w-comments.js
        -rw-r--r--  1 ron ron  957176 Apr 20 14:14 ext-dev.js
        -rw-r--r--  1 ron ron  215506 Apr 20 14:14 ext.js

I'm going to use ext-all.js, all 1.2 Mb of it.

Four (4) of these are discussed on the Getting Started page [8].

OK. On to the code.

But now another thing strikes me: If you set up a HTML-style form just with Ext JS code, such as:

    /*
     * Here is where we create the Form
     */
    var gridForm = Ext.create('Ext.form.Panel', { ...

you are eliminating HTML syntax from the forms, and are now committed to proprietry JS code instead. Of course, each JS library does the same thing to some degree, but it's a warning about how far one library can take the process.

Thus this means staff need a specific type of training before being able to work on such code, in the sense you're not reading and writing HTML as such.

You can read about some of the claimed advantages here.

I must admit though, there is a way of combining Ext JS-specific code with HTML-style CGI form fields, by using the 'contentEl' property, to link a Panel field to a pre-existing CGI form field. This then means only part of your code is proprietry. Your call.

Eventually I gave up, and just did 'git reset --hard', to eliminate Ext JS. And what a relief that was.

So, jQuery it is.

jQuery - Best in Show, after a tacky start

The jQuery docs and examples are not as sophisticated as YUI's, which is a bit frustrating when you're used to YUI, but in the end they sufficed.

E.g., go to docs page at jQuery, and search for 'grid'. The results are peculiar to behold. There is no page called 'grid', and the first within-page hit refers to ... ExtJS! The next line refers to various matters, including 'jqGrid'. So now search for jqGrid and again there is no such page, but there are 5 results for page text hits. Click on the 2nd, 'Plugins', and you get 'The plugins site is currently in development'.

Or go to the tutorials page, and search for grid. The first hit is 'My First ExtJS DataGrid' (as above). But click on that and you get 'Sorry There are no blog entries available that match your criteria.'

Anyway, while searching for how to convert from YUI's DataTable to jQuery's equivalent, I came across a (old) posting referring to tablesorter [7], a jQuery add-on, which I tried. tablesorter has built-in support for a number of datatypes. I could not find any documentation for them, but you can find them very close to the end of the source code. The code struggled with my simple data, so I had to use 12 non-breaking spaces to pad out the heading row of the output, but in the end it worked well enough, but I'd be reluctant to use (this particular) tablesorter for any work. See below for a much more sophisticated solution from DataTables.net.

A sample result, output by jQuery, is here.

YUI's Ajax code - very, very verbose

One striking and unfortunate feature of YUI is the quantity of code required to run an Ajax request.

        function prepare_search_form()
        {
                YUI().use
                (
                        "datatable", "gallery-formmgr", "io-base", "io-form", "json", "node", function(Y)
                        {
                                var f = new Y.FormManager("search_form",
                                  {
                                  });

                                f.prepareForm();

                                var success_fn = function(ioId, o)
                                {
                                        if (o.responseText !== undefined)
                                        {
                                                try
                                                {
                                                        var data = Y.JSON.parse(o.responseText);
                                                        var cols =
                                                                [
                                                                        {key: "style_name",      label: "Style"},
                                                                        {key: "grape_name",      label: "Grape"},
                                                                        {key: "wine_maker_name", label: "Winemaker"},
                                                                        {key: "vineyard_name",   label: "Vineyard"},
                                                                        {key: "comment",         label: "Comment"},
                                                                        {key: "vintage",         label: "Vintage"},
                                                                        {key: "rating",          label: "Rating"}
                                                                ];
                                                        div.set("innerHTML", "");
                                                        var table = new Y.DataTable.Base
                                                        ({
                                                                columnset: cols,
                                                                recordset: data.results
                                                        }).render("#search_result_div");
                                                }
                                                catch(e)
                                                {
                                                        alert("The server's reply is not in JSON format");
                                                }
                                        }
                                        else
                                        {
                                                div.set("innerHTML", "The server's response is incomprehensible");
                                        }
                                };
                                var failure_fn = function(ioId, o)
                                {
                                        div.set("innerHTML", "The server failed to respond");
                                };

                                Y.on("click", function()
                                 {
                                         f.populateForm();
                                 }, "#reset_search");
                                Y.on("click", function()
                                 {
                                         if (FIC_checkForm("search_form") == false)
                                         {
                                                 return false;
                                         }

                                         var cfg =
                                                 {
                                                         form:
                                                         {
                                                                 id: "search_form"
                                                         },
                                                         method: "POST",
                                                         on:
                                                         {
                                                                 success: success_fn,
                                                                 failure: failure_fn
                                                         },
                                                         sync: true
                                                 };
                                         var request = Y.io("/Search", cfg);
                                 }, "#submit_search");

                                var div = Y.one("#search_result_div");
                        }
                );
        }

jQuery's Ajax code using tablesorter - concise is nice

The jQuery equivalent (without proper error-checking) is:

        function submit_search()
        {
                $.ajax
                ({
                        data: {search_name: document.search_form.search_name.value},
                        dataType: "html",
                        type: "POST",
                        url:  "<: $self_url :>/Search" // Syntax as per Text::Xslate.
                }).success(function(html)
                {
                        $('#search_result_div').empty().append(html);
                        $("#search_result").tablesorter();
                });
        }

        $(document).ready(function()
        {
                $("#tabs").tabs();

                $("#search_form").submit(function()
                {
                        submit_search();
                        return false;
                });

        });

Notes for the beginner:

(1) $(document).ready(function(){...}) is executed upon page load.

The call to tabs() actually builds the tab control from HTML.

I also use ready() to attach a JS function to the submit button. That's the sort of thing that is so frustratingly not mentioned in the deficient jQuery docs re Ajax. It's listed of course under form handling.

I did not implement the current code's Add functionality yet.

(2) <: $x :> is syntax for my choice of templater, Text::Xslate.

Back to jQuery.

YUI's Tab control - All Perl, and a bit verbose

Of course, some very small changes where made to the CGI form itself, and the View's display logic (in formatting the output), to accomodate all this, but really, the difference in JS code is sad, and almost distressing.

Now for the tab control stuff. Firstly, YUI:

        my($about_html)  = $self -> build_about_html;
        my($add_html)    = $self -> param('view') -> add -> build_add_html;
        my($search_html) = $self -> param('view') -> search -> build_search_html;
        my($head_init)   = <<EJS;

        YUI().use('node-base', 'tabview', function(Y)
        {
                function init()
                {
                        var tabview = new Y.TabView
                                ({
                                  children:
                                        [
                                         {
                                           label:   'Search',
                                           content: '$search_html'
                                         },
                                         {
                                           label:   'Add',
                                           content: '$add_html'
                                         },
                                         {
                                           label:   'Help',
                                           content: '$about_html'
                                         }
                                        ]
                                 });

                        tabview.render('#tabs');
                        tabview.on
                                ('selectionChange', function(e)
                                 {
                                         var label = e.newVal.get('label');

                                         if (label === "Search")
                                         {
                                                 make_search_name_focus(); # Doesn't work with YUI 3.
                                         }
                                         else if (label === "Add")
                                         {
                                                 make_wine_maker_name_focus(); # Doesn't work with YUI 3.
                                         }
                                 }
                                );
                        make_search_name_focus();
                        prepare_add_form();
                        prepare_search_form(); # See above.
                }

                Y.on("domready", init);
        });

        EJS

jQuery's Tab control - HTML and Perl, and concise, again

The roughly equivalent code in jQuery is this HTML:

        <div id="tabs">
            <ul>
                <li><a href="#fragment-1"><span>Search</span></a></li>
                <li><a href="#fragment-2"><span>Add</span></a></li>
                <li><a href="#fragment-3"><span>About</span></a></li>
            </ul>
            <div id="fragment-1">
                        <: $html4search :>
            </div>
            <div id="fragment-2">
                        <: $html4add :>
            </div>
            <div id="fragment-3">
                        <: $html4about :>
            </div>
        </div>

And this Perl:

        my($config) = $self -> param('config');
        my($param)  =
        {
        html4about  => mark_raw($self -> build_about_html),
        html4add    => mark_raw($self -> param('view') -> add -> build_add_html),
        html4search => mark_raw($self -> param('view') -> search -> build_search_html($self -> query) ),
        };

        return $self -> param('templater') -> render('web.page.tx', $param);

Note: mark_raw() is a function exported from Text::Xslate, which stops the renderer escaping various stuff.

Of course, the <head> of the web page must be populated with a set of URLs for various CSS and JS components, but that's trivial.

jQuery - Switching from tablesorter to DataTables

As indicated, I was disappointed with tablesorter, so I kept searching and discovered DataTables.net [12]. Again, the jQuery docs are deficient in not making it much, much simpler for beginners to find such information.

Anyway, the download of DataTables includes jQuery V 1.7.1, with V 1.7.2 downloadable directly from the jQuery site. I decided to use the version shipped with DataTables. Also, since I was using tabs in this app, I also needed the jQuery UI download.

Now, jQuery V 1.7.2 is included in the jQuery UI download, so you could link to that, which has the advantage of being a minimized version. Or, perhaps, you could copy jquery-1.7.2.min.js into the DataTables dir, overwriting the file jquery.js in media/js. Fun for all ages awaits you (I hope). I didn't try any of that.

DataTables comes with various demo scripts, so I adapted the simplest, which is what you can see at [3].

To get started I've unzipped the downloads DataTables-1.9.2.zip and jquery-ui-1.8.21.custom.zip and copied the 2 resultant directory structures into /assets/js/ within the directory which is the doc root of my web server. Simple.

The code needed in the <head> of your static web page is:

        <style type="text/css" title="currentStyle">
                @import "/assets/js/DataTables-1.9.2/media/css/demo_page.css";
                @import "/assets/js/DataTables-1.9.2/media/css/demo_table.css";
        </style>

        <link rel="stylesheet" type="text/css" href="/assets/css/local/wines/fancy.table.css">
        <link rel="stylesheet" type="text/css" href="/assets/js/jquery-ui-1.8.21/css/flick/jquery-ui-1.8.21.custom.css">
        <link rel="stylesheet" type="text/css" href="/assets/css/validator/validator.css">
        <link rel="stylesheet" type="text/css" href="/assets/css/local/wines/web.page.css">

        <script type="text/javascript" src="/assets/js/DataTables-1.9.2/media/js/jquery.js"></script>
        <script type="text/javascript" src="/assets/js/jquery-ui-1.8.21/js/jquery-ui-1.8.21.custom.min.js"></script>
        <script type="text/javascript" src="/assets/js/DataTables-1.9.2/media/js/jquery.dataTables.min.js"></script>
        <script type="text/javascript" src="/assets/js/validator/validator.js"></script>

        <script type="text/javascript">

        function submit_search()
        {
                $.ajax
                ({
                        data: {search_name: document.search_form.search_name.value},
                        dataType: "html",
                        type: "POST",
                        url:  "http://127.0.0.1:5020/Search"
                }).success(function(html)
                {
                        $("#search_result_div").empty().append(html);
                        $("#example").dataTable();
                });
        }

        $(document).ready(function()
        {
                $("#tabs").tabs();

                $("#search_form").submit(function()
                {
                        submit_search();
                        return false;
                });

        });

        </script>

Big warning: The script link to jquery.dataTables.min.js absolutely must come after the link to jquery.js.

Notes:

o The '#example' refers to the id of the table below.
o This includes jQuery UI for the tab stuff
o validator.* is an independent package
o demo_page.css and demo_table.css are from the jQuery demo

The simplicity of the Ajax calling code compared to YUI's must be mentioned again, to the latter's detriment.

Setting up the Ajax response

Within the body of the web page, the HTML table will end up as:

        <div id="dt_example">
        <div id="container">
        <table class="display" id="example" cellpadding="0" cellspacing="0" border="0" width="100%">
                <thead>
                        <tr>
                          <td>#</td>
                          <td>Wine maker</td>
                          <td>Vineyard</td>
                          <td>Style</td>
                          <td>Grape</td>
                          <td>Comment</td>
                          <td>Vintage</td>
                          <td>Rating</td>
                          <td>Reviewed</td>
                        </tr>
                </thead>
                        <tr>
                        <td>1</td>
                        <td>3 Rings</td>
                        <td>Eastwood SA</td>
                        <td>-</td>
                        <td>Shiraz</td>
                        <td>-</td>
                        <td>2006</td>
                        <td>4</td>
                        <td>2011-03-08</td>
                        </tr>
                        ...
                <tfoot>
                        <tr>
                          <td>Wine maker</td>
                          <td>Vineyard</td>
                          <td>Style</td>
                          <td>Grape</td>
                          <td>Comment</td>
                          <td>Vintage</td>
                          <td>Rating</td>
                          <td>Reviewed</td>
                        </tr>
                </tfoot>
        </table>
        </div>
        </div>

The <tfoot> is optional.

Within the Perl, it's just a matter of outputting the table:

                my($html) = <<EOS;
        <div id="dt_example">
        <div id="container">
        <table class="display" id="example" cellpadding="0" cellspacing="0" border="0" width="100%">
        <thead>
        <tr>
                <th>Winemaker</th>
                <th>Vineyard</th>
                <th>Style</th>
                <th>Grape</th>
                <th>Vintage</th>
                <th>Rating</th>
                <th>Review date</th>
        </tr>
        </thead>
        <tbody>
        EOS
                my($count) = 0;

                my($class);

                for my $row (@$result) # Search result, that is.
                {
                        $count++;

                        $class = ($count % 2 == 1) ? 'odd gradeC' : 'even gradeC';

                        $html .= <<EOS;
                <tr class="$class">
                <td>$$row{wine_maker_name}</td>
                <td>$$row{vineyard_name}</td>
                <td>$$row{style_name}</td>
                <td>$$row{grape_name}</td>
                <td>$$row{vintage}</td>
                <td>$$row{rating}</td>
                <td>$$row{review_date}</td>
                </tr>
        EOS
                }

                $html .= <<EOS;
        </tbody>
        <tfoot>
        <tr>
                <th>Winemaker</th>
                <th>Vineyard</th>
                <th>Style</th>
                <th>Grape</th>
                <th>Vintage</th>
                <th>Rating</th>
                <th>Review date</th>
        </tr>
        </tfoot>
        </table>
        </div>
        </div>
        EOS

                return $html;

And that's it! I am very, very impressed.

What the means is that there is code within jQuery for unpacking the Ajax response and formatting it, just using the table structure, whereas with YUI you have to specify these details yourself.

Along the way I was able to delete search.js, which I criticised above as being 'very, very verbose'. See the code starting with:

        function prepare_search_form()

There is simply no code I have to write for jQuery to replace all the stuff needed for YUI.

References

[1] http://yuilibrary.com/

[2] http://jquery.com/

[3] http://savage.net.au/Wine.html

[4] http://dojotoolkit.org/

[5] I searched use.perl.org, trying to find the link, but failed.

[6] http://www.sencha.com/products/extjs/

[7] http://tablesorter.com/docs/

[8] http://docs.sencha.com/ext-js/4-0/#/guide/getting_started

[9] http://docs.sencha.com/touch/2-0/#!/guide/command

[10] http://www.sencha.com/forum/showthread.php?203663-Sencha-SDK-Tools-2.0-and-ExtJS4-The-Missing-Docs

[11] http://git-scm.com/

[12] http://datatables.net/

Author

The article was written by Ron Savage in 2012.

Home page: http://savage.net.au/index.html.

Copyright

Australian copyright © 2012, Ron Savage. All Programs of mine are 'OSI Certified Open Source Software'; you can redistribute them and/or modify them under the terms of The Artistic License, a copy of which is available at: http://www.opensource.org/licenses/index.html