小提琴中的淘汰赛错误

时间:2017-01-11 16:37:06

标签: knockout.js

我无法弄清楚这个错误在我的小提琴中意味着什么。将光标放在最后一行的最后一个输入中并选中。它应该添加一个新行。但是我的代码中出现了错误。我得到的错误是TypeError: itemNo(...) is undefined。这是我的小提琴:https://jsfiddle.net/tLfezuu1/1/

HTML:

<table class="table table-bordered table-striped" id="brochureItems">
    <thead>
        <tr>
            <th>
                Item No
            </th>
            <th>
                Bro Code
            </th>
            <th width="36%">
                Desc
            </th>
            <th width="15%">
                Retail
            </th>
            <th>
                Prize Cnt
            </th>
            <th>
                Order
            </th>
            <th>
                Remove
            </th>
        </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td>
                <div data-bind="if: (itemNo().length < 1)"><input data-bind="value: itemNo, hasFocus: invalidItem(), selected: invalidItem(), event: { blur: $parent.checkItemNo }, attr: { name: 'brochureitems[' + $index() + '].itemNo', id: 'brochureItems_' + $index() + '__itemNo' }, validationOptions: { errorElementClass: 'input-validation-error' }" class="form-control item-id" /></div>
                <div data-bind="if: (itemNo().length > 0)"><input data-bind="value: itemNo, attr: { name: 'brochureitems[' + $index() + '].itemNo', id: 'brochureItems_' + $index() + '__itemNo' }, validationOptions: { errorElementClass: 'input-validation-error' }" class="form-control item-ID" readonly="readonly" tabindex="-1" /></div>
            </td>
            <td>
                <div data-bind="if: (brocCode.length < 1)">
                    <input data-bind="value: brocCode, insertPress: $index, attr: { name: 'brochureitems[' + $index() + '].brocCode', id: 'brochureItems_' + $index() + '__brocCode' }, validationOptions: { errorElementClass: 'input-validation-error' }" class="form-control" />
                </div>
                <div data-bind="if: (brocCode.length > 0)">
                    <input data-bind="value: brocCode, insertPress: $index, attr: { name: 'brochureitems[' + $index() + '].brocCode', id: 'brochureItems_' + $index() + '__brocCode' }, validationOptions: { errorElementClass: 'input-validation-error' }" class="form-control" readonly="readonly" tabindex="-1" />
                </div>
            </td>
            <td class="item-desc">
                <input data-bind="value: itemDesc, attr: { name: 'brochureitems[' + $index() + '].itemDesc', id: 'brochureItems_' + $index() + '__itemDesc' }, validationOptions: { errorElementClass: 'input-validation-error' }" class="form-control" readonly="readonly" tabindex="-1" />
            </td>
            <td class="item-retail">
                <div class="input-group">
                    <div class="input-group-addon">$</div>
                    <div data-bind="if: ($index() === ($parent.items().length - 1))"><input data-bind="value: retail, valueUpdate: 'afterkeydown', attr: { name: 'brochureitems[' + $index() + '].retail', id: 'brochureItems_' + $index() + '__retail' }, validationOptions: { errorElementClass: 'input-validation-error' }, style: { backgroundColor: retail == 0 ? '#FFFCCE' : '#ffffff'}" class="form-control" /></div>
                    <div data-bind="if: ($index() < ($parent.items().length - 1))"><input data-bind="value: retail, valueUpdate: 'afterkeydown', money: retail, attr: { name: 'brochureitems[' + $index() + '].retail', id: 'brochureItems_' + $index() + '__retail' }, validationOptions: { errorElementClass: 'input-validation-error' }, style: { backgroundColor: retail == 0 ? '#FFFCCE' : '#ffffff'}" class="form-control" readonly="readonly" tabindex="-1" /></div>
                </div>
            </td>
            <td>
                <div><input data-bind="value: prizeNum, valueUpdate: 'afterkeydown', attr: { name: 'brochureitems[' + $index() + '].prizeNum', id: 'brochureItems_' + $index() + '__prizeNum' }, validationOptions: { errorElementClass: 'input-validation-error' }, style: { backgroundColor: prizeNum == 0 ? '#FFFCCE' : '#ffffff'}" class="form-control" /></div>
            </td>
            <td>
                <div data-bind="if: ($index() === ($parent.items().length - 1))"><input data-bind="value: itemOrder, valueUpdate: 'afterkeydown', enterPress: 'addRow', attr: { name: 'brochureitems[' + $index() + '].itemOrder', id: 'brochureItems_' + $index() + '__itemOrder' }, validationOptions: { errorElementClass: 'input-validation-error' }, style: { backgroundColor: itemOrder == 0 ? '#FFFCCE' : '#ffffff'}" class="form-control" /></div>
                <div data-bind="if: ($index() < ($parent.items().length - 1))"><input data-bind="value: itemOrder, valueUpdate: 'afterkeydown', attr: { name: 'brochureitems[' + $index() + '].itemOrder', id: 'brochureItems_' + $index() + '__itemOrder' }, validationOptions: { errorElementClass: 'input-validation-error' }, style: { backgroundColor: itemOrder == 0 ? '#FFFCCE' : '#ffffff'}" class="form-control" /></div>
            </td>
            <td class="remove"><span class="glyphicon glyphicon-remove removeRow" data-bind="click: $parent.removeItem"></span></td>

        </tr>
    </tbody>
</table>

淘汰赛:

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

(function(){

  var toMoney = function(num){
    if(num != null && num != "") {
      num = parseFloat(num);
      return (num.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,') );
    } else {
      return "";
    }
  };

  var handler = function(element, valueAccessor, allBindings){
    var $el = $(element);
    var method;

    // Gives us the real value if it is a computed observable or not
    var valueUnwrapped = ko.unwrap( valueAccessor() );

    if($el.is(':input')){
      method = 'val';
    } else {
      method = 'text';
    }
    return $el[method]( toMoney( valueUnwrapped ) );
  };

  ko.bindingHandlers.money = {
    update: handler
  };
})();

var itemModel = function (data) {
  var self = this;
  self.invalidItem = ko.observable(true);
  self.itemNo = ko.observable(data ? data.itemNo : undefined).extend( {
    required: {
      params: true,
      message: "Item no. required."
    }
  });
  self.brocCode = ko.observable(data ? data.brocCode : undefined).extend( {
    required: {
      params: true,
      message: "Bro code required."
    }
  });
  self.itemDesc = ko.observable(data ? data.itemDesc : undefined).extend( {
    required: {
      params: true,
      message: "Item desc required."
    }
  });
  self.retail = ko.observable(data ? data.retail : undefined).extend( {
    required: {
      params: true,
      message: "Retail required."
    }
  })
    .extend({numeric: 2});
  self.prizeNum = ko.observable(data ? data.prizeNum : undefined).extend( {
    required: {
      params: true,
      message: "Prize num required."
    }
  });
  self.itemOrder = ko.observable(data ? data.itemOrder : undefined).extend( {
    required: {
      params: true,
      message: "Item order required."
    }
  });
}

var itemsModel = function(items) {
  var self = this;
  self.items = ko.mapping.fromJSON(items);

  //self.invalidItem = ko.observable(true);

  self.checkItemNo = function(data) {
    console.log("lost focus - " + self.invalidItem());
    var itemNo = $.trim(data.itemNo());

    if (itemNo != "") {
      var item = "";
      $.each(window.listOfItems, function(i, v) {
        if (v.No.search(itemNo) != -1) {
          item = v.Description;
          return;
        }
      });
      if(item != "") {
        var match = ko.utils.arrayFirst(self.items(), function(newItem) {
          return itemNo === newItem.itemNo;
        });
        console.log("match: " + match);
        if (!match) {
          data.itemDesc(item);
        } else { // item already entered
          //setTimeout(function() { self.items.invalidItem(true); }, 1);
          data.itemDesc("");
          slideDownMsg("Item already entered.");
          slideUpMsg(3000);
        }
      } else { // invalid item #
        console.log(data);
        data.invalidItem(true);
        //setTimeout(function() { data.invalidItem(true); }, 1);
        data.itemDesc("");
        slideDownMsg("Invalid item number.");
        slideUpMsg(3000);
      }
    }
  }

  self.submit = function() {
    //self.showErrors(true);
    if (viewModel.errors().length === 0) {
      console.log('Thank you.');
      $("#brochureForm").submit();
    }
    else {
      console.log('Please check your submission.');
      viewModel.errors.showAllMessages();
      $(".input-validation-error").first().focus();
    }
  }

  self.addLine = function() {
    var iModel = new itemModel();
    iModel.invalidItem(true);
    //self.invalidItem(true);
    console.log("adding new line; it is: " + iModel.invalidItem());
    self.items.push( iModel );
    //setTimeout(function() { self.invalidItem(true); }, 1);
  };

  self.insertLine = function(index) {
    self.items.splice(index, 0, new itemModel() );
  };

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

  self.errors = ko.validation.group(self.items, { deep: true, live: true });

  self.validate = function() {
    self.errors.showAllMessages();
  }
};

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();
        //bindingContext.$root.invalidItem(false);
        bindingContext.$root.addLine();
        return false;
      }
      return true;
    });
  }
};

ko.bindingHandlers.insertPress = {
  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 === 45) {
        event.preventDefault();
        bindingContext.$root.insertLine(ko.unwrap(valueAccessor()));
        return false;
      }
      return true;
    });
  }
};

ko.bindingHandlers.selected = {
  update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    var selected = ko.utils.unwrapObservable(valueAccessor());
    if (selected) element.select();
  }
};        

    function GetItems() {
    var itemsJSON =  '[{"brochureId":1,"itemNo":"1000","brocCode":"1000","itemDesc":"Bicycle","retail":13.5, "prizeNum":1, "itemOrder":1},{"brochureId":1,"itemNo":"1100","brocCode":"1100","itemDesc":"Front Wheel","retail":35, "prizeNum":2, "itemOrder":2},{"brochureId":1,"itemNo":"1120","brocCode":"1120","itemDesc":"Spokes","retail":12.5, "prizeNum":3, "itemOrder":3},{"brochureId":1,"itemNo":"1150","brocCode":"1150","itemDesc":"Front Hub","retail":5, "prizeNum":4, "itemOrder":4},{"brochureId":1,"itemNo":"1151","brocCode":"1151","itemDesc":"Axle Front Wheel","retail":14, "prizeNum":5, "itemOrder":5},{"brochureId":1,"itemNo":"120","brocCode":"120","itemDesc":"Loudspeaker, Black, 120W","retail":12.5, "prizeNum":6, "itemOrder":6},{"brochureId":1,"itemNo":"125","brocCode":"125","itemDesc":"Socket Back","retail":10, "prizeNum":7, "itemOrder":7}]';
    var viewModel = new itemsModel(itemsJSON);
    ko.applyBindings(viewModel, $("#brochureItems")[0]);
  }

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

1 个答案:

答案 0 :(得分:2)

它说itemNo未定义,因为如果没有传入数据,则将itemNo设置为undefined:

self.itemNo = ko.observable(data ? data.itemNo : undefined)...

你可以做到

self.itemNo = ko.observable(data ? data.itemNo : '').extend({
    required: {
      params: true,
      message: "Item no. required."
    }
});