淘汰赛:viewModel 未定义

Knockout: viewModel is not defined

当使用 this JSFiddle 作为参考构建具有可组织内容的网格时,我 运行 遇到了问题。一条错误消息显示 Error: viewModel is not defined.

它应该像示例中那样正常工作,因为我的 viewModel 定义在 JS 的第一行。它可能与从模板中请求 viewModel 有关。

查看其他答案时,都太笼统了。我没有找到可以解决我问题的答案。

完整错误:

Uncaught ReferenceError: Unable to process binding "template: function (){return { name:'gridTmpl',foreach:gridItems,templateOptions:{ parentList:gridItems}} }"
Message: Unable to process binding "template: function (){return { name:'rowTmpl',foreach:rowItems,templateOptions:{ parentList:rowItems}} }"
Message: Unable to process binding "visible: function (){return $data !== viewModel.selectedRowItem() }"
Message: viewModel is not defined

var viewModel = {
  gridItems: ko.observableArray(
    [{
      "rowItems": [{
        "name": "Item 1"
      }, {
        "name": "Item 2"
      }, {
        "name": "Item 3"
      }]
    }, {
      "rowItems": [{
        "name": "Item 4"
      }]
    }, {
      "rowItems": [{
        "name": "Item 5"
      }, {
        "name": "Item 6"
      }]
    }]
  ),
  selectedRowItem: ko.observable(),
  selectRowItem: function(rowItem) {
    this.selectedRowItem(rowItem);
  }
};

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
  init: function(element, valueAccessor, allBindingsAccessor, context) {
    $(element).data("sortList", valueAccessor()); //attach meta-data
    $(element).sortable({
      update: function(event, ui) {
        var item = ui.item.data("sortItem");
        if (item) {
          //identify parents
          var originalParent = ui.item.data("parentList");
          var newParent = ui.item.parent().data("sortList");
          //figure out its new position
          var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
          if (position >= 0) {
            originalParent.remove(item);
            newParent.splice(position, 0, item);
          }

          ui.item.remove();
        }
      },
      connectWith: '.container'
    });
  }
};

//attach meta-data
ko.bindingHandlers.sortableItem = {
  init: function(element, valueAccessor) {
    var options = valueAccessor();
    $(element).data("sortItem", options.item);
    $(element).data("parentList", options.parentList);
  }
};

//control visibility, give element focus, and select the contents (in order)
ko.bindingHandlers.visibleAndSelect = {
  update: function(element, valueAccessor) {
    ko.bindingHandlers.visible.update(element, valueAccessor);
    if (valueAccessor()) {
      setTimeout(function() {
        $(element).focus().select();
      }, 0); //new RowItems are not in DOM yet
    }
  }
}

ko.applyBindings(viewModel);
.sortable {
  list-style-type: none;
  margin: 0;
  padding: 0;
  width: 60%;
}

.sortable li {
  margin: 0 3px 3px 3px;
  padding: 0.4em;
  padding-left: 1.5em;
  font-size: 1.4em;
  height: 18px;
  cursor: move;
}

.sortable li span {
  position: absolute;
  margin-left: -1.3em;
}

.sortable li.fixed {
  cursor: default;
  color: #959595;
  opacity: 0.5;
}

.sortable-grid {
  width: 100% !important;
}

.sortable-row {
  height: 100% !important;
  padding: 0 !important;
  margin: 0 !important;
  display: block !important;
}

.sortable-item {
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.0-beta.1/themes/smoothness/jquery-ui.css" rel="stylesheet" />
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<ul class="sortable sortable-grid" data-bind="template: { name: 'gridTmpl', foreach: gridItems, templateOptions: { parentList: gridItems} }, sortableList: gridItems">
</ul>

<script id="gridTmpl" type="text/html">
  <li class="sortable-row">
    <table style="width:100%">
      <tbody>
        <tr class="sortable container" data-bind="template: { name: 'rowTmpl', foreach: rowItems, templateOptions: { parentList: rowItems} }, sortableList: rowItems">
        </tr>
      </tbody>
    </table>
  </li>
</script>

<script id="rowTmpl" type="text/html">
  <td class="item" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
    <a href="#" data-bind="text: name, click: function() { viewModel.selectRowItem($data); }, visible: $data !== viewModel.selectedRowItem()"></a>
    <input data-bind="value: name, visibleAndSelect: $data === viewModel.selectedRowItem()" />
  </td>
</script>

您需要在应用绑定时实例化您的视图模型,否则不会调用构造函数。这就是为什么它需要是一个函数,而不是一个对象。

ko.applyBindings(new viewModel());

这是必须更改 viewModel 的方式:

var viewModel = function() {
    var self = this;

    self.gridItems= ko.observableArray(
        [{
          "rowItems": [{
            "name": "Item 1"
           }, {
            "name": "Item 2"
           }, {
            "name": "Item 3"
           }
          ]
       }, {
          "rowItems": [{
            "name": "Item 4"
          }]
       }, {
          "rowItems": [{
            "name": "Item 5"
          }, {
            "name": "Item 6"
          }]
       }]
    );
    self.selectedRowItem = ko.observable();
    self.selectRowItem = function(rowItem) {
        this.selectedRowItem(rowItem);
    };
};

Viewmodel 属性不能从 Viewmodel 外部更改或读取。要在您的视图模型中访问您的属性,您不能使用 viewmodel 因为它在 KO 绑定上下文中是未知的。您可以访问那些前缀 $root:

<script id="rowTmpl" type="text/html">
  <td class="item" data-bind="sortableItem: { item: $data, parentList: $data.parentList }">
    <a href="#" data-bind="text: name, click: function() { $root.selectRowItem($data); }, visible: $data !== $root.selectedRowItem()"></a>
    <input data-bind="value: name, visibleAndSelect: $data === $root.selectedRowItem()" />
  </td>
</script>

viewModel 未在您的视图模型中定义。当您调用 ko.applyBindings(viewModel); 时,名称 viewModel 不会被带入您的绑定引用中;我想你想要 $root 代替。你应该能够做到:

  <td class="item" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
    <a href="#" data-bind="text: name, click: function() { $root.selectRowItem($data); }, visible: $data !== $root.selectedRowItem()"></a>
    <input data-bind="value: name, visibleAndSelect: $data === $root.selectedRowItem()" />
  </td>