KnockoutJS foreach 不会遍历集合

Posted

技术标签:

【中文标题】KnockoutJS foreach 不会遍历集合【英文标题】:KnockoutJS foreach doesn't iterate through collection 【发布时间】:2019-05-14 20:49:08 【问题描述】:

由于某种原因,Knockout.js 中的 foreach 不会遍历我的可观察数组。

在我的 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

【问题讨论】:

我不知道这是否相关,但在您的绑定中,您对 tag.tagName 使用骆驼大小写,但在您的标签模型中,TagName 是 pascal-case。 感谢您指出,但实际上它在我的代码中正确输入(帕斯卡约定),但仍然没有工作。但是,下面 adiga 发布的 adiga 解决方案有效。 【参考方案1】:

1)。如果您在ko.applyBindings(masterVM) 中绑定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) 您的containerless control flow syntax 将不起作用。 (&lt;!--ko foreach: employeeVM.Tags --&gt;)。在这个 foreach 中,$data 是当前的 Tag 对象 in context。所以,应该是&lt;span data-bind="text: $data.TagName"&gt;&lt;/span&gt;

这是代码的最小版本。点击“运行代码sn-p”进行测试。当您单击 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>

【讨论】:

非常感谢!将employeeVM 变成observable 确实帮助并解决了我的问题。所以这就是问题所在。

以上是关于KnockoutJS foreach 不会遍历集合的主要内容,如果未能解决你的问题,请参考以下文章

使用 KnockoutJs 遍历嵌套的 Django REST Framework API

jquery中都有哪些方法可以遍历节点

可观察到的数组更改不会反映在第一次点击 - KnockoutJS

Knockoutjs 映射不会将 observables 添加到字符串数组中

为什么这个knockoutjs observableArray不会导致UI更新?

JQ基础方法