KnockoutJS: показать HTML - заполнить HTML - PullRequest
0 голосов
/ 16 сентября 2018

Мне нужно добиться интересного эффекта с 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 голосов
/ 17 сентября 2018

Вот пример, который предполагает:

  • Есть один вызов, который сообщает нам, сколько элементов мы в конечном итоге отобразим
  • Для каждого из этих элементов есть вызов, который должензавершен, чтобы отобразить фактический пользовательский интерфейс

Я сделал отдельные элементы ответственными за их собственную загрузку данных.Это облегчает запись полученных данных, которые могут быть возвращены в любом порядке, в соответствующий элемент списка.

Шаги, которые вы увидите при визуализации интерфейса:

  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>
0 голосов
/ 17 сентября 2018

Эта строка вашего кода явно неверна:

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

Должно быть:

value.title(data.results[0].title);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...