KnockoutJS:显示HTML-填充HTML

时间:2018-09-16 20:08:07

标签: javascript ajax knockout.js

我需要用KnockoutJS实现有趣的效果。想象一下,我有最简单的模型:

var Item = function () {
    var self = this;
    self.title = ko.observable("");
};

当然我有一个ViewModel:

var ItemList = function () {
var self = this;
self.list = ko.observableArray();
}
然后,乐趣开始了。在ViewModel的这里,我得到了一些HTML标记块。有多少个未知数。对于每个块,我需要立即显示HTML标记:

var blocks = await getBlocks();
$.each(blocks, function (index, value) {
    //At this point (as planned), the blocks should be displayed 
    //together with a rotating loading animation.
    self.list.push(new Item());
});

下一步(再次在ViewModel中),我需要获取数据以填充以下块:

$.each(self.list(), async function (index, value) {
    var data = await getData("some-url");
    //At this point, the blocks should be filled with data, 
    //and the spinning loading animation should disappear.
    self.list().push(data.results[0].title);
});
现在一起:

 var Item = function () {
    var self = this;
    self.title = ko.observable("");
};

var ItemList = function () {
    var self = this;
    self.list = ko.observableArray();
    var blocks = await getBlocks();
    $.each(blocks, function (index, value) {
        self.list.push(new Item());
    });

    $.each(self.list(), async function (index, value) {
        var data = await getData("some-url");
        self.list().push(data.results[0].title);
    });
};

ko.applyBindings(new ItemList());
所有这些丑陋的HTML看起来非常简单:

<div data-bind="foreach: list">
    <span data-bind="text: title"></span>
</div>
此方法无法正常工作。而且我不知道如何使用KnockoutJS做到这一点。有可能吗?

2 个答案:

答案 0 :(得分:2)

这行代码显然是错误的:

self.list().push(data.results[0].title);

应该是:

value.title(data.results[0].title);

答案 1 :(得分:1)

这是一个假设的例子:

  • 有一个电话告诉我们最终将渲染多少个项目
  • 对于每个项目,都有一个调用需要完成以呈现实际的UI

我已经使各个项目负责自己的数据加载。这样可以更轻松地将接收到的数据(可以以任何顺序返回)写入相应的列表项。

您将看到UI呈现的步骤:

  1. 正在检索用于渲染初始列表的数据的调用:显示常规加载消息
  2. 我们为要检索的每个项目创建了一个列表项目。所有项目均已开始加载数据,但显示加载状态直到完成
  3. 一个接一个地完成单个数据的加载,列表元素接收其内容。

const { getProductIdsAsync, getProductAsync } = apiMethods();

function Item(id) {
  this.name = ko.observable(null);
  this.loading = ko.pureComputed(() => !this.name());
  
  getProductAsync(id).then(this.name);
};

Item.fromId = id => new Item(id);

function List() {
  this.items = ko.observableArray([]);
  this.loading = ko.pureComputed(() => !this.items().length);
  
  getProductIdsAsync()
    .then(ids => ids.map(Item.fromId))
    .then(this.items);
}

ko.applyBindings({ list: new List() });


// Mocking of some async API, not relevant to question:
function apiMethods() {
  const productCatalogDB = {
    1: "Apples",
    2: "Oranges",
    3: "Bananas"
  };

  const delayed = (f, minT, maxT) => 
    (...args) => 
      new Promise(res => {
        setTimeout(
          () => res(f(...args)),
          minT + Math.random() * (maxT - minT)
        )
      });

  return {
    getProductIdsAsync: delayed(
      () => Object.keys(productCatalogDB), 500, 1200),
    getProductAsync: delayed(
      id => productCatalogDB[id], 500, 1500)
  };
}
.loading {
  opacity: .6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<p data-bind="visible: list.loading">
  Loading catalog ids...
</p>

<ul data-bind="foreach: list.items">
  <li data-bind="css: { loading: loading }, text: name() || 'Loading...'">
    
  </li>
</ul>