3
votes

Is there a way to extend an observable to make it required only if and observable is true, and that changes can be tracked?

For example, I got this custom extender:

ko.extenders.requiredOnlyIf = function (target, makeRequired) {
target.HasError = ko.observable();

function validate(newValue) {
    if (makeRequired) {
        target.HasError(newValue ? false : true);
    } else {
        target.HasError(false);
    }
}

   validate(target());
   target.subscribe(validate);
   return target;
}

And this observables:

self.Field1 = ko.observable().extend({ requiredOnlyIf : self.Field2() });
self.Field2 = ko.observable().extend({ requiredOnlyIf : self.Field1() });

This observables are dependant, so, if one is filled, the other must be filled too. But the extender only works fine when the value is binding the first time, but when the value of any of the observables is changed is not working.

1

1 Answers

4
votes

A couple of things:

  1. You need to pass the observables, not the observables' contents, when setting up the extensions. (You can't create Field1 based on Field2 before Field2 exists, either.)
  2. You need two subscriptions, so that if a change to one makes the other invalid, that is noticed.
  3. (update) Rather than subscriptions and an observable, you can use a computed

ko.extenders.requiredOnlyIf = function(target, makeRequired) {
  target.HasError = ko.pureComputed(() => {
    const otherHasValue = !!makeRequired();
    const targetHasValue = !!target();

    return otherHasValue && !targetHasValue;
  });

  return target;
}

self = {};

self.Field1 = ko.observable();
self.Field2 = ko.observable().extend({
  requiredOnlyIf: self.Field1
});
self.Field1.extend({
  requiredOnlyIf: self.Field2
});

ko.applyBindings(self);
.invalid {
  border-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: Field1, css: {invalid: Field1.HasError()}" />
<input data-bind="value: Field2, css: {invalid: Field2.HasError()}" />