Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

input type=reset and knockout

Knockout doesn't update observables when a form reset button is clicked.

http://jsfiddle.net/nQXeM/

HTML:

<form>
    <input type="text" data-bind="value: test" />
    <input type="reset" value="reset" />
</form>
<p data-bind="text: test"></p>

JS:

function ViewModel() {
    this.test = ko.observable("");
}

ko.applyBindings(new ViewModel());

Clearly the change event of the input box isn't being fired, as seen with this jQuery test: http://jsfiddle.net/LK8sM/4/

How would we go about forcing all observables bound to form inputs to update without having to manually specify them if the reset button isn't firing of change events?

It would be easy enough to use jQuery to find all inputs inside the form and trigger change events, but lets assume we've a knockout only controlled form.

like image 680
Fergal Avatar asked Apr 08 '13 21:04

Fergal


2 Answers

I copied and modified the default Knockout submit binding in order to create a similar binding for the form reset event:

ko.bindingHandlers['reset'] = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        if (typeof valueAccessor() !== 'function')
            throw new Error('The value for a reset binding must be a function');

        ko.utils.registerEventHandler(element, 'reset', function (event) {
            var handlerReturnValue;
            var value = valueAccessor();

            try {
                handlerReturnValue = value.call(bindingContext['$data'], element);
            } finally {
                if (handlerReturnValue !== true) {
                    if (event.preventDefault)
                        event.preventDefault();
                    else
                        event.returnValue = false;
                }
            }
        });
    }
};

You'd bind this like:

<form data-bind="reset: onFormReset">

and onFormReset would be on your view model:

function ViewModel() {
    this.onFormReset = function () {
        //Your custom logic to notify or reset your specific fields.

        return true;
    }
}

In your reset handler, if you return true, then JavaScript will continue to call its reset function on the form. If you are setting observables that are bound to value, though, you don't really need to have JavaScript continue to reset the form. Therefore, you could technically not return anything, or return false in that scenario.

Someone else could extend this further to notify all the bound observables in the form automatically, but this worked for my purposes.

like image 95
crush Avatar answered Oct 01 '22 19:10

crush


As you mentioned, the change event isn't fired when a form is reset. If you're only using KnockOut, I don't think you really have may options unless you create custom bindings that can register for the reset event and detect changes - that would still involve manual JS, but at least it would be centralized.

A more general approach, although it does require jQuery, is to create a function to handle the form's reset event, and detect changes on the form inputs at that time.

Here's an example of an event handler that might work. Please be aware, this is not production-ready code. I would look at it with a good jQuery eye before using :)

$('form').on('reset', function (evt) {
    evt.preventDefault();
    $(this).find('input, select, textarea').each(function () {
        if ($(this).is('input[type="radio"], input[type="checkbox"]')) {
            if ($(this).is(':checked') !== $(this)[0].defaultChecked) {
                $(this).val($(this)[0].defaultChecked);
                $(this).trigger('click');
                $(this).trigger('change');
            }
        } else {
            if ($(this).val() !== $(this)[0].defaultValue) {
                $(this).val($(this)[0].defaultValue);
                $(this).change();
            }
        }
    });
});

Here's a fiddle that demonstrates the idea: http://jsfiddle.net/Fm8rM/2/

like image 45
Joseph Gabriel Avatar answered Oct 01 '22 17:10

Joseph Gabriel