2
\$\begingroup\$

I have written a JS library (think, jQuery, but with much much less features, and targeted for newer browsers on mobile). This library provides an extension mechanism. One of the ways the extension can be defined is:

$.extension("extname", function(options) {
    this.forEach(function(elem) {
        // do something
    });
    return this; // for chaining.
});

This can be later used like so:

$("#myId").extname({various: "options"});

Using this mechanism I've written UI widgets (as extensions). For UI widgets that have state I don't want to have chaining like jQuery does. I want widget objects with various methods. I am aware (although I'm not too sure) that jQuery UI uses a different pattern for UI widgets.

e.g. In jQuery, if you have an accordion widget, then you have to use it like so (example only, not actual widget):

$("#acc").accordion({various: "options"}).accordion(
    "collapse", 0).accordion("expand", 1); //etc.

Although I like chaining and use it in various cases, I am not particularly a fan of this style when it comes to stateful widgets.

I would like widgets to be used like so:

var acc = $("#acc").accordion(options);
acc.collapse(0).expand(1);
acc.getExpanded(); // etc.

I've written a data list widget using the extension mechanism above in the following way:

(function($, undefined) {
    var defaults = {
        // various defaults for list widget   
    },
    extend = $.extend,
    other, variables;

    // these functions don't depend or modify any state of the widget
    function thoseNotDependingOnState() {
    }

    $.extension("datalist", function(options) {
        var opts = extend({}, defaults, options),
            data = [], // data for this list
            self = this,
            widget,
            privateVars_maintaining_state;
       // these functions work on private state of the widget
       function someFunctionDependingOnPrivateState() {}

        widget = {
            getItems: function() {},
            selectItemAt: function(index)  {},
            addItem: function(itm) {}
            // etc.
        };
        return widget;
    });
})(h5);

Since there's a chance that some widgets will be used a lot, resulting the call of the extension function, re-defining functions (private) everytime a new widget is created does not make sense(?), so I'm planning to do this in the following way. Here the bulk of the behaviour is defined in the prototype. The wrapper is only a lightweight object that delegates all the calls to the underlying widget and exposes only the widget's public API. The convention followed here is that all the private members of the widgets have names starting with an '_' character.

(function($, undefined) {
    var defaults = {
        // various defaults for list widget   
    },
    extend = $.extend,
    other, variables,
    widgetProto; // This is the prototype object 
                 // from which all the list widgets inherit.

    function thoseNotDependingOnState() {
    }


    widgetProto = {
        /* ----------------- Private variables ---------------------- */
        _data: [],
        _element: null, // ui element associated with this list
        _allItems: [],

        /* ----------------- Private functions ---------------------- */
        /* Initialize the list from options passed to factory */
        _init: function(element, options) {
        },
        _createListItem: function() {},
        // other private functions

        /* -------------------- Public Api --------------------------- */
        getItems: function() {},
        getItemAt: function(i) {},
        addItem: function() {}
        // other public functions
    };

    $.extension("datalist", function(options) {
        var widget = Object.create(widgetProto), widgetWrapper = {};
        // initialize the widget, passing the dom wrapper
        widget._init(this, $.extend({}, defaults, options));

        // prepare the wrapper
        $.forEach(widget, function(val, prop) {
         var property;
         // expose only public API, i.e all properties 
         // not starting with an '_'
         if(prop.indexOf("_") !== 0) {
            if(typeof val === "function") {
               widgetWrapper[prop] = function() {
                  return val.apply(widget, arguments);
               }
            }else {
               property = capitalize(prop);
               widgetWrapper["get" + property] = function() {
                  return widget[prop];
               };
               widgetWrapper["set" + property] = function(v) {
                  widget[prop] = v;
               };
            }
         }
      });
      return widgetWrapper;
    });
})(h5);

With all this, I have two questions:

  1. Do you think its a good idea to do away with chaining pattern for widgets?

  2. Of the above two approaches, does the second one provide any advantages in terms of performance (memory or otherwise)?

\$\endgroup\$
  • \$\begingroup\$ You say you want to do away with chaining, but your example, acc.collapse(0).expand(1); uses chaining. Also, you may want to read this part of Addy Osmani's book about JavaScript Patterns: addyosmani.com/resources/essentialjsdesignpatterns/book/… \$\endgroup\$ – Heretic Monkey Aug 15 '12 at 16:14
  • \$\begingroup\$ @Mike, what I meant was, I didn't want to have jQuery-like chaining e.g. $.accordion("expand", 1).accordion("collapse", 3), etc. But I'm okay with chaining where it makes sense in method calls, like $("selector").accordion({}).expand(1).collapse(3). Btw Thanks for the book link :) \$\endgroup\$ – naikus Nov 13 '12 at 15:36
2
\$\begingroup\$

@naikus, Bill Barry's point is rather accurate -- chainable methods are for convenience rather than convention. You can structure your method calls either way, though the chaining helps with expressiveness in your code (readability is up to the developer.)

So, no--I wouldn't do away with chaining unless you absolutely want or need to for some reason.

As for your second question, I'd be in favour of your first approach for a couple reasons:

  1. Legibility -- far easier to read, and honestly, far more flexible.
  2. Maintainability -- the pattern can be extended to make use of a factory if you want, and still have the benefits of being legible and maintainable.

As for your actual widgets, I think you might need to spend a little more time looking at implementations of the JS Design patterns you're using--and not because what you have is wrong, but I found it really cumbersome to follow. Here's a couple sources that really helped me when I was developing my own system:

  1. http://www.klauskomenda.com/code/javascript-programming-patterns/#module
  2. http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

jQuery's plugin system is quite flexible. It's use of a module pattern gives developers the ability to declare public and private members/methods. My own module system is heavily inspired by it:

(function( o ){

    // +------------------------------------------------------------------------+
    // MEMBERS (PRIVATE)
    // +------------------------------------------------------------------------+
    var parent  = this;
    var $       = {};

    // +------------------------------------------------------------------------+
    // METHODS (PRIVATE)
    // +------------------------------------------------------------------------+
    // Inject dependencies
    function init( jQuery, ModuleNamespace )
    {
        $       = jQuery;
        parent  = ModuleNamespace;

        console.log( 'READY: BaseModule::init()', $, parent );  

    }

    function __notify( strEvent, objData )
    {
        switch( strEvent )
        {
            case 'onReady' :
            case 'onModuleReady' :
                init( objData.jQuery, objData.ModuleNamespace );
                break;
            default :
                break;
        }
    }

    // +------------------------------------------------------------------------+
    // METHODS (PRIVATE)
    // +------------------------------------------------------------------------+

    // +------------------------------------------------------------------------+
    // METHODS (PUBLIC)
    // +------------------------------------------------------------------------+
    o.notify    = __notify; // Expose private methods

    // Broadcast when this module is loaded
    ModuleNamespace.EventDispatcher.notify( 'onModuleReady', o );

    return o;

})( window.ModuleNamespace.BaseModule = window.ModuleNamespace.BaseModule || {} );

This base module is my starting point for my plugins. ModuleNamespace is a reference to a parent object, e.g. window.ModuleNamespace to keep things as self contained as possible. The example module illustrates dependency injection (jQuery and the ModuleNamespace objects), which I fall back to when dependencies like jQuery are already a mainstay in the project; however, you could limit this to simply, ModuleNamespace. I also make use of an EventDispatcher module (observer pattern) to deal global state handling, but bubbling up events through callbacks etc, is still possible.

It's easy enough to create a Factory method or object similar to jQuery UI's widget factory, or to go the common plugin route and extend.

All this to say, I would favour a simpler approach to encapsulation and extendability.

\$\endgroup\$
0
\$\begingroup\$

Are you aware that you can use JQueryUI widgets like this:

var acc = $('#acc').accordion(options).data('accordion');
acc.collapse(0);
acc.expand(1);
acc.getExpanded(); // etc.
\$\endgroup\$
  • \$\begingroup\$ Yes Bill, but doesn't the accordion object 'acc' has all the private properties and functions too? I think we can call acc._insertItem(). This is what I don't want. \$\endgroup\$ – naikus Jul 16 '12 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.