开发者

jQuery extension and closures

I'm learning javascript, and in the way I'm trying to learn more about jQuery. I have created a very simple "form controller" in JS, so when I create the object passing the form as parameter, it gets the events wired and hijacks the submit:

var FormController = function (form) {

    // private field
    var _form = $(form);
    var _url = _form.attr('action');
    var _isValid = false;

    $(form).submit(function (e) {
        submitForm();
        e.preventDefault();
    });

    var disableAll = function () {
        _form.find('select,input,textarea').attr('disabled', 'disabled')
    };

    var enableAll = function () {
        _form.find('select,input,textarea').removeAttr('disabled')
    };

    var submitForm = function () {

        disableAll();
        _isValid = true;

        if (_isValid) {
            $.ajax({
                url: _url,
                type: 'POST',
                success: function (data, textStatus, jqXHR) {
                    enableAll();
                    var obj = jQuery.parseJSON(jqXHR.responseText);
                    print(data.Result ? data.Result : "[No Result]");
                    print(textStatus.toString());
                },
                error: function (data, textStatus, jqXHR) {
                    print(textStatus);
                    _form.css('border', '1px solid Red');
                    enableAll();
                }
            });
        }
    };

    // public fields and functions
    return {
        getIsValid: function () { return _isValid; },
        submit: function () { return submitForm(); }
    };
};

It works as expected. Now I would like to create a jQuery extension, so I can apply that object to the results:

$.fn.formController = function () {
    return this.each(function (i, item) {
        new FormController(item);
    });
};

I also works as expected, but... will the GC collect that object? Or because the events are wired, it counts as referenced?

Now, I would like to keep the instances of my controllers available, so I can manipulate them after creation, for example for invoking methods. Something like this:

$('#myForm').formController('submit');

I have tried several ways to do it, but I cannot get it. I have tried to put a closure with a object that keep track of the items, etc... but I just got mess with "this".

What would be the correct way to do this? And I assume that everything I have done so far could be wrong even if it works.

Thanks.

UPDATE

Sorry if the question was not clear enough. What I am aiming for is a way I can do : $('#myForm').formController('submit'); , and then the funcion will find the "FormController" object associated with that HTML object, and invoke this "submit" member.

I want to achieve something like this:

$.fn.formController = function (name) {

    if(!document._items)
        document._items = [];

    if (name) {
        return this.each(function (i, item) {
            var id = 开发者_StackOverflow$(item).data('form-controller');

            if (id) {
                var fc = document._items[id];
                var member = fc[name];
                if (member) {
                    if (typeof member == 'function')
                        member();
                }
            }
        });
    }
    else {    
        return this.each(function (i, item) {
            var id = document._items.push(new FormController(item)) - 1;
            $(item).data('form-controller', id.toString());
        });
    }
};

The problem in this approach is that I'm keeping a collection as global object, and what I want is make it internal. I have tried with a closure, but I only got into problems with "this" (that points to DOMwindow), or the "_items" var being empty.

What would be the correct way to approach this?


You can use jQuery UI's Widget factory, which gives you that functionality out-of-the-box:

$.widget("vtortola.formcontroller", {
    _create: function() {
         // Use `this.element` to initialize the element.
         // You can also use this.options to get the options object that was passed.
    },
    _destroy: function() {
        // You can unbind events here to remove references from the DOM, so is'll get
        // deleted by the garbage collector.
    },
    submit: function() { /* ... */ }
});

// Example
$('#form').formcontroller({foo: 123}); // Calls `_create()` with the supplied options
$('#form').formcontroller('submit'); // Calls the `submit` method

The widget factory also gives you options setter/getters, default options, and other cool stuff. See the official docs and the tutorial at bililite for more details. If you aren't using jQuery UI for other stuff, you can use the widget factory code as a stand-alone, its not depended on other parts of jQuery UI.

If you prefer to it without using jQuery UI's widget factory, you can store the objects directly on .data() (which is also what the widget factory does), no need to keep a collection of them and keep track of that manually. Your code would be something along the lines of:

$.fn.formController = function () {
    var args = arguments;
    return this.each(function (i, item) {
        var $this = $(this), _obj;
        // When calling `$(..)`.formController() with arguments when the object already
        // exists, it treats the first argument as the method name and passes the rest
        // as arguments to that method.
        if (arguments.length && (_obj = $this.data('FormController'))) {
            _obj[args[0]].apply(_obj, Array.prototype.slice.call(args, 1));
        } else {
            $this.data('FormController', new FormController(item));
        }
    });
};


use

(function($){
    $.fn.formController = function(parameter1, parameter2){
            //this = #myForm
    }
)(jQuery);

and read this


I have also tried creating a collection as property of the function itself.

It works, but I don't know how correct/appropriate it is :D

$.fn.formController = function (name) {

    if (name) {
        return this.each(function (i, item) {
            var id = $(item).data('form-controller');

            if (id) {
                var fc = $.fn.formController.Items[id];
                print('get: ' + id + ' ' + (fc?'found':'not found'));
                var member = fc[name];
                if (member) {
                    if (typeof member == 'function')
                        member();
                }
            }
        });
    }
    else {

        return this.each(function (i, item) {
            var id = $.fn.formController.Items.push(new FormController(item)) - 1;
            print('add: ' + id);
            $(item).data('form-controller', id.toString());
        });
    }
};
$.fn.formController.Items = [];
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜