Вот пример, который предполагает:
- Есть один вызов, который сообщает нам, сколько элементов мы в конечном итоге отобразим
- Для каждого из этих элементов есть вызов, который должензавершен, чтобы отобразить фактический пользовательский интерфейс
Я сделал отдельные элементы ответственными за их собственную загрузку данных.Это облегчает запись полученных данных, которые могут быть возвращены в любом порядке, в соответствующий элемент списка.
Шаги, которые вы увидите при визуализации интерфейса:
- Вызовдля извлечения данных для рендеринга загружается начальный список: отобразить общее сообщение о загрузке
- Мы создали элемент списка для каждого элемента, который мы извлекаем.Все элементы начали свою загрузку данных, но отображают состояние загрузки до тех пор, пока они не будут завершены
- Один за другим загрузка отдельных данных завершится, и элементы списка получат свое содержимое.
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>