Search code examples
javascriptknockout.jsknockout-validation

Knockout ValidationMessage binding not displaying message


I'm trying to use knockout.validation on a number of parts of a form. Most of this all works fine and can use the built in message appender, but I've got 1 specific field where I need to add the message myself manually to the DOM.

I'm having a real problem getting it working though, because I simply can't get the text to display if there hasn't been a change to the field. I've mocked up a really small example to illustrate as close to the larger form as I can.

The behaviour I'm seeing:

  • Clicking submit correctly runs the validation (adds a red border around the input) but it doesn't display the error message like it should do.
  • Typing something into the password box, then emptying it out, then hitting submit works correctly displaying both error message and the red border.

Due to the fact I'm trying to introduce a required field - I can't guarantee that the field has ever been modified, so I need to ensure the first case works. Can anyone see what I'm doing wrong?

ko.validation.init({
  registerExtenders: true,
  messagesOnModified: true,
  insertMessages: true,
  decorateInputElement: true,
  decorateElementOnModified: false,
  errorElementClass: "is-invalid",
  messageTemplate: "inline_error"
}, true);

const viewModel = {};
viewModel.submitted = ko.observable(false);
viewModel.clicked = () => { viewModel.submitted(true); };
const onlyIf = ko.computed(() => viewModel.submitted());

viewModel.user = {
  password: ko.observable().extend({
    required: {
      message: `password is required.`,
      onlyIf,
    }
  })
};

ko.applyBindings(viewModel);
.is-invalid {
  border: thick solid red;
}

.text-danger {
   color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>

<div id="domTest">
  <button type="button" data-bind="click: clicked">Submit</button>

  <input type="password" class="form-control" id="password" placeholder="Password" data-bind="validationOptions:{ insertMessages: false }, textInput: user.password">
  <div class="text-danger error-message" data-bind="validationMessage: user.password" />
</div>


Solution

  • In this Github issue, validationMessage does not run on initial binding, stevegreatrex say that you need to change messagesOnModified to false:

    messagesOnModified: false

    And from the answer to this question Knockout Validation onlyif object no function and pattern validation (by steve-greatrex) you need to change the onlyIf computed:

    viewModel.onlyIf = ko.computed(() => viewModel.submitted());
    

    And then change the property onlyIf in viewModel.user to point to the computed viewModel.onlyIf:

    viewModel.user = {
      password: ko.observable().extend({
        required: {
          message: `password is required.`,
          onlyIf: viewModel.onlyIf(),
        }
      })
    };
    

    Hope this helps.

    ko.validation.init({
      registerExtenders: true,
      //messagesOnModified: true,
      messagesOnModified: false,	
      insertMessages: true,
      decorateInputElement: true,
      decorateElementOnModified: false,
      errorElementClass: "is-invalid",
      messageTemplate: "inline_error"
    }, true);
    
    const viewModel = {};
    viewModel.submitted = ko.observable(false);
    viewModel.clicked = () => { viewModel.submitted(true); };
    viewModel.onlyIf = ko.computed(() => viewModel.submitted());
    
    viewModel.user = {
      password: ko.observable().extend({
        required: {
          message: `password is required.`,
          onlyIf: viewModel.onlyIf(),
        }
      })
    };
    
    ko.applyBindings(viewModel);
    .is-invalid {
      border: thick solid red;
    }
    
    .text-danger {
       color: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
    
    <div id="domTest">
      <button type="button" data-bind="click: clicked">Submit</button>
    
      <input type="password" class="form-control" id="password" placeholder="Password" data-bind="validationOptions:{ insertMessages: false }, textInput: user.password">
      <div class="text-danger error-message" data-bind="validationMessage: user.password" />
    </div>