KnockoutJS foreach не перебирает коллекцию - PullRequest
0 голосов
/ 13 декабря 2018

По какой-то причине foreach в Knockout не выполняет итерацию по моему наблюдаемому массиву.

В моем HTML-коде у меня есть это, которое прекрасно работает с наблюдаемой моделью

<div class="field-group">
    <label class="popup-label" for="email">Email</label>
    <span class="email" data-bind="text: masterVM.employeeVM.Email"></span>
</div>

Но вта же модель, этот код не работает

<ul data-bind="foreach: { data: masterVM.employeeVM.Tags, as: 'tag' }">
    <li>
        <span class="popup-tag" data-bind="text: tag.tagName"><i class="zmdi zmdi-delete"></i></span>
    </li>
</ul>

Есть две модели

Сотрудник

var observableEmployee = function(id, email, tags) {
    var self = this;

    self.Id = ko.observable(id);
    self.Email = ko.observable(email); 

    self.Tags = ko.observableArray(ko.utils.arrayMap(tags, function(item) {
        return new observableTag(item.Id, item.EmployeeId, item.TagId, item.tagName)
    }));

    self.errors = ko.validation.group(this, {
        deep: true
    });

    self.isValid = ko.computed(function() {
        return self.errors().length > 0 ? false : true;
    });
}

И тег

var observableTag = function(id, employeeId, tagId, tagName) {
    var self = this;

    self.Id = ko.observable(id);
    self.employeeId = ko.observable(employeeId);
    self.tagId = ko.observable(tagId);
    self.TagName = ko.observable(tagName);

    self.errors = ko.validation.group(this, {
        live: true
    });

    self.isValid = ko.computed(function() {
        return self.errors().length > 0 ? false : true;
    });
}

Ифункция-обработчик

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

self.getEmployeeDetails = function (header) {
    $.ajax({
        url: masterVM.controller.renderEmployeeDetails,
        dataType: 'json',
        contentType: 'application/json',
        type: 'POST',
        data: JSON.stringify({ id: header.data("employeeid") }),
        success: function (result) {                

            masterVM.employeeVM = new observableEmployee(
                result.model.Id,
                result.model.Email,
                result.model.Tags
            );
            ko.applyBindings(masterVM, $("#employee-planning-selected")[0]);

            //header.parent().addClass('open');
            //header.next().slideDown('normal');

            //hideLoader(header);
            console.log('get employee details');
            $(document).on('click', "div.employee", onNameCardClick);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            alert('Error!');
        }
    });

}}

В моем HTML-файле

    <script>
        masterVM = {
            controller: {
                renderEmployeeDetails: '@(Html.GetActionUrl<EmployeesController>(c => c.RenderEmployeeDetails(0)))'
            },
            employeeHandler: new employeeHandler(),
            employeeVM: new observableEmployee(0, '', '', '', '')
    }
    ko.applyBindings(masterVM);
</script>

Пробовал что-то вроде этого, а еще ничего

<!--ko foreach: employeeVM.Tags -->
    <span data-bind="text: $data.Tags"></span>
<!-- /ko -->

И нет, нетошибки в консоли , я использовал отладчик контекста KnockouJS, который показывает мне, что в этой коллекции есть элементы, даже когда я пытаюсь отобразить их как объект, он показывает мне список из 4 элементов.

Версия нокаута: 2.3.0

1 Ответ

0 голосов
/ 13 декабря 2018

1) .Если вы связываете masterVM объект в ko.applyBindings(masterVM), вам не нужно снова указывать этот объект в ваших привязках данных.

Итак, это должно быть

foreach: { data: employeeVM.Tags, as: 'tag' }

А не

foreach: { data: masterVM.employeeVM.Tags, as: 'tag' }

(я не уверен, как работает первый data-bind="text: masterVM.employeeVM.Email")


2) .Вам не нужно звонить applyBindings более одного раза.Если вы хотите обновить объект сотрудника, вы можете превратить ваш employeeVM в наблюдаемый и продолжать обновлять его внутри метода getEmployeeDetails.


3) Ваш синтаксис потока управления без контейнера не будет работать.(<!--ko foreach: employeeVM.Tags -->).Внутри этого foreach $data является текущим Tag объектом в контексте .Итак, это должно быть <span data-bind="text: $data.TagName"></span>


Вот минимальная версия кода.Нажмите «Выполнить фрагмент кода», чтобы проверить его.Когда вы нажимаете кнопку Update employee, я обновляю наблюдаемую employeeVM, и данные снова отображаются.Без звонка applyBindings снова

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

  self.getEmployeeDetails = function(header) {
    var newEmployee = new observableEmployee(0, 'newEmployee@xyz.com', [{
      Id: 3,
      EmployeeId: 3,
      TagId: 3,
      tagName: 'Tag Name 3'
    }]);
  
   // You need to use employeeVM(newEmployee) instead of employeeVM = newEmployee
   // Because employeeVM is an observable.
    masterVM.employeeVM(newEmployee);
  }
}

var observableEmployee = function(id, email, tags) {
  var self = this;

  self.Id = ko.observable(id);
  self.Email = ko.observable(email);

  self.Tags = ko.observableArray(ko.utils.arrayMap(tags, function(item) {
    return new observableTag(item.Id, item.EmployeeId, item.TagId, item.tagName)
  }));
}

var observableTag = function(id, employeeId, tagId, tagName) {
  var self = this;

  self.Id = ko.observable(id);
  self.employeeId = ko.observable(employeeId);
  self.tagId = ko.observable(tagId);
  self.TagName = ko.observable(tagName);
}

var masterVM = {
  controller: {
    renderEmployeeDetails: ''
  },
  employeeHandler: new employeeHandler(),
  // change this to an observable
  employeeVM: ko.observable(new observableEmployee(0, 'abc@xyz.com', [{
    Id: 1,
    EmployeeId: 1,
    TagId: 1,
    tagName: 'Tag name 1'
  }]))
}

ko.applyBindings(masterVM);

document.getElementById("button").addEventListener("click", function(e) {
  masterVM.employeeHandler.getEmployeeDetails()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="field-group">
  <label class="popup-label" for="email">Email:</label>
  <span class="email" data-bind="text: employeeVM().Email"></span>
</div>

<ul data-bind="foreach: { data: employeeVM().Tags, as: 'tag' }">
  <li>
    <span class="popup-tag" data-bind="text: tag.employeeId"></span> <br>
    <span class="popup-tag" data-bind="text: tag.tagId"></span><br>
    <span class="popup-tag" data-bind="text: tag.TagName"></span>
  </li>
</ul>

<button id="button">Update employee</button>
...