Можно ли выйти из привязки ко foreach до завершения рендеринга?

Это мой первый пост на SO, поэтому, пожалуйста, будьте осторожны со мной:)

Я создаю веб-приложение с Durandal.js, и у меня возникает ситуация, когда я запускаю простое связывание данных foreach нокаута, которое перебирает ko.observableArray и создает дочерние представления для каждого элемента в массиве. Этот массив состоит из нескольких дочерних view / viewmodels, которые я хочу визуализировать на странице, но массив может содержать большое количество элементов за раз (иногда более 400). Приложение также может иметь несколько страниц результатов, и я обрабатываю подкачку страниц, заменяя содержимое ko.observableArray при нажатии на новую страницу.

Проблема, с которой я иногда сталкиваюсь (это не каждый раз, но это повторяется), заключается в том, что я получаю ошибку, вызванную knockout.js (я использую knockout v3.0.0), который говорит следующее: "Uncaught TypeError: Невозможно прочитать свойство 'insertBefore' из null".

Вот часть метода prepend для виртуальных элементов knockout.js, которая выдает ошибку:

prepend: function(containerNode, nodeToPrepend) {
        if (!isStartComment(containerNode)) {
            if (containerNode.firstChild)
                containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
            else
                containerNode.appendChild(nodeToPrepend);
        } else {
            // Start comments must always have a parent and at least one following sibling (the end comment)
            containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
        }
    }

Покопавшись в нокаут-коде, я вижу, что то, что называется null, является containerNode.parentNode. В моем фрагменте HTML-кода ниже, containerNode.parentNode - это #photosContainer, поэтому я нахожу странным, что это null, поскольку foreach, очевидно, все еще работает (так как я получаю сообщение об ошибке для каждого элемента, который еще не отображался).

Вот некоторые фрагменты кода из моего приложения Durandal: HTML-код из моего представления, который содержит привязку foreach, которая перебирает observableArray 'photos' и составляет дочернее представление:

<div id='photosContainer' data-bind="visible: !video_mode_on(), foreach: photos">
    <!-- ko compose: $data --><!--/ko-->
</div>

и вот код из моей viewmodel, который в основном заменяет содержимое observableArray 'photos':

newHome.prototype.getPhotos = function() {
    var self = this;

    var defs = [];
    var arr = [];
    var args, newArr;

    self.rawPhotos().forEach( function( set ) {
        defs.push( self.some_more_all( set.mf, set.images ) );
    });
    $.when.apply($, defs).done(function( res ) {
        args = Array.prototype.slice.call(arguments, 0);
        newArr = args.sort();

        newArr.forEach( function( set ) {
            set.arr.forEach( function( p ) {
                arr.push( self.addPhoto( p, self.mfOwnedByViewer( set.mf ) ? { ownedByViewer: true } : { ownedByViewer: false, owner_uuid: set.mf.owner_uuid } ) );
            });
        });

        self.photos( arr );
    });
};

Кажется, ошибка, о которой я упоминал, возникает, когда я перехожу на другую страницу результатов до того, как привязка foreach завершилась в моем представлении. Итак, мой вопрос: есть ли способ остановить или прервать привязку foreach в моем представлении, чтобы я мог перейти на другую страницу результатов, не дожидаясь отображения текущего списка результатов?

Также некоторые вещи, которые я пробовал, но не помогли: я сначала удалял все содержимое массива photos, а затем вставлял в него непосредственно при запуске моей функции getPhotos(), но думал, что ошибка могла быть вызвана пустой массив. Я также попытался запустить функцию knoout ko.virtualElements.emptyNode() в #photosContainer перед тем, как выполнить итерацию по массиву rawPhotos() в моей функции getPhotos(), чтобы посмотреть, не приведет ли это к очистке незаконченных визуализаций в привязке foreach представления, но похоже, это тоже ничего не делает. Я попытался обернуть мою компоновочную привязку функцией проверки, чтобы увидеть, существует ли узел #photosContainer следующим образом:

<div id='photosContainer' data-bind="visible: !video_mode_on(), foreach: photos">
    <!-- ko if: $parent.checkForPhotosContainer() -->
        <!-- ko compose: $data --><!--/ko-->
    <!-- /ko -->
</div>

и функция проверки:

newHome.prototype.checkForPhotosContainer = function() {
    var self = this;

    var el = $(self.element).find('#photosContainer');

    if( el.length ) {
        return true;
    } else {
        return false;
    }
};

Пока что ничего не сработало, и у меня заканчиваются идеи (и волосы!!). Любая помощь будет принята с благодарностью!

1 ответ

Кажется, что нет способа остановиться или вырваться из ко foreach связывание, как указано в ссылке Магнуса Алина на Как замкнуть Array.forEach, например, вызвать прерывание? Однако основная причина моей проблемы была связана с тем, что я использовал виртуальные элементы, а не обычные элементы.

Для тех, кто сталкивается с проблемой нокаута, жалуется на "Uncaught TypeError: Невозможно прочитать свойство 'insertBefore' of null" и использует виртуальные элементы для создания дочерних представлений в ko.observableArray с использованием foreach связывание, попробуйте заменить ваши виртуальные элементы нормальными элементами.

Поэтому замените HTML-код на ваш взгляд следующим образом:

<div data-bind="foreach: myObservableArray">
    <!-- ko compose: $data --><!--/ko-->
</div>

с чем-то вроде этого:

<div data-bind="foreach: myObservableArray">
    <div data-bind="compose: $data"></div>
</div>
Другие вопросы по тегам