How To Attach Knockout Validation To A Custom Binding

0

I have created a Knockout custom binding to format dates using moment.js.

ko.bindingHandlers.dateStringValue = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor(),
        allBindings = allBindingsAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);

        if (valueUnwrapped === null) {
            ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel);
            $(element).val("");
        } else {
            ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel);
            var pattern = allBindings.datePattern || "MM/DD/YYYY";
            $(element).val(moment(valueUnwrapped).format(pattern));
        }
    }
};

This works great with my binding:

<input type="text" data-bind="dateStringValue: entryDate, datePattern: 'M/D/YYYY'" />

I created a custom validation rule from code I found on the web.

// http://stackoverflow.com/a/23086828/139917
// Use: var someDate= ko.observable().extend({ simpleDate: true });
// Dependencies: date.js
ko.validation.rules['simpleDate'] = {
    validator: function (val, validate) {
        var d = Date.parse(val);
        return (d != null);
        //return ko.validation.utils.isEmptyVal(val) || moment(val, 'MM/DD/YYYY').isValid();
    },
    message: 'Invalid date entry date'
};

ko.validation.registerExtenders();

I tried both moment.js and date.js date libraries.

I attached the custom validation rule to my viewmodel observable.

self.entryDate = ko.observable(je.entryDate || new Date()).extend({ simpleDate: true });

Now I want to use Knockout Validation to validate input but don't know how to let the validation framework know about my new binding. It seems Knockout Validation only knows about some or all of the built in Knockout bindings.

Calling ko.validation.makeBindingHandlerValidatable("dateStringValue"); raises a 0x800a138f - JavaScript runtime error: Object expected error in Knockout v3.2.0. It has been suggested that calling into the Knockout value binding from my custom binding would work but code like ko.bindingHandlers.value.update(element, valueAccessor); have not worked for me.

How can I let Knockout Validation know about my custom binding?

javascript
knockout.js
knockout-validation
asked on Stack Overflow Apr 17, 2015 by DaveB

3 Answers

0

Create your validation inside your custom binding by passing a new parameter to your binding i.e:

<input type="text" data-bind="customDate: { value: entryDate, pattern: 'M/D/YYYY'", validatable: true } />

Just do the magic inside your binding.

answered on Stack Overflow Apr 18, 2015 by DAG
0

Here's how I solved my problem. I created a read/write computed property on my entity and used moment.js to validate the input:

var dateFormatString = "M/D/YYYY"

self.entryDate = ko.observable(je.entryDate || new Date());
self.formattedEntryDate = ko.computed({
    read: function () {
        return moment(self.entryDate()).format(dateFormatString);
    },
    write: function (value) {
        var momentDate = moment(value, dateFormatString);
        if (momentDate.isValid()) {
            self.entryDate(momentDate.format(dateFormatString));
            self.isValidEntryDate(true);
            return;
        }
        self.isValidEntryDate(false);
    },
    owner: self
});
self.isValidEntryDate = ko.observable(true);

If the input string was not in the expected format, I would set an isValidEntryDate observable to false which would display a message to the user prompting them to re-enter the date.

<input type="text" data-bind="value: formattedEntryDate" />
<div class="validationMessage" data-bind="hidden: isValidEntryDate">Invalid entry date. Use M/D/YYYY format.</div>

This binding uses a custom binding handler of hidden.

I realize this doesn't address the question but in the name of productivity, this accomplishes the goal.

Feel free to answer the question directly.

answered on Stack Overflow May 1, 2015 by DaveB
0

I got it working like below -

<input type="text" data-bind="dateStringValue: entryDate.extend({ simpleDate: entryDate()}), datePattern: 'M/D/YYYY'" />
answered on Stack Overflow Jul 22, 2016 by gkb

User contributions licensed under CC BY-SA 3.0