Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating methods on the fly

Hi I'm trying to author a jQuery plugin and I need to have methods accessible to elements after they are initialized as that kind of object, e.g.:

$('.list').list({some options}); //This initializes .list as a list

//now I want it to have certain methods like:
$('.list').find('List item'); //does some logic that I need

I tried with

$.fn.list = function (options) {
    return this.each(function() {
        // some code here
        this.find = function(test) {
            //function logic
        }
    }
}

and several other different attempts, I just can't figure out how to do it.

EDIT:

I'll try to explain this better.

I'm trying to turn a table into a list, basically like a list on a computer with column headers and sortable items and everything inbetween. You initiate the table with a command like

$(this).list({
    data: [{id: 1, name:'My First List Item', date:'2010/06/26'}, {id:2, name:'Second', date:'2010/05/20'}]
});

.list will make the <tbody> sortable and do a few other initial tasks, then add the following methods to the element:
.findItem(condition) will allow you to find a certain item by a condition (like findItem('name == "Second"')
.list(condition) will list all items that match a given condition
.sort(key) will sort all items by a given key
etc.

What's the best way to go about doing this?

like image 687
Rob Avatar asked Jun 26 '10 19:06

Rob


2 Answers

If you want these methods to be available on any jQuery object, you will have to add each one of them to jQuery's prototype. The reason is every time you call $(".list") a fresh new object is created, and any methods you attached to a previous such object will get lost.

Assign each method to jQuery's prototype as:

jQuery.fn.extend({
    list: function() { .. },
    findItem: function() { .. },
    sort: function() { .. }
});

The list method here is special as it can be invoked on two occasions. First, when initializing the list, and second when finding particular items by a condition. You would have to differentiate between these two cases somehow - either by argument type, or some other parameter.

You can also use the data API to throw an exception if these methods are called for an object that has not been initialized with the list plugin. When ('xyz').list({ .. }) is first called, store some state variable in the data cache for that object. When any of the other methods - "list", "findItem", or "sort" are later invoked, check if the object contains that state variable in its data cache.

A better approach would be to namespace your plugin so that list() will return the extended object. The three extended methods can be called on its return value. The interface would be like:

$('selector').list({ ... });
$('selector').list().findOne(..);
$('selector').list().findAll(..);
$('selector').list().sort();

Or save a reference to the returned object the first time, and call methods on it directly.

var myList = $('selector').list({ ... });
myList.findOne(..);
myList.findAll(..);
myList.sort();
like image 82
Anurag Avatar answered Nov 17 '22 18:11

Anurag


I found this solution here: http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html

This seems to do exactly what I need.

(function($) {
    var TaskList = function(element, options)
    {
        var $elem = $(element);
        var options = $.extend({
            tasks: [],
            folders: []
        }, options || {});

        this.changed = false;
        this.selected = {};

        $elem.sortable({
            revert: true,
            opacity: 0.5
        });

        this.findTask = function(test, look) {
            var results = [];

            for (var i = 0,l = options.tasks.length; i < l; i++)
            {
                var t = options['tasks'][i];
                if (eval(test))
                {
                    results.push(options.tasks[i]);
                }
            }
            return results;
        }

        var debug = function(msg) {
            if (window.console) {
                console.log(msg);
            }
        }
    }

    $.fn.taskList = function(options)
    {
        return this.each(function() {
            var element = $(this);

            if (element.data('taskList')) { return; }

            var taskList = new TaskList(this, options);

            element.data('taskList', taskList);

        });
    }

})(jQuery);

Then I have

    $('.task-list-table').taskList({
        tasks: eval('(<?php echo mysql_real_escape_string(json_encode($tasks)); ?>)'),
        folders: eval('(<?php echo mysql_real_escape_string(json_encode($folders)); ?>)')
    });
    var taskList = $('.task-list-table').data('taskList');

and I can use taskList.findTask(condition);

And since the constructor has $elem I can also edit the jQuery instance for methods like list(condition) etc. This works perfectly.

like image 2
Rob Avatar answered Nov 17 '22 16:11

Rob