Реализация Knockout.js деструктивный цикл foreach

#javascript #arrays #knockout.js

#javascript #массивы #knockout.js

Вопрос:

Вот html-код с элементами привязки к данным:

 div data-bind="foreach: clientRequests" id="test2">
           <div class="list-group" >
               <a href="#" class="list-group-item active"><b data-bind="text: client"></b></a>
               <a href="#" class="list-group-item"><b>Priority: </b><b data-bind="text: client_priority"></b></a> 
               <a href="#" class="list-group-item"><b>Title: </b><b data-bind="text: title"></b></a>
               <a href="#" class="list-group-item"><b>Description: </b><b data-bind="text: description"></b></a> 
               <a href="#" class="list-group-item"><b>Product Area: </b><b data-bind="text: product_area"></b></a>
               <a href="#" class="list-group-item"><b>Target Date: </b><b data-bind="text: target_date"></b></a>
               <a href="#" class="list-group-item"><b>Ticket URL: </b><b data-bind="text: ticket_url"></b></a>
           </div>
        </div>
  

Вот как я передаю массив, вызываемый requestsArray в foreach цикл:

 ko.cleanNode(document.getElementById('test2'));

        ko.applyBindings({
            clientRequests: requestsArray
        }, document.getElementById('test2'));
  

При различных вызовах AJAX возвращаются разные массивы запросов. Например, после начальной загрузки страницы выполняется вызов AJAX, который получает один экземпляр requestArray, который может содержать 10 элементов. Цикл foreach, похоже, ведет себя так, как ожидалось, и все 10 элементов в массиве заполняются на странице. Затем выполняется второй вызов AJAX, но на этот раз массив может содержать только 5 элементов. Происходит то, что каждый элемент повторяется дважды, и в общей сложности на странице появляется 10 элементов.

Проблема, по-видимому, заключается в том, что, хотя ko.cleanNode(document.getElementById('test2')) вызывается перед:

 ko.applyBindings({
                clientRequests: requestsArray
            }, document.getElementById('test2'))
  

с каждым новым массивом количество HTML-элементов, созданных каждой foreach итерацией, продолжает увеличиваться с каждым новым массивом. С Vue.js , каждый раз, когда вы передаете новый массив в цикл привязки данных и for, он носит деструктивный характер и ничего не сохраняет от предыдущей итерации по массиву.

Очевидно, что использование ko.cleanNode здесь не работает в этом сценарии, и я знаю, что в документах есть пример, который делает то, что я считаю правильной процедурой, но только по одному элементу html за раз с помощью кнопки и self.array.remove(this) , и я не совсем уверен, как адаптировать его для полной очистки -из всех html-элементов, созданных foreach на основе итерации массива.

Комментарии:

1. почему вы не очищаете свой массив в своей модели каждый раз, когда вызываете Ajax?1

2. @Matt.kaaj Я делаю это, обновлю свой код в сообщении. Это не помогает с тем, что происходит с Knockout.js обязательный. Проблема не в обновлении самого массива, а в накоплении html-элементов, созданных с каждой итерацией foreach через каждый новый массив.

Ответ №1:

Я не понимаю, почему вам нужно повторно применять привязки вручную. Весь смысл модели представления с наблюдаемым массивом заключается в том, что knockout заботится об обновлениях данных за вас… Обычно, когда вы используете cleanNode , есть более простой способ сделать что-то.

Вы пробовали что-то подобное?

 // Apply bindings _once_, viewmodel instance does not change
// in between requests
ko.applyBindings(new ViewModel());


function ViewModel() {
  // Because the array is observable, knockout will
  // monitor for changes and update the UI
  this.requests = ko.observableArray([]);
  
  // The view model has the request method
  // the .done callback writes the results to the observable
  // requests array
  this.doRequest = function() {
    mockupAjaxGetter().done(this.requests);
  }.bind(this);
  
  // Do an initial request
  this.doRequest();
};



// Mockup code, just to produce some random numbers on a timeout
function mockupAjaxGetter() {
  var randomResults = [];
  for (var i = 0; i < Math.random() * 20; i  = 1) {
     randomResults.push(Math.random()); 
  }
  var cb;
  var applyCb = function() {
    if (cb) cb(randomResults); 
  }
  
  setTimeout(applyCb, 500);
  
  return {
    done: function(fn) { cb = fn; }
  }
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<ul data-bind="foreach: requests">
  <li data-bind="text: $data"></li>
</ul>
<button data-bind="click: doRequest">New request</button>