1

Я использую нокаут (KO) в своем проекте MVC. Я создаю модель MVC (для сетки) на сервере и передаю ее в представление. По представлению он сериализуется и преобразуется в модель KO (с использованием ko.mapping), которая, в свою очередь, используется для привязки. Это связывание затем используется в HTML для создания сетки.Нокаутное отображение наблюдаемого массива не работает

Это как моя модель сетки MVC выглядит в свою очередь, преобразуется в соответствующей модели KO по ko.mapping:

public class GridModel 
    { 
     /// <summary> 
     /// Grid body for the grid. 
     /// </summary> 
     public GridBodyModel GridBodyModel { get; set; } 

     /// <summary> 
     /// Grid context. 
     /// </summary> 
     public GridContext GridContext { get; set; } 

     /// <summary> 
     /// Grid header for the grid. 
     /// </summary> 
     public GridHeaderModel GridHeader { get; set; } 
    } 

public class GridBodyModel 
    { 
     /// <summary> 
     /// List of grid body rows. 
     /// </summary> 
     public IList<GridRowModel> Rows { get; set; } 
    } 


public class GridContext 
    { 
     /// <summary> 
     /// Total number of pages. Read-only. 
     /// </summary> 
     public int TotalPages{ get; set; } 
    } 

public class GridHeaderModel 
    { 
     /// <summary> 
     /// List of grid header cells. 
     /// </summary> 
     public IList<GridHeaderCellModel> Cells { get; set; } 
    } 

Как видно основной класс модели GridModel состоит из следующих классов, которые present as properties:

GridBodyModel: содержит список строк, которые будут отображаться в теле сетки.

GridContext: Общее количество страниц. Он имеет и другие свойства, но это выходит за рамки этого обсуждения.

GridHeaderModel: содержит список ячеек, которые должны отображаться в заголовке сетки.

Тогда у меня есть этот скрипт, который будет выполняться при новой загрузке страницы.

$(document).ready(function() { 
     // Apply Knockout view model bindings when document is in ready state. 
     ko.applyBindings(Global_GridKOModel, document.getElementById("gridMainContainer")); 
    }); 

// Serialize the server model object. It will be used to create observable model. 
Global_GridKOModel = ko.mapping.fromJS (<%= DataFormatter.SerializeToJson(Model) %>); 

Global_GridKOModel является глобальной переменной Javascript. Модель - модель сетки MVC, поступающая с сервера.

Пользователь может выполнить дальнейший поиск на странице еще раз. Я обрабатываю это путем публикации новых критериев поиска через Ajax. На этом посту создается новая модель MVC и отправляется обратно как ответ Ajax. Эта новая модель MVC затем просто используется для обновления Global_GridKOModel с использованием ko.mapping, которая, в свою очередь, обновляет сетку (с новыми данными), которая была построена ранее при новой загрузке страницы. Вот как я это делаю.

$.ajax({ 
     url: destUrl, 
     data: dataToSend 
     success: function (result) { 
      ko.mapping.fromJS(result, Global_GridKOModel); 
     }, 
     error: function (request, textStatus, errorThrown) { 
      alert(request.statusText); 
     } 
    }); 

Все работает нормально, за исключением следующего сценария.

Выполняется запрос Ajax, для которого результат не возвращается. I.e. GridBodyModel и GridHeaderModel имеет значение null в модели GridModel. Эта сетка времени правильно показывает, что запись не найдена. Это верно. Это происходит с помощью следующей привязки HTML.

<!-- When no record is found. --> 
<div data-bind="ifnot: GridContext.GridPager.TotalPages() > 0"> 
    No record(s) were found. 
</div> 

<!-- This is actual grid table container. This is binded when records are found --> 
<div data-bind="if: GridContext.TotalPages() > 0"> 

Grid construction happens here 

</div> 

Теперь после того, как это, если другой запрос Ajax сделан, но на этот раз записи возвращаются (я проверил реакцию с поджигатель и он подтвердил, что записи действительно возвращается). Это временное построение сетки происходит, когда доступны различные наблюдаемые массивы. Например, для построения пейджера для следующей сетки является частью HTML-привязки, которую я написал.

<td data-bind="attr:{colspan: GridHeader.Cells().length }"> 

На этот раз KO выдает следующую ошибку, которая видна в firebug.

Невозможно проанализировать привязки. Сообщение: TypeError: GridHeader.Cells не является функцией; Наручников значение: атр: {Colspan:. GridHeader.Cells() длина}

Он отлично работает так долго есть записи возвращаются, но он ломает после записи не возвращается, как описана выше.Обратите внимание, что GridHeader был недействителен в более раннем ответе, когда никаких записей не было возвращено. Я чувствую запах чего-то подозрительного в ko.mapping. Я думаю, что есть проблема при отображении наблюдаемого массива.

Итак, что я не делаю правильно? Кто-нибудь, пожалуйста?

Пожалуйста, не стесняйтесь просить разъяснений, если я не упомянул ничего ясно.

Заранее благодарен.

ответ

2

Фактическая причина поведения заключается в том, что первоначально GridHeader является объектом javascript, но когда ваше преобразование обратного вызова с нулевым значением для свойства GridHeader, оно станет наблюдаемым с нулевым значением, а во всех будущих обновлениях оно будет по-прежнему наблюдаемым.

обходные являются (выбрать один):

  1. Не возвращать нулевые значения для дочерних моделей (возврат пустых объектов вместо) - я думаю, что это предпочтительный способ
  2. Или - перед вызовом карту, запустить Global_GridKOModel .GridHeader = null; (это гарантирует, что после обновления GridHeader не будет наблюдаемым)
  3. Или - после сопоставления вызовите Global_GridKOModel.GridHeader = ko.unwrapObservable (Global_GridKOModel.GridHeader);

Полное объяснение поведения у вас есть в коде ниже (скопировать на jsfiddle - http://jsfiddle.net/nickolsky/zrBuL/9/)

console.log('step1'); 
var vm = ko.mapping.fromJS({ body: { children: [ 1, 2, 3]} }); 
console.log(vm.body.children()[1]); //prints 2 

console.log('step2'); 
ko.mapping.fromJS({ body: { children: [ 4, 5, 6]} }, vm); 
console.log(vm.body.children()[1]); //prints 5 

console.log('step3'); 
//after next call, vm.body is no longer variable - it is now observable 
ko.mapping.fromJS({ body: null }, vm); 
console.log(vm.body); //prints observable 
console.log(vm.body()); //prints null 

console.log('step4'); 
//and it will remain observable for next call 
ko.mapping.fromJS({ body: { children: [ 7, 8, 9]} }, vm); 
console.log(vm.body().children()[1]); //prints 8 
console.log(vm.body().children().length); //prints 3 
console.log(vm.body); //prints observable function body 
console.log(vm.body()); //prints object 

//workaround for null issue - we can reset it to be null instead of null observable 
//and it will get original behavior 
vm.body = null; 
console.log('step5');  
ko.mapping.fromJS({ body: { children: [ 7, 8, 9]} }, vm); 
console.log(vm.body.children()[1]); //prints 8 - body is no longer observable again 
​ 

Для меня это выглядит как дизайн вопросу отображения плагина, я не смог найти способ настроить сопоставление свойств дочернего вида, чтобы убедиться, что они не станут наблюдаемыми, если они равны нулю (функциональность «копировать» сделает все внутреннее содержимое ненаблюдаемым, к сожалению).

+0

Большое спасибо Артему за то, что вы нашли время для чтения. Да, вы правы, null не следует возвращать. Я тоже понял это вчера вечером, но не публиковал. Пожалуйста, прочтите мой ответ ниже, где я упомянул о своем наблюдении, и он отражает именно то, что вы сказали. – Aum

1

Я понял это вчера вечером. На самом деле это была основная ошибка, которую я делал. Я пытался получить доступ к массиву после установки его в null. Я объясню это как.

Когда результат был возвращен в это время, GridHeaderModel и ячейки IList в нем были определены, то есть не были нулевыми. В это время ko.mapping смог преобразовать модель и создать модель и ее внутри. Но когда был сделан какой-либо запрос ajax, в котором был возвращен результат noo, а значение GridHeaderModel было нулевым, очевидно, что ячейки IList тоже были нулевыми. В то время ko.mapping сделал то же самое, модель KO, которую он обновил, также установил значение GridHeaderModel равным нулю, а наблюдаемый массив Cell внутри не присутствовал, что равно нулю. Теперь, когда я сделал еще один запрос ajax с некоторыми возвращенными записями, ko.mapping попытался обновить наблюдаемый массив Ячейки, которые не существовали (или были установлены в NULL) в модели KO на клиенте, это не удалось. Если вместо ajax это была новая загрузка страницы, все бы сработало. Таким образом, решение не должно возвращать какое-либо перечисление (те, которые будут преобразованы в наблюдаемый массив), не инициализированные клиенту для обновления модели KO. Следовательно, когда запись не должна была быть возвращена, я убедился, что GridHeaderModel не является нулевым, а также убедитесь, что ячейки IList инициализированы, хотя в ней не было никакого элемента. Это поставило проблему.

Эта проблема может быть рассмотрена в следующем примере.

public class Demo 
{ 
    public IList<string> DemoArray; 
} 

public class DemoUser 
{ 
    public void UseDemo() 
     { 
      var demoInstance = new Demo(); 
      demoInstance.DemoArray.Add("First Element"); 
     } 
} 

Здесь в методе UseDemo() мы инициализирован демо экземпляра класса, но и IList DemoArray остается не-инициализирован. Поэтому, когда мы пытаемся получить к нему доступ, будет выбрано исключение. Это то, что происходило в моем случае.В первом ответе ajax я устанавливал наблюдаемый массив на нуль, то есть не инициализировал его, а затем в следующем ответе ajax я пытался получить к нему доступ.

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