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


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

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

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

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

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

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

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

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

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

EDIT

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

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

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

mn.AccountCreationModel = null;

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

Я боюсь, что код слишком "вовлечен", чтобы сделать демо jsFiddle.

9 110

9 ответов:

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

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

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

для проекта, над которым я работаю, я написал простой ko.unapplyBindings функция, которая принимает узел jQuery и логическое значение remove. Сначала он разблокирует все события 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]);
    }
};

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

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

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

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

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

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

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

  1. определение Привязок в 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>
      
    
  2. модель магазина приходит от ваш сервер (или создается любым другим способом).

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. определите пустые модели на модели вида верхнего уровня. Тогда структура этой модели может быть обновлена с фактические данные.

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

    ko.applyBindings(storeViewModel);

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

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

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

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

Я должен позвонить ко.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>

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

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

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

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

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

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

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

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