user1032531
user1032531

Reputation: 26281

Create an instance of a jQuery plugin

I have several pages which I wish to allow the the user to inline edit many fields and update the server DB. To implement this, my intent is to create a jQuery plugin which I can do the typical passing of the configuration options and uses ajax to save the results.

(function($){
    var methods = {
        init    : function (options) {return this.each(function () {/* ... */});},
        method1 : function ()        {return this.each(function () {/* ... */});},
        method2 : function ()        {return this.each(function () {/* ... */});}
    };

    $.fn.myEditPlugin= function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));   //Line 10
        } else if (typeof method === 'object' || ! method) {
            return methods.init.apply(this, arguments);                                     //Line 12
        } else {
            $.error('Method ' +  method + ' does not exist on jQuery.myEditPlugin');
        }    
    };
    }(jQuery)
);

For each individual page, there are several options which are common to all (i.e. the url endpoint, the record's primary key, etc) and I would rather not duplicate each when applying the plugin.

Originally, I was just going to define a function on each page which takes some input and applies the common options to each.

function wrapEdit(e,options) {
  options.url='/page1/etc';
  options.pk=document.getElementById('pk').value;
  return $(e).myEditPlugin(options);
}
wrapEdit('.someclass',{foo:123});

It doesn't seem all that professional to me, so in my obsessive quest, thought I would make a class which I could pass the common options to and it would apply the plugin.

class WrapEdit(options)
{
  constructor(options) {
    this.options = options;
  }
  this.applyIndividualOptions=function(e, options) {
     return $(e).myEditPlugin(Object.assign({}, this->options, options));
  }
}

var wrapEdit=new WrapEdit({url: '/page1/etc', pk: document.getElementById('pk').value});
wrapEdit.applyIndividualOptions('.someclass',{foo:123});

Better, but not very jQueryish as I will be passing the select element instead of directly applying the plugin to elements typical of jQuery.

Is it possible to create an instance of a jQuery plugin which keeps previously defined data? Maybe something like the following:

$.myEditPlugin({url: '/page1/etc', pk: document.getElementById('pk').value});
$('.someclass').myEditPlugin({foo:123});  //Will also pass previously defined url and pk to myEditPlugin

Or maybe best to create a custom jQuery plugin per page which just adds the extra options and initiates the real plugin...

$.fn.myEditPluginInstance = function(options) {
    return this.myEditPlugin(Object.assign({url: '/page1/etc', pk: document.getElementById('pk').value}, options));    
};

Upvotes: 0

Views: 162

Answers (1)

Stphane
Stphane

Reputation: 3456

Creating a function to be called against a jquery collection

The basic idea is to define a new property (function) in jQuery.fn, before any call to your plugin is made (In other words, any code related to the application is executed). You can use an "Immediately Invoked Function Expressions" (a.k.a. IIFEs) to fence your plugin API in. Then you have to loop over the collection and execute any code your plugin needs to apply on the collection items.

Basic skeleton:

(function ($) {
    // Enclosed scope (IIFE)
    // You can define private API/variables in here
    // …

    // Once your plugin API is ready, you have to apply the magic to each item
    // in the collection in some ways. You must add a property to jQuery.fn object.
    $.fn.myAwesomePlugin = function(Opt) {
      var defaultConfig = {option1: 'someValue' /*, …*/};

      // Eval supplied Opt object (Validate, reject, etc.)
      // If all goes well, eventually merge the object with defaults.
      $.extend(defaultConfig, Opt);

      // Apply the magic against each item in the jQuery collection
      // (Your plugin may not need to use "each" function though)
      // Return the jQuery collection anyway to keep chaining possible.
      // Once again, this is not required, your plugin may return something else depending on the options passed earlier for instance.
      return this.each(function(el, idx) {
        // Your plugin magic applied to collection items…
      });
    }

})(jQuery);

You should be able to call your plugin $('someSelector').myAwesomePlugin(); right after the declaration.

Simple implementation example:

(function ($) {
    let required = {url: null, pk: null}
      // Function to be executed upon first call to the plugin
      , populateCommons = () => {
        let ep = $('#someNode').data('endpoint')
            , pk = document.querySelector('#pk')
            ;
        // Basic tests to alert in case the page 
        // doesn't comply with the plugin requirements
        if( typeof ep !== 'string' || !/^\/[a-z]+/.test(ep) || !pk) {
            throw ` "myEditPlugin" init phase error:
            Detected endpoint: '${ep}'
            Is PK value found: ${!!pk}
            `;
        }
        [required.url, required.pk] = [ep, +pk.value];
      };

    $.fn.myEditPlugin = function(Opt) {
      let allOpts;
      // First call will trigger the retrival of common data
      // that should be available as static data somewhere every page source.
      !required.url && populateCommons();
      allOpts = $.extend({}, Opt, required);
      
      return this.each(function(el, idx) {
        // Your logic here, request
        console.log("Payload is", allOpts);
      });
    }
    
})(jQuery);

function debounce(fn, time) {
  debounce.timer && (clearTimeout(debounce.timer));
  debounce.timer = setTimeout(() => (fn(), debounce.timer = null), time);
}

$('[type="text"]').keydown(function(e){
   debounce(() => this.value && $(this).myEditPlugin({foo:this.value, bar: 'Contextual value'}), 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<input id="pk" type="hidden" value="5">

<div id="someNode" data-endpoint="/api/endpoint">
  Editing the below input will trigger the plug-in code
</div>

<input type="text" title="Edit me"/>

Related documentation here

Upvotes: 1

Related Questions