Knockout.JS: My console says "Uncaught TypeError: Cannot read property 'toLowerCase' of undefined" when i try to search something

Knockout.JS: My console says "Uncaught TypeError: Cannot read property 'toLowerCase' of undefined" when i try to search something

我想用 knockout JS 创建一个搜索和下拉过滤器。我的下拉过滤器工作得很好,但是当我尝试搜索位置时,我的控制台给我这个错误:

Uncaught TypeError: Cannot read property 'toLowerCase' of undefined

ko.utils.stringStartsWith = function(string, startsWith) {
  string = string || " ";
  if (startsWith.length > string.length) return false;
  return string.substring(0, startsWith.length) == startsWith;
};

var Record = function(dropType, dropName, dropAddress, homeTown) {

  this.dropType = dropType;
  this.dropName = dropName;
  this.dropAddress = dropAddress;
  this.homeTown = homeTown;

};

var ViewModel = function(records, homeTowns) {
  var self = this;
  self.homeTowns = ko.observableArray(homeTowns);
  self.records = ko.observableArray(
    ko.utils.arrayMap(records, function(r) {
      return new Record(r.dropType, r.dropName, r.dropAddress, r.homeTown);
    }));



  // search filter
  self.dropTypeSearch = ko.observable('');
  self.dropNameSearch = ko.observable('');
  self.townSearch = ko.observable('');

  self.filteredRecords = ko.computed(function() {
    return ko.utils.arrayFilter(self.records(), function(x) {
      return (self.dropNameSearch().length == 0 || ko.utils.stringStartsWith(x.dropName.toLowerCase(), self.dropNameSearch().toLowerCase())) && (self.townSearch().length == 0 || x.homeTown == self.townSearch())
    });
  });
};



var homeTowns = ["Search Location", "Kalk Bay", "Simon's Town", "Muizenberg", "Claremont"];



var data = [{}, {
  id: 1,
  dropType: "Shelter",
  dropName: "The Haven, Kalk Bay",
  dropAddress: "139 Main Road, Kalk Bay",
  homeTown: "Kalk Bay",
}, {
  id: 2,
  dropType: "Shelter",
  dropName: "Happy Valley Simon's Town",
  dropAddress: "Palace Hill Road, Simon's Town",
  homeTown: "Simon's Town",
}, {
  id: 3,
  dropType: "Meals",
  dropName: "Living Grace Community Centre",
  dropAddress: "171 Main Road, Muizenberg",
  homeTown: "Muizenberg",
}, {
  id: 4,
  dropType: "Shelter",
  dropName: "The Haven, Claremont",
  dropAddress: "PO Box 23999, Claremont, 5 Fir Street",
  homeTown: "Claremont",
}, ];



ko.applyBindings(new ViewModel(data, homeTowns));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script>

//search input Name: <input data-bind="value: dropNameSearch, valueUpdate: 'afterkeyup'" /> Location: <select data-bind="options: homeTowns, value: townSearch"></select> //drop-down filter
<div data-bind="foreach: filteredRecords" class="record">
  <ul>
    <li>
      Type: <span data-bind="text: dropType"></span>
    </li>
    <li>
      Name: <span data-bind="text: dropName"></span>
    </li>
    <li>
      Address: <span data-bind="text: dropAddress"> </span>
    </li>
    <li>
      Location: <span data-bind="text: homeTown"></span>
    </li>

  </ul>

</div>

我是 Knockout.js 的新手。 JS fiddle 或片段 运行 将不胜感激。

因为函数是window对象的一部分,而不是输入元素对象。因此,计算函数,您调用的函数是 window 对象的一部分。此对象将引用 window。使用 call 或 apply,您可以将 this 的引用更改为另一个对象。在这种情况下,您的输入字段。当你有机会时,研究 "call" 和 "apply"。它将帮助您更好地理解它。

您的数据中有一个空对象,因此这将导致 Record 实例的属性未定义。消除错误的一种方法是将默认值添加到 Record 函数。

var Record = function(dropType, dropName, dropAddress, homeTown) {
    this.dropType = dropType || "";
    this.dropName = dropName || "";
    this.dropAddress = dropAddress || "";
    this.homeTown = homeTown || "";
};

这将确保所有属性都是字符串类型并修复错误。

但是,filteredRecords 计算变量中的代码逻辑仍然存在一些错误。如果 dropNameSearch() 中有值且 townSearch() 值为 "Search Location",则结果仍为空,因为没有数据的 homeTown 值为 "Search Location"

要解决此问题,请遵循以下逻辑顺序:

  1. 如果dropNameSearch为空,townSearch为"Search Location",则return[].
  2. 如果 dropNameSearch 中有值,则根据其值进行初始过滤。
  3. 如果townSearch的值不是"Search Location",则根据它的值进行二次过滤

这是一个示例 fiddle:https://jsfiddle.net/8z5Lmcau/