KnockoutJS:显示 HTML - 填充 HTML
KnockoutJS: show HTML - fill HTML
我需要用 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-markup:
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 做到这一点。这可能吗?
你的这行代码显然是错误的:
self.list().push(data.results[0].title);
应该是:
value.title(data.results[0].title);
这是一个假设的例子:
- 有一个电话告诉我们最终要渲染多少项目
- 对于其中的每一项,都需要完成一个调用才能呈现实际的 UI
我已经让各个项目负责自己的数据加载。这样可以更方便地将接收到的数据return以任意顺序写入相应的列表项。
您将看到 UI 渲染的步骤:
- 检索用于呈现初始列表的数据的调用正在加载:显示一般加载消息
- 我们为要检索的每个项目创建了一个列表项。所有项目都已开始加载数据,但在完成之前显示加载状态
- 单个数据加载完成,列表元素收到它们的内容。
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>
我需要用 KnockoutJS 实现一个有趣的效果。假设我有最简单的模型:
var Item = function () {
var self = this;
self.title = ko.observable("");
};
当然我有一个 ViewModel:
var ItemList = function () {
var self = this;
self.list = ko.observableArray();
}
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());
<div data-bind="foreach: list">
<span data-bind="text: title"></span>
</div>
你的这行代码显然是错误的:
self.list().push(data.results[0].title);
应该是:
value.title(data.results[0].title);
这是一个假设的例子:
- 有一个电话告诉我们最终要渲染多少项目
- 对于其中的每一项,都需要完成一个调用才能呈现实际的 UI
我已经让各个项目负责自己的数据加载。这样可以更方便地将接收到的数据return以任意顺序写入相应的列表项。
您将看到 UI 渲染的步骤:
- 检索用于呈现初始列表的数据的调用正在加载:显示一般加载消息
- 我们为要检索的每个项目创建了一个列表项。所有项目都已开始加载数据,但在完成之前显示加载状态
- 单个数据加载完成,列表元素收到它们的内容。
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>