Как очистить/удалить наблюдаемые привязки в Knockout.js?

Я создаю функциональность на веб-странице, которую пользователь может выполнять несколько раз. Через действие пользователя объект/модель создается и применяется к HTML с помощью ko.applyBindings().

HTML, связанный с данными, создается с помощью шаблонов jQuery.

Пока все хорошо.

Когда я повторяю этот шаг, создавая второй объект/модель и вызываю ko.applyBindings(), я сталкиваюсь с двумя проблемами:

  • Разметка показывает предыдущий объект/модель, а также новый объект/модель.
  • Возникает ошибка javascript, относящаяся к одному из свойств объекта/модели, хотя она все еще отображается в разметке.

Чтобы обойти эту проблему, после первого прохода я вызываю jQuery.empty() для удаления шаблона HTML, который содержит все атрибуты привязки данных, так что он больше не находится в DOM. Когда пользователь запускает процесс для второго прохода, связанный с данными HTML снова добавляется в DOM.

Но, как я уже сказал, когда HTML снова добавляется в DOM и повторно привязывается к новому объекту/модели, он по-прежнему включает данные из первого объекта/модели, и я все еще получаю ошибку JS, которая не работает во время первого прохода.

Как представляется, вывод состоит в том, что Knockout поддерживает эти связанные свойства, даже если разметка удалена из DOM.

Итак, я ищу средство для удаления этих связанных свойств из Knockout; говорящий нокаутом, что уже нет наблюдаемой модели. Есть ли способ сделать это?

ИЗМЕНИТЬ

Основной процесс заключается в том, что пользователь загружает файл; сервер затем отвечает с помощью объекта JSON, связанный с данными HTML добавляется в DOM, тогда объектная модель JSON привязана к этому HTML с помощью

mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);

После того, как пользователь сделал некоторые варианты в модели, тот же объект отправляется обратно на сервер, HTML-код, связанный с данными, удаляется из DOM, и тогда у меня есть следующий JS

mn.AccountCreationModel = null;

Когда пользователь хочет сделать это еще раз, все эти шаги повторяются.

Я боюсь, что код слишком "задействован" для демонстрации jsFiddle.

Ответ 1

Пробовали ли вы использовать метод node для нокаута на вашем элементе DOM для удаления связанных с памятью объектов?

var element = $('#elementId')[0]; 
ko.cleanNode(element);

Затем повторное применение привязок нокаута только к этому элементу с вашими новыми моделями просмотров приведет к обновлению привязки вашего представления.

Ответ 2

Для проекта, над которым я работаю, я написал простую функцию ko.unapplyBindings, которая принимает jQuery node и удаление boolean. Сначала он отключает все события jQuery, поскольку метод ko.cleanNode не заботится об этом. Я тестировал утечку памяти, и, похоже, он работает нормально.

ko.unapplyBindings = function ($node, remove) {
    // unbind events
    $node.find("*").each(function () {
        $(this).unbind();
    });

    // Remove KO subscriptions and references
    if (remove) {
        ko.removeNode($node[0]);
    } else {
        ko.cleanNode($node[0]);
    }
};

Ответ 3

Вы можете попробовать использовать привязку с привязкой, которую предлагают нокауты: http://knockoutjs.com/documentation/with-binding.html Идея состоит в том, чтобы использовать привязки привязки один раз, и всякий раз, когда ваши данные изменяются, просто обновите свою модель.

Предположим, что у вас есть модель верхнего уровня просмотра storeModel, ваша корзина, представленная cartViewModel, и список предметов в этой корзине - например, cartItemsViewModel.

Вы привязывали бы модель верхнего уровня - storeViewModel к всей странице. Затем вы можете отделить части своей страницы, которые несут ответственность за элементы корзины или корзины.

Предположим, что cartItemsViewModel имеет следующую структуру:

var actualCartItemsModel = { CartItems: [
  { ItemName: "FirstItem", Price: 12 }, 
  { ItemName: "SecondItem", Price: 10 }
] }

CartItemsViewModel может быть пустым в начале.

Шаги будут выглядеть так:

  • Определите привязки в html. Отделите привязку cartItemsViewModel.

      
        <div data-bind="with: cartItemsViewModel">
          <div data-bind="foreach: CartItems">
            <span data-bind="text: ItemName"></span>
            <span data-bind="text: Price"></span>
          </div>
        </div>
      
    
  • Модель магазина поступает с вашего сервера (или создается каким-либо другим способом).

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  • Определите пустые модели в модели верхнего уровня. Затем структура этой модели может быть обновлена ​​с помощью фактические данные.

      
        storeViewModel.cartItemsViewModel = ko.observable();
        storeViewModel.cartViewModel = ko.observable();
     
    
  • Привязать модель представления верхнего уровня.

    ko.applyBindings(storeViewModel);

  • Когда доступен объект cartItemsViewModel, назначьте его ранее определенному заполнителю.

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

Если вы хотите очистить корзину:   storeViewModel.cartItemsViewModel(null);

Нокаут позаботится о html - то есть он появится, когда модель не будет пустой, и содержимое div (тот, у которого есть "со связыванием" ) исчезнет.

Ответ 4

Мне нужно вызвать ko.applyBinding каждый раз при нажатии кнопки поиска, а отфильтрованные данные возвращаются с сервера, и в этом случае следующая работа для меня без использования ko.cleanNode.

Я испытал, если мы заменим foreach на шаблон, тогда он должен отлично работать в случае коллекций /observableArray.

Вы можете найти этот сценарий полезным.

<ul data-bind="template: { name: 'template', foreach: Events }"></ul>

<script id="template" type="text/html">
    <li><span data-bind="text: Name"></span></li>
</script>

Ответ 5

Вместо использования внутренних функций KO и обращения с обработчиком обработчиков событий JQuery, лучше всего использовать привязки with или template. Когда вы это сделаете, ko повторно создает эту часть DOM и поэтому автоматически очищается. Это также рекомендуется, см. Здесь: fooobar.com/questions/55884/....

Ответ 6

Я думаю, что было бы лучше сохранить привязку все время и просто обновить связанные с ней данные. Я столкнулся с этой проблемой и обнаружил, что просто вызов с использованием метода .resetAll() в массиве, в котором я хранил данные, был самым эффективным способом для этого.

В принципе, вы можете начать с некоторого глобального var, который содержит данные, которые будут отображаться через ViewModel:

var myLiveData = ko.observableArray();

Мне потребовалось некоторое время, чтобы понять, что я не мог просто сделать myLiveData нормальный массив - часть ko.oberservableArray была важна.

Затем вы можете пойти и сделать все, что хотите, myLiveData. Например, сделайте вызов $.getJSON:

$.getJSON("http://foo.bar/data.json?callback=?", function(data) {
    myLiveData.removeAll();
    /* parse the JSON data however you want, get it into myLiveData, as below */
    myLiveData.push(data[0].foo);
    myLiveData.push(data[4].bar);
});

Как только вы это сделаете, вы можете продолжить и применять привязки с помощью ViewModel, как обычно:

function MyViewModel() {
    var self = this;
    self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());

Тогда в HTML просто используйте myData, как обычно.

Таким образом, вы можете просто отключить myLiveData от любой функции. Например, если вы хотите обновлять каждые несколько секунд, просто оберните эту строку $.getJSON в функцию и вызовите setInterval на ней. Вам никогда не понадобится удалить привязку, если вы не забыли сохранить строку myLiveData.removeAll();.

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

Ответ 8

Я обнаружил, что если модель представления содержит множество привязок div, лучшим способом очистить ko.applyBindings(new someModelView); является использование: ko.cleanNode($("body")[0]); Это позволяет динамически вызывать новый ko.applyBindings(new someModelView2);, не беспокоясь о предыдущей модели представления все еще привязаны.

Ответ 9

Задумывались ли вы об этом:

try {
    ko.applyBindings(PersonListViewModel);
}
catch (err) {
    console.log(err.message);
}

Я придумал это, потому что в Knockout я нашел этот код

    var alreadyBound = ko.utils.domData.get(node, boundElementDomDataKey);
    if (!sourceBindings) {
        if (alreadyBound) {
            throw Error("You cannot apply bindings multiple times to the same element.");
        }
        ko.utils.domData.set(node, boundElementDomDataKey, true);
    }

Итак, для меня это не проблема, связанная с тем, что ее ошибка не была обнаружена и не обработана...