0

Я использую плагин для сопоставления нокаутов для обновления пользовательского интерфейса с помощью JSON, извлекаемого с сервера каждые 3 секунды. Пользовательский интерфейс состоит из некоторых вложенных привязок foreach. Тем не менее, кажется, что все во всех привязках foreach полностью удаляется и повторно отображается при каждом обновлении, даже если ничего не изменилось.Нокаут Картирование повторного рендеринга все

var testData = { 
    Answers: [], 
    Inspectable: { 
     Categories: [{ 
      Id: 1, 
      Name: "Test Category", 
      Questions: [{ 
       Id: 1, 
       Text: "Test Question", 
       Active: true, 
       Answers: [{ 
        Text: "Test Answer", 
        Id: 1 
       }] 
      }] 
     }] 
    } 
}; 

function ViewModel() { 

    var self = this; 

    this.refreshUrl = $("[data-view=edit]").data("url"); 

    this.refresh = function(callback) { 
     $.get(self.refreshUrl, function(data) { 
      //Ignoring actual JSON data for testing 
      ko.mapping.fromJS(testData, {}, self); 
      if (typeof callback == "function") { 
       callback.call(self); 
      } 
     }); 
    } 

    this.addedQuestion = function() { 
     // Gets called for every question every refresh by afterRender 
     // Never gets called at all by afterAdd 
    } 

}; 

var refreshing = false, handler; 
window.viewModel = new ViewModel(); 

//Initialize the UI after initial AJAX is completed 
viewModel.refresh(function() { 

    ko.applyBindings(this); 

     $(document).on("click", ".add-question", function() { 
     if (!refreshing) { 
      handler = setInterval(viewModel.refresh, 3000); 
      refreshing = true; 
     } 
    }); 
}); 

Кто-нибудь видит что-либо в этом роде?

EDIT

Я редактировал сценарий, чтобы использовать статический объект JavaScript. Он все еще обновляет каждое обновление. Также обновлено до Knockout 2.3.0. Вот вид:

<!-- ko foreach: Inspectable.Categories --> 
     <div class="row row-fluid space-above"> 
      <h4 class="orange" data-bind="text: Name"></h4> 
      <!-- ko foreach: { data: Questions, afterRender: $root.addedQuestion } --> 
       <!-- ko if: Active() || ~$.map($root.Answers(), function(a) { return a.Id() == Id() }) --> 
        <div class="question space-above"> 
         <p><strong data-bind="text: Text"></strong></p> 
         <div class="answers" data-bind="foreach: Answers"> 
          <!-- ko if: $parent.AllowMultiple --><label class="checkbox"><input type="checkbox" data-url="<%= Url.Action("AddOrRemoveAnswer") %>" data-bind="attr: { value: Id, name: 'question-' + $parent.Id() }"/><!-- ko text: Text --><!-- /ko --></label><!-- /ko --> 
          <!-- ko ifnot: $parent.AllowMultiple --><label class="radio"><input type="radio" data-url="<%= Url.Action("AddOrRemoveAnswer") %>" data-bind="attr: { value: Id, name: 'question-' + $parent.Id() }"/><!-- ko text: Text --><!-- /ko --></label><!-- /ko --> 
         </div> 
        </div> 
       <!-- /ko --> 
      <!-- /ko --> 
      <!-- ko if: Questions().length == 0 --> 
       <div class="question space-above"> 
        <p><strong>No questions in this category.</strong> <a class="add-question" data-bind="attr: { href: '<%= Url.Action("Create", "Questions") %>?categoryId=' + Id() + '&inProgress=true' }" target="_blank">Add some.</a> </p> 
       </div> 
      <!-- /ko --> 
      <!-- ko if: Questions().length > 0 --> 
       <div class="question space-above"> 
        <a class="add-question" data-bind="text: 'New question for ' + Name(), attr: { href: '<%= Url.Action("Create", "Questions") %>?categoryId=' + Id() + '&inProgress=true' }" target="_blank"></a> 
       </div> 
      <!-- /ko --> 
     </div> 
    <!-- /ko --> 
+0

Я не вижу очевидной проблемы, которая могла бы вызвать вашу проблему. Единственное дело в том, что вы, кажется, не устанавливаете 'refreshing' обратно на false в любом месте. В любом случае, некоторый код представления и макет данных, чтобы воспроизвести эту проблему, помогут нам вам помочь. – Jeroen

+1

Вы пытались «изолировать» проблему? Я хочу удалить некоторые привязки, чтобы указать проблему. – Damien

ответ

3

Причина крепления становятся полностью удалены и повторно отображаются с каждым обновлением, даже если ничего не изменились, потому что вы звоните ko.applyBindings каждое обновление.

Вы не хотите, чтобы ko.applyBindings внутри вашей функции обновления. Вы хотите вызвать ko.applyBindings один раз, и все. После этого KO будет обрабатывать обновление DOM при обновлении данных (или наоборот). Вы просто хотите:

var testDate = { ... }; 
function ViewModel() { ... } 
var viewModel = new ViewModel(); 
ko.applyBindings(viewModel); 
viewModel.refresh(function() { 
    // ko.applyBindings(this); <- Get rid of this here, no bueno 
    $(document).on("click", ....); 
}); 

И все. Каждый раз, когда вы вызываете обновление, вы получаете данные с сервера, который обновляет viewModel. KO, в свою очередь, обновит DOM по мере необходимости, если значения будут обновлены. Когда вы вызываете applyBindings после всего этого, KO проходит через DOM с помощью широкого слова, обновляя все ваши привязки, нужно ли это или нет.


Быстрый Jquery Совет:

$.get(...) возвращает promise. Есть три важных функции, обещание возвращения, .done(), .fail(), and .always(). Для вашего this.refresh() функции, возвращающие результат $ .get так:

this.refresh = function() { 
    return $.get(...); 
}; 

Тогда что когда-либо называет это будет сделать это:

viewModel.refresh().done(function(data){ 
    // Callback function 
}); 

Теперь вам не нужно передавать функцию обратного вызова, проверять, является ли обратный вызов типом функции, или беспокоиться о работе с функцией обратного вызова в случае сбоя. Это также универсально, и вы можете продолжать возвращать обещание по цепочке функций, которые будут ждать до тех пор, пока запрос $ .get не будет устранен, прежде чем они выполнит свою функцию.

+0

Я полностью изменил эту функциональность, но все это звучит хорошо. Благодарю. – Andrew

+0

Думал, что это могло быть так, учитывая дату, когда это было опубликовано. Рад, что вы смогли выработать решение. – nwayve

Смежные вопросы