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:

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>