Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a good practice to store jquery plugin configuration in data?

I want to create jQuery plugin with config (for example plugin myplugin). Than call $(elem).myplugin(config); After that I want to call methods from this plugin like $(elem).myplugin().method() with already stored config.

My offer is something like that:

(function($) {
    $.fn.myplugin = function(options) { 
        var $this = $(this);

        var getOptions = function() {
            return $this.data('myplugin');
        };

        var initOptions = function(opt) {
            $this.data('myplugin', opt);
        };

        var setOption = function(key, value) {
            $this.data('myplugin')[key] = value;
        }

        var updateBorderWidth = function() {  
            $this.css('border-width', 
                      getOptions().borderWidth * getOptions().coeficient);
        };

        var init = function(opt) {
            initOptions(opt);
            updateBorderWidth();
        }        

        function changeBorder(width) {            
            setOption('borderWidth', width)
            updateBorderWidth();
        }

        if(options) {
            init(options);            
        }        

        return {
            changeBorder : changeBorder
        };
    }        
})(jQuery);

And usage:

 $(function() {
     var item1 = $('#test1').myplugin({ coeficient: 1, borderWidth: 1 });
     var item1 = $('#test2').myplugin({ coeficient: 2, borderWidth: 1 });

     $('#btn').click(updateBorder);     
});

function updateBorder() {
    $('#test1').myplugin().changeBorder($('#inpt').val());
    $('#test2').myplugin().changeBorder($('#inpt').val());
}

Example: http://jsfiddle.net/inser/zQumX/4/

My question: is it a good practice to do that?

May be it's incorrect approach. Can you offer better solution?

like image 813
inser Avatar asked Mar 21 '13 09:03

inser


2 Answers

Edit:

After searching for threads on jQuery plugin template I found these Boilerplate templates (updated) which are more versatile and extensive designs than what I've offered below. Ultimately what you choose all depends on what your needs are. The Boilerplate templates cover more use cases than my offering, but each has its own benefits and caveats depending on the requirements.


Typically jQuery plugins either return a jQuery object when a value is passed to them as in:

.wrap(html) // returns a jQuery object

or they return a value when no parameter is passed in

.width() // returns a value

.height() // also returns a value

To read your example calling convention:

$('#test1').myplugin().changeBorder($('#inpt').val());

it would appear, to any developer who uses jQuery, as though two separate plugins are being utilized in tandem, first .myplugin() which one would assume will return a jQuery object with some default DOM maniplulation performed on #test1, then followed by .changeBorder($('#inpt').val()) which may also return a jQuery object but in the case of your example the whole line is not assigned to a variable so any return value is not used - again it looks like a DOM manipulation. But your design does not follow the standard calling convention that I've described, so there may be some confusion to anyone looking at your code as to what it actually does if they are not familiar with your plugin.


I have, in the past, considered a similar problem and use case to the one you are describing and I like the idea of having a convenient convention for calling separate functions associated with a plugin. The choice is totally up to you - it is your plugin and you will need to decide based on who will be using it, but the way that I have settled on is to simply pass the name of the function and it's parameters either as a separate .myplugin(name, parameters) or in an object as .myplugin(object).

I typically do it like so:

(function($) {
    $.fn.myplugin = function(fn, o) { // both fn and o are [optional]
        return this.each(function(){ // each() allows you to keep internal data separate for each DOM object that's being manipulated in case the jQuery object (from the original selector that generated this jQuery) is being referenced for later use
            var $this = $(this); // in case $this is referenced in the short cuts
            
            // short cut methods
            if(fn==="method1") {
                if ($this.data("method1"))  // if not initialized method invocation fails
                    $this.data("method1")() // the () invokes the method passing user options
            } else if(fn==="method2") {
                if ($this.data("method2"))
                    $this.data("method2")()
            } else if(fn==="method3") {
                if ($this.data("method3"))
                    $this.data("method3")(o) // passing the user options to the method
            } else if(fn==="destroy") {
                if ($this.data("destroy"))
                    $this.data("destroy")()
            }
            // continue with initial configuration
            
            var _data1,
                _data2,
                _default = { // contains all default parameters for any functions that may be called
                    param1: "value #1",
                    param2: "value #2",
                },
                _options = {
                    param1: (o===undefined) ? _default.param1 : (o.param1===undefined) ? _default.param1 : o.param1,
                    param2: (o===undefined) ? _default.param2 : (o.param2===undefined) ? _default.param2 : o.param2,
                    
                }
                method1 = function(){
                    // do something that requires no parameters
                    return;
                },
                method2 = function(){
                    // do some other thing that requires no parameters
                    return;
                },
                method3 = function(){
                    // does something with param1
                    // _options can be reset from the user options parameter - (o) - from within any of these methods as is done above
                    return;
                },
                initialize = function(){
                    // may or may not use data1, data2, param1 and param2
                    $this
                        .data("method1", method1)
                        .data("method2", method2)
                        .data("method3", method3)
                        .data("destroy", destroy);
                },
                destroy = function(){
                    // be sure to unbind any events that were bound in initialize(), then:
                    $this
                        .removeData("method1", method1)
                        .removeData("method2", method2)
                        .removeData("method3", method3)
                        .removeData("destroy", destroy);
                }
            initialize();
        }) // end of each()
    } // end of function        
})(jQuery);

And the usage:

var $test = $('#test').myplugin(false, {param1: 'first value', param2: 'second value'}); // initializes the object
$test.myplugin('method3', {param1: 'some new value', param2: 'second new value'}); // change some values (method invocation with params)

or you could just say:

$('#test').myplugin(); // assume defaults and initialize the selector
like image 138
Nolo Avatar answered Nov 15 '22 18:11

Nolo


Passing parameters to javascript via data attributes is a great pattern, as it effectively decouples the Javascript code and the server-side code. It also does not have a negative effect on the testability of the Javascript code, which is a side-effect of a lot of other approaches to the problem.

I'd go as far as to say it is the best way for server-side code to communicate with client-side code in a web application.

like image 37
Chris Aitchison Avatar answered Nov 15 '22 16:11

Chris Aitchison