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

111

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

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

Все идет нормально

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

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

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

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

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

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

РЕДАКТИРОВАТ

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

<code>mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);
</code>

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

<code>mn.AccountCreationModel = null;
</code>

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

Боюсь, код слишком «запутан» для демонстрации jsFiddle.

Ваш Ответ

9   ответов
6

тчиком событий JQuery гораздо лучше использоватьwith илиtemplate привязки. Когда вы делаете это, Ко воссоздает эту часть DOM, и она автоматически очищается. Это также рекомендуемый способ, смотрите здесь:https: //stackoverflow.com/a/15069509/20766.

165

чтобы избавиться от объектов в памяти?

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

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

Это работает - спасибо. Однако я не могу найти никакой документации по этому методу. awj
Я также искал документацию по этой функции удаления нокаута. Насколько я могу судить по исходному коду, он вызываетdelete на определенных ключах на самих элементах dom, где, по-видимому, хранится вся магия нокаута. Если у кого-то есть источник документации, я был бы очень признателен. Patrick M
Ты не найдешь это. Я искал документы по функциям ко, но не существует. Это самое близкое сообщение, которое вы найдете в этом блоге, но оно касается только членов ko.utils: Knockmeout.net / 2011/04 / подсобные-функции-в-knockoutjs.html Nick Daniels
Вы также захотите удалить события вручную, как показано в моем ответе ниже. Michael Berkompas
@ KodeKreachor Я уже разместил рабочий пример ниже. Моя точка зрения заключалась в том, что, возможно, было бы лучше сохранить привязку без изменений, одновременно выпуская данные в ViewModel. Таким образом, вам никогда не придется иметь дело с отменой / повторным связыванием. Это кажется чище, чем использование недокументированных методов для ручного открепления напрямую из DOM. Помимо этого, CleanNode проблематичен, потому что он не освобождает ни один из обработчиков событий (см. Ответ здесь для более подробной информации: / Stackoverflow.com вопросы / 15063794 / ...) Zac
30

над которым я работаю, я написал простойko.unapplyBindingsункция @, которая принимает узел jQuery и логическое удаление. Сначала он связывает все события jQuery какko.cleanNode method не заботится об этом. Я проверил на утечки памяти, и это, кажется, работает просто отлично.

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]);
    }
};
Только одно предостережение, я не проверял повторное связывание с чем-то, что только что былоko.cleanNode() звонил и не весь html заменил. Michael Berkompas
Разве ваше решение также не отменяет привязки других событий? есть ли возможность удалить только обработчики событий ко? lordvlad
без изменения ко-ядра, которое является lordvlad
Правда, насколько это возможно, насколько я могу судить, без изменения ядра. Смотрите эту проблему, которую я поднял здесь: Github.com / SteveSanderson / Нокаут / вопросы / 724 Michael Berkompas
Разве не мысль о нокауте, что ты сам очень редко должен касаться дома? Этот ответ проходит через дом, и в моем случае использования он, безусловно, будет далек от использования. Blowsie
12

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

Допустим, у вас есть модель представления верхнего уровня storeViewModel, ваша корзина представлена 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);

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

9

когда нажимается кнопка поиска, и отфильтрованные данные возвращаются с сервера, и в этом случае я работаю без использования 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>
Я потратил не менее 4 часов, пытаясь решить подобную проблему, и у меня работает только решение Аамира. Antonin Jelinek
@ AntoninJelinek - еще одна вещь, с которой я столкнулся в моем сценарии: полностью удалить html и динамически добавить его снова, чтобы полностью удалить все. например, у меня есть контейнер с кодом Knockout div <div id = "knockoutContainerDiv"> </ div> на $ .Ajax. Успешный результат, который я получаю, следуя каждый раз, когда серверный метод вызывает $ ("# knockoutContainerDiv"). children.remove (); / / удалить его содержимое, вызвать метод для добавления динамического html с кодом выбивания $ ("# knockoutContainerDiv"). append ("childelements с кодом привязки нокаута") и повторным вызовом applyBinding aamir sajjad
Эй, амир, у меня был такой же сценарий, как и у тебя. Все, что вы упомянули, прекрасно работало, за исключением того, что мне пришлось использовать ko.cleanNode (element); до каждого повторного связывания. Radoslav Minchev
@ RadoslavMinchev Как вы думаете, я могу помочь вам дальше, если да, то как, я буду рад поделиться своими мыслями / опытом по конкретному вопросу. aamir sajjad
Спасибо. @aamirsajjad, просто хотел упомянуть, что для меня работало, чтобы вызвать функцию cleanNode (), чтобы она работала. Radoslav Minchev
4

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

В основном вы можете начать с некоторой глобальной переменной, содержащей данные, которые будут отображаться через 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(); строка в.

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

Со времени публикации вопроса я сейчас так и делаю. Просто некоторые из этих методов Knockout либо недокументированы, либо вам действительно нужно осмотреться (уже зная имя функции), чтобы найти, что она делает. awj
Мне тоже пришлось очень трудно это найти (когда количество часов, копаясь в документах, превышает результирующее количество строк кода ... вау). Рад, что у вас все получилось. Zac
2

ko.cleanNode(element); не сделал бы это для меня -ko.removeNode(element); сделал.течка памяти @Javascript + Knockout.js - Как убедиться, что объект уничтожается?

В нокауте 3.1 ko.removeNode фактически вызывает ko.cleanNode. Я не знаю, что было в случае с более ранними версиями. kiprainey
1

Думали ли вы об этом:

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

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

    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);
    }

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

0

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

Есть пара моментов, которые я хотел бы добавить: (1) это удалит ВСЕ привязки с вашей веб-страницы, которые могут подойти вашему приложению, но я думаю, что есть много приложений, в которых привязки были добавлены к нескольким частям страницы отдельные причины. Многим пользователям может быть не полезно очищать ВСЕ привязки в одной, широкой команде. (2) более быстрый, более эффективный, родной JavaScript-метод получения$("body")[0] являетсяdocument.body. awj

Похожие вопросы