【问题标题】:Knockout: viewModel is not defined淘汰赛:未定义 viewModel
【发布时间】:2016-01-19 11:57:10
【问题描述】:

当使用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>

【问题讨论】:

  • 在你的错误信息中你有viewmodel 同时绑定viewModel。你能在你的代码中搜索viewmodel 吗?
  • 对不起,这是复制错误时的错误。原始错误消息显示viewModel
  • 顺便说一句,您的视图模型方法selectRowItem: function(rowItem) {this.selectedRowItem(rowItem);} 是多余的。只需使用click: $root.selectedRowItem

标签: knockout.js viewmodel knockout-2.0 knockout-3.0 knockout-templating


【解决方案1】:

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>

【讨论】:

  • 是的,这是可以接受的解决方案。我试图完全按照这个例子,但不知何故它没有参考viewModel
  • 没有必要做click: function() { $root.selectRowItem($data); }。一个简单的click: $root.selectedRowItem 就足够了。
【解决方案2】:

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

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>

【讨论】:

  • 这通常会有所帮助,但在这种情况下,它会给出另一个错误Uncaught TypeError: viewModel is not a function。我使用的参考解决方案有ko.applyBindings(viewModel);,它可以正常工作。
  • 这里更新了 JSFiddle 的变化,在这种情况下错误也保持不变jsfiddle.net/piglin/uun74v1j
  • 他没有使用构造函数。他只是在创建一个对象,这是完全有效的。
  • jsfiddle.net/uun74v1j/1 如@Roy J 所述,您无法在绑定中访问viewmodel。而是使用$root
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-09
  • 1970-01-01
  • 2016-08-13
  • 2015-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多