Comparing two fields with knockout-validation
Asked Answered
B

4

6

I have a viewmodel for a form that I'm trying to add validation to using knockout-validation.

fromDate: ko.observable(
            moment().subtract('days', 1).startOf('day').format(dateFormat)
          ),
toDate: ko.observable(
            moment().startOf('day').format(dateFormat)
        ),

I need to make sure that the from date is less than the to date. I can't seem to get any form of custom validator to pick up the reference to the second observable. I need something along the lines of:

toDate: ko.observable(moment().startOf('day').format(dateFormat)).extend({
          validation: {
            validator: function (val, someOtherVal) {
                return moment(val) >= moment(someOtherVal);
            },
            message: 'Must be greater or equal to From Date',
            params: viewModel.fromDate()
          }
        }),

Any ideas?

Update

I'm sure I'd already tried this, but moving the extension method into the onload function works.

$(function () {
    ko.validation.configure({ decorateElement: true });

    viewModel.toDate.extend({
    validation: {
            validator: function (val, someOtherVal) {
                return moment(val) >= moment(viewModel.fromDate());
            },
            message: 'To date must be greater than from date',
        }
    });

    ko.applyBindings(viewModel);
});
Backwoods answered 29/1, 2013 at 12:3 Comment(0)
L
6

knockout-validation's core rules are not able to handle observables as parameters for validations. But there is already a push request which should fix that: https://github.com/ericmbarnard/Knockout-Validation/pull/217

Therefore you have to use a custom role for that. You should insert the param as a function (by omitting the parenthesis). That will enable knockout-validation to react to changes in the param.

function ViewModel() {
    var self = this; // capture this to be able to reference properties

    self.fromDate = ko.observable(
        moment().subtract('days', 1).startOf('day').format(dateFormat)
    );
    self.toDate = ko.observable(
        moment().startOf('day').format(dateFormat)
    ).extend({
      validation: {
        validator: function (val, someOtherVal) {
            return moment(val) >= moment(someOtherVal());
        },
        message: 'Must be greater or equal to From Date',
        params: self.fromDate
      }
    }),
}
Lazulite answered 30/1, 2013 at 9:6 Comment(0)
I
3

Looking at the documentation for it, this looks like the way that you could do it:

ko.validation.rules['greaterThan'] = {
    validator: function (val, otherVal) {
        return val > otherVal;
    },
    message: 'The field must be greater than {0}'
};

ko.validation.registerExtenders();

fromDate: ko.observable(
            moment().subtract('days', 1).startOf('day').format(dateFormat)
          ),
toDate: ko.observable(
            moment().startOf('day').format(dateFormat)
        ).extend({ greaterThan: fromDate() });

It's untested, and I don't know if you want to pass in fromDate(), or fromDate and then in the validator use otherVal().

Impending answered 29/1, 2013 at 12:40 Comment(5)
Already tried that, it still comes up with 'Uncaught TypeError: Cannot call method 'fromDate' of undefined'Backwoods
Do you have a reference inside the viewmodel (var self = this), and try greaterThan: self.fromDate() ?Impending
Yes, it seems that the extension methods can't reference the view model if they are declared inside it. Registering them once there is an instance of the view model seems to work. note I was using the var vm ={} syntax not the function based syntax, this may not be the case then.Backwoods
I imagine that it's because at the time you were adding the extend method, the viewmodel hadn't finished initialising, and therefore it didn't exist, hence it being undefined in your validation method.Impending
The last line in Paul's answer should look like: ).extend({ greaterThan: this.fromDate }); i.e. parameter has to be passed without parentheses. and the third line should look like: return val > otherVal(); -- otherVal with parentheses. That way it works.Greenburg
S
0

This is a bit late to the party, but when I google for "knockoutjs password field match" this is the top hit.

I wanted a matcher without using the validation extension, because I did not need all the offerings of that package.

I have found a tidy knockoutjs trick for checking two or more fields against each other using an extender. That is, subscribe the original field to the second field in an extender to trigger a valueHasMutated() against the second field when the original is updated.

  ko.extenders.match = function(target, match_original) {
  target.match_ok = ko.observable();
  function copy(value) {
    target.match_ok(value == match_original());
  }
  function original(value) {
    target.valueHasMutated();
  }
  copy(target());
  target.subscribe(copy);
  match_original.subscribe(original);
  return target;
};

Here is a jsfiddle to better illustrate the functionality https://jsfiddle.net/8nugva9f/13/

HTH

Simmons answered 19/9, 2017 at 6:4 Comment(0)
D
-1
this.fromDate= ko.observable(null).extend({
        date: true,

    });

this.toDate = ko.observable(null).extend({
        date: true,
        min: this.fromDate,

    });

Try this.

Dm answered 25/10, 2017 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.