Нокаут: viewModel не определен

При использовании этого 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>


person Peter G.    schedule 19.01.2016    source источник
comment
В вашем сообщении об ошибке у вас есть viewmodel при привязке viewModel. Можете ли вы найти свой код для viewmodel ?   -  person Nikolay Ermakov    schedule 19.01.2016
comment
Извините, это была ошибка при копировании ошибки. Исходное сообщение об ошибке говорит viewModel.   -  person Peter G.    schedule 19.01.2016
comment
Кроме того, ваш метод модели представления selectRowItem: function(rowItem) {this.selectedRowItem(rowItem);} лишний. Просто используйте click: $root.selectedRowItem.   -  person Tomalak    schedule 19.01.2016


Ответы (2)


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>
person Roy J    schedule 19.01.2016
comment
Да, это приемлемое решение. Пробовал в точности следовать примеру, но как-то не получилось применительно к viewModel. - person Peter G.; 19.01.2016
comment
Нет необходимости делать click: function() { $root.selectRowItem($data); }. Достаточно простого click: $root.selectedRowItem. - person Tomalak; 19.01.2016

Вам нужно создать экземпляр вашей модели представления при применении привязок, иначе конструктор не вызывается. Вот почему это должна быть функция, а не объект.

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, так как это неизвестно в контексте привязок 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>
person connexo    schedule 19.01.2016
comment
Обычно это помогает, но в этом случае выдает другую ошибку Uncaught TypeError: viewModel is not a function. Эталонное решение, которое я использую, имеет ko.applyBindings(viewModel); и работает нормально. - person Peter G.; 19.01.2016
comment
Вот обновленный JSFiddle с изменениями, ошибка остается такой же и в этом случае jsfiddle.net/piglin/uun74v1j - person Peter G.; 19.01.2016
comment
Он не использует конструктор. Он просто создает объект, что вполне допустимо. - person Roy J; 19.01.2016
comment
jsfiddle.net/uun74v1j/1, как указано @Roy J, вы не можете получить доступ к viewmodel в своих привязках . Вместо этого используйте $root. - person connexo; 19.01.2016