1
\$\begingroup\$

I have no problem, but I would like some advice:

I've read some help to do some jQuery plugin, and I just did my first one. This plugin helps to generate dynamic datatable:

  • Autofill the table with some JSON data using the plugin datatable
  • Create a form to add new lines
  • Create some trigger click on the line => display a form to modify
  • Create a trigger for sending form => add or modify a line

All works fine! But because of my first jQuery plugin, I would like some advice to optimize it, and make it as clean as possible. Is there a better way to do it?

Here is my plugin:

(function($) {
    var PLUGIN_NS = 'dataTableAuto';

    var Plugin = function ( target, options ) { 
        this.$T = $(target); 
        this._init( target, options ); 
        return this; 

        /** #### OPTIONS #### */
        this.options= $.extend(
            true, {
                DEBUG: false
            },
            options
        );

    }
    /* Init */
    Plugin.prototype._init = function ( target, options ) { 
        this.field = $(target).attr("id").split("_")[1];

        /* some default values are generated with the table id, but can be changed if needed */
        defaultAutoOptions = {
            tableName:  "table_" + this.field,
            formClassName:  "form_" + this.field,
            jsonFile:   "/json/json_" + this.field + ".json",
            submitEvtFct: this._submitEvtFct,
            clickEvtFct: this._clickEvtFct,
            dispFormAjax: false, /* click on the line will change it by a form or make any ajax call to get form if needed */
            formAdd: true, /* add some lines allowed */
            titreFormNew: ""
        }
        this.options = $.extend(defaultAutoOptions, Plugin.defaults, options);
        if (this.options.formAdd && $("#formNew_" + this.field).length == 0)
            this._createFormAdd(target);
        this.dataTableAuto = this._dataTableAuto(target);
        this.options.clickEvtFct(this);
        this.options.submitEvtFct(this);

        return this;
    };

    /* Default val */
    Plugin.defaults = {
        events: function(_, callback){ callback([]) }
    };

    /* Auto create the form to add some lines */
    Plugin.prototype._createFormAdd = function ( target ) { 
        var _form = "<form id='formNew_" + this.field + "' class='" + this.options.formClassName + " preventDblClick' method='post'>";
        _form += "<h2>" + this.options.titreFormNew + "</h2>";
        $(" > thead > tr th", $(this.$T)).each(function(index) {
            var attr = $(this).attr("id");
            if (typeof attr !== typeof undefined && attr !== false && attr != "col_actions")
                _form += "<label>" + $(this).html() + "<br /><input type='text' name='" + attr.split("_")[1] + "' maxlength='" + $(this).data("maxlength") + "' /></label>";
        })
        _form += "<input type='submit' value='Enregistrer' />";
        _form += "</form>";
        $(this.$T).before(_form);
    }

    /* Init the datatable */
    Plugin.prototype._dataTableAuto = function ( target, options ) { 
        var _this = this;
        var infosTable = this._getInfosColumn(target); 
        return tableDT = $(this.$T).DataTable({
            "ajax": {
                'type': 'GET',
                'url': this.options.jsonFile,
                'data':  function ( d ) {
                }
            },
            destroy: true,
            info: false,
            rowId: "lineID",
            'columnDefs': infosTable.listColumnDef,
            "columns": infosTable.listColumn,
            'paging' : false,
            "searching": false,
            "initComplete": function( settings, json ) {
                if (typeof json['eval']!='undefined'){
                    eval(json['eval']);
                }
            },
            createdRow: function( row, data, dataIndex ) {
                _this._setClickableRow(target, row);
            }
        });
    }

    /* get column data used for datatable */
    Plugin.prototype._getInfosColumn = function ( target) {
        listColumn = [];
        listColumnDef = [];
        attr = $(" > thead > tr th:first-child", $(target[0])).attr("id");
        if (typeof attr === typeof undefined || attr === false) {
            listColumn.push({ "data": "control" });
            listColumnDef.push({ className: 'control', targets: 0 });
        }
        $(" > thead > tr th", $(target[0])).each(function(index) {
            if ($(this).attr("id") != undefined) {
                listColumnDef.push({ responsivePriority: $(this).attr("id") == "col_buttons" ? 1 : index, targets: index});
                if ($(this).hasClass("isDate"))
                    listColumn.push(
                        { "data": $(this).attr("id").split("_")[1],
                            render: function ( data, type, row ) {
                               return (data.split("-").length == 3 ? moment(data).format("DD/MM/YYYY") : data);
                            } 
                        }
                    )
                else
                    listColumn.push({ "data": $(this).attr("id").split("_")[1] })
            }
        })
        return {listColumnDef: listColumnDef, listColumn: listColumn};
    }

    /* On which row add a trigger onclick */
    Plugin.prototype._setClickableRow = function (target, row) {
        $(" > thead > tr th", $(target[0])).each(function(index) {
            if ($(this).hasClass("clickable")) {
                $("td:eq(" + index + ")", row).addClass('clickable');
            }
            if ($(this).attr("id") != undefined)
                $("td:eq(" + index + ")", row).addClass($(this).attr("id").split("_")[1]);
        })
    }


    /* trigger onclick */
    Plugin.prototype._clickEvtFct = function ( target, options) {
        $(target.$T).on("click", "tr td.clickable, .btDelete", function() {
            /* Click on a cell => aff a form to modify */
            if ($(this).hasClass("clickable")) {
                var theRowObject = target.dataTableAuto.row($(this).closest('tr'));
                /* Get the form with ajax request */
                if (target.options.dispFormAjax) {
                    $.getJSON( target.options.jsonFile, {
                        command: "modLigne", 
                        lineID: lineID
                    })
                    .done(function( data ) {
                        data = data.data[0];
                        target.dataTableAuto.row(theRowObject).data(data).draw();
                        /* Not update again the line if the form is displayed */
                        $("tr#" + data.lineID + " td.clickable").addClass("pausedClick").removeClass("clickable");
                    });
                }
                /* auto create the form */
                else {
                    temp = theRowObject.data();
                    var affForm = true;
                    var tr = $(this).closest("tr");
                    var lineID = tr.attr("id").split("_")[1];
                    $.each(temp, function(item, value) {
                        var maxLength = $(" > thead > tr th#col_" + item, $(target.$T)).data("maxlength");
                        if (item != "lineID" && item != "control")
                        if (item == "actions")
                            temp[item] = "<button form='mod" + target.field + "_" + lineID + "'>OK</button>";
                        else if (affForm) {
                            temp[item] = "<form name='formMod' id='mod" + target.field + "_" + lineID + "' class='form_" + target.field + "'><input type='text' name='" + item + "' value='" + value + "' maxlength='" + maxLength + "' /></form>";
                            affForm = false;
                        }
                        else
                            temp[item] = "<input type='text' name='" + item + "' value='" + value + "' form='mod" + target.field + "_" + lineID + "' maxlength='" + maxLength + "' />";
                    })
                    target.dataTableAuto.row(theRowObject).data(temp).draw();
                    $("td.clickable", tr).addClass("pausedClick").removeClass("clickable");
                }
            }
            /* bouton to delete the line */
            else if ($(this).hasClass("btDelete")) {
                var goAction = true;
                var tr = $(this).closest("tr");
                var lineID = tr.attr("id").split("_")[1];
                if(typeof $(this).data("message")!='undefined' && !confirm($(this).data("message")))
                    return false;
                $.getJSON( target.options.jsonFile, {command: 'suppLine', lineID: lineID})
                .done(function( datas ) {
                    /* Actions to do when succeed */
                    if (datas.success)
                        target.dataTableAuto.row('#' + tr.attr("id")).remove().draw(); 
                });
            }
        })
    }

    /* trigger submit form to add or update line */
    Plugin.prototype._submitEvtFct = function ( target) {
        $("body").on("submit",  "form." + target.options.formClassName, function(e) {
            e.preventDefault();
            $.getJSON( target.options.jsonFile, $(this).serialize() + "&command=saveData&lineID=" + ($(this).closest("tr").length > 0 ? $(this).closest("tr").attr("id").split("_")[1] : 0))
            .done(function( datas ) {
                data = datas.data[0];
                var _tr = $("tr#" + data.lineID);
                if (_tr.length > 0) {
                    target.dataTableAuto.row($("tr#" + data.lineID).index()).data(data).draw();
                    /* Autorize the click for display one more time the update form */
                    $("tr#" + data.lineID + " td.pausedClick").addClass("clickable").removeClass("pausedClick");
                }
                else {
                    target.dataTableAuto.row.add( data ).node().id = data.lineID;
                    target.dataTableAuto.draw();
                }
                if(typeof datas['eval']!='undefined')eval(datas['eval']);
            });
        })  
    }

    Plugin.prototype.DWARN = function () {
        this.DEBUG && console.warn( arguments );    
    }


    $.fn[ PLUGIN_NS ] = function( methodOrOptions ) {
        if (!$(this).length) {
            return $(this);
        }
        var instance = $(this).data(PLUGIN_NS);

        if ( instance 
                && methodOrOptions.indexOf('_') != 0 
                && instance[ methodOrOptions ] 
                && typeof( instance[ methodOrOptions ] ) == 'function' ) {

            return instance[ methodOrOptions ](this,  Array.prototype.slice.call( arguments, 1 ) ); 


        } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {

            return this.each(function() {
                instance = new Plugin( $(this), methodOrOptions ); 
                $(this).data( PLUGIN_NS, instance );
            })

        // CASE: method called before init
        } else if ( !instance ) {
            $.error( 'Plugin must be initialised before using method: ' + methodOrOptions );

        // CASE: invalid method
        } else if ( methodOrOptions.indexOf('_') == 0 ) {
            $.error( 'Method ' +  methodOrOptions + ' is private!' );
        } else {
            $.error( 'Method ' +  methodOrOptions + ' does not exist.' );
        }
    };

})(jQuery);

The HTML code can be:

    <table id="table_categorie">
        <thead>
            <th></th>
            <th id="col_categorieCode" class="clickable" data-maxlength="10">Code</th>
            <th id="col_categorieLib" class="clickable" data-maxlength="100">libellé</th>
            <th id="col_actions"></th>
        </thead>
        <tbody>
        </tbody>
    </table>

I call my plugin by the line:

var tableCategorie = $("table#table_categorie").dataTableAuto({titreFormNew: "New category"});

And the JSON will return this data, for example:

"data": [
        {

                "control":              "",
                "lineID":               "categorie_1",
                "categorieClientCode":  "123",
                "categorieClientLib":   "categorie 1",
                "codeCompta":           "",
                "actions":              "<button class="btSupp" data-message="Are you sure to delete this categorie ?"></button>"
        }
    ,
        {
                "control":              "",
                "lineID":               "categorie_2",
                "categorieClientCode":  "124",
                "categorieClientLib":   "categorie 2",
                "codeCompta":           "",
                "actions":              "<button class="btSupp" data-toggle="tootltip" data-placement="top" data-message="Are you sure to delete this categorie ?"></button>"
        }
]

And when I delete a line, it return after delete the line in the database:

{"success":"true","msg":"the line was deleted"}
\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.