Как очистить/удалить наблюдаемые привязки в 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();
.
Если ваши данные действительно огромны, пользователь даже не сможет заметить время между сбросом массива и последующим добавлением самых текущих данных.
Ответ 7
В последнее время у меня была проблема с утечкой памяти, а ko.cleanNode(element);
не сделала этого для меня - ko.removeNode(element);
. Утечка памяти Javascript + Knockout.js - Как убедиться, что объект уничтожается?
Ответ 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);
}
Итак, для меня это не проблема, связанная с тем, что ее ошибка не была обнаружена и не обработана...