knockout validation not working on required items

2k views Asked by At

I'm very new to Knockout and even newer to using Knockout validation. I have some simple required inputs that should prevent from submitting the form. It still submits with empty inputs. I'm not sure what I'm doing wrong.

I have created a fiddle here. To test, hit tab when cursor is in the last retail input. That will add a new blank line. Then hit submit and look at the console. Here is my javascript code:

var viewModel;

ko.validation.rules.pattern.message = 'Invalid.';
ko.validation.init({
  registerExtenders: true,
  messagesOnModified: true,
  insertMessages: true,
  parseInputAttributes: true,
  messageTemplate: null
}, true);

var itemModel = function () {
  var self = this;
  self.itemNo = ko.observable().extend( {required: true} );
  self.brocCode = ko.observable().extend( {required: true} );
  self.itemDesc = ko.observable().extend( {required: true} );
  self.retail = ko.observable().extend( {required: true} );
}

var itemsModel = function(items) {
    var self = this;
  //self.items = ko.observableArray(items);

  self.items = ko.mapping.fromJSON(items);

  self.checkItemNo = function(data) {
    console.log("blurred!");
    var itemNo = "abc";
    if (itemNo != "") {
      var item = "";
        /*
        $.each(fullItemList, function(i, v) {
        if (v.No.search(itemNo) != -1) {
        item = v.Description;
        return;
        }
        });
        if(item != "") {
        console.log("found: " + item);
        var match = ko.utils.arrayFirst(self.items, function(item) {
        return itemNo === item.itemNo;
        });
        */
      var match = false;
      item = "Mudguard front";
      if (!match) {
        console.log("not a match!");
        console.log(data);
        data.itemDesc(item);
      }
    }
  }

  self.addLine = function() {
    self.items.push( new itemModel() )
  };

  self.removeItem = function(item) {
    self.items.remove(item);
  };

  self.errors = ko.validation.group(self.items);
};

ko.bindingHandlers.enterPress = {
  init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    var allBindings = allBindingsAccessor();
    element.addEventListener('keydown', function (event) {
      var keyCode = (event.which ? event.which : event.keyCode);
      if (keyCode === 13 || (!event.shiftKey && keyCode === 9)) {
        event.preventDefault();
        console.log("hit enter/tab!");
        bindingContext.$root.addLine();
        return false;
      }
      return true;
    });
  }
};

function GetItems() {
            //var itemsJSON = @Html.Raw(Json.Encode(Model.brochureItems));
            var itemsJSON =  '[{"brochureId":1,"itemNo":"1000","brocCode":"1000","itemDesc":"Bicycle","retail":13.5},{"brochureId":1,"itemNo":"1100","brocCode":"1100","itemDesc":"Front Wheel","retail":35},{"brochureId":1,"itemNo":"1120","brocCode":"1120","itemDesc":"Spokes","retail":12.5},{"brochureId":1,"itemNo":"1150","brocCode":"1150","itemDesc":"Front Hub","retail":5},{"brochureId":1,"itemNo":"1151","brocCode":"1151","itemDesc":"Axle Front Wheel","retail":14},{"brochureId":1,"itemNo":"120","brocCode":"120","itemDesc":"Loudspeaker, Black, 120W","retail":12.5},{"brochureId":1,"itemNo":"125","brocCode":"125","itemDesc":"Socket Back","retail":10}]';
            viewModel = new itemsModel(itemsJSON);
            ko.applyBindings(viewModel);
        }

$(document).ready(function () {
    GetItems();

  $('#brochureForm :submit').on('click', function(e) {
    e.preventDefault();  //prevent form from submitting

    if (viewModel.errors().length === 0)
      //if(viewModel.validationModel.isValid())
    {
      console.log("submitting!");
      //$("#brochureForm").submit();
    }
    else
    {
      console.log("not valid!");
    }
  });
});
1

There are 1 answers

3
Stuart Bourhill On BEST ANSWER

I've updated your fiddle https://jsfiddle.net/q7ahfzut/2/. Your problem was simply due to configuration when grouping. You need to add { deep: true } which will cause validation to be run on all the validated observables. You can add live: true which will cause validation to run when adding new items to your array. Some other things to note:

  1. No need for any jquery - you can use knockout for click events and submits
  2. I used the mapping library to add validation to all items from the get go.

Hope that helps you with your problem!

Update

Checkout the ko validation documentation here for more information on configuration

Update 2

To answer your question in your comment: take a look at the mapping plugin for knockout to find out how it works (its a bit better documented than the validation is. You can create to intercept the mapping in order to manipulate the resulting ViewModel. options.data contains the data that youre mapping before it is mapped to an observable. Best way to learn about it is to read the docs and then play with it by stepping through it slowly.