Нокаут: 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="/bg#" 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="/bg#" 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="/bg#" 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