javascript - ¿Cómo borrar/eliminar enlaces observables en Knockout.js?
(9)
Estoy construyendo funcionalidades en una página web que el usuario puede realizar varias veces. A través de la acción del usuario, se crea un objeto / modelo y se aplica a HTML utilizando ko.applyBindings ().
El HTML enlazado a datos se crea a través de plantillas de jQuery.
Hasta aquí todo bien.
Cuando repito este paso creando un segundo objeto / modelo y llamo a ko.applyBindings () encuentro dos problemas:
- El marcado muestra el objeto / modelo anterior, así como el nuevo objeto / modelo.
- Se produce un error de javascript relacionado con una de las propiedades del objeto / modelo, aunque todavía se representa en el marcado.
Para evitar este problema, después de la primera pasada, invoco .empty () de jQuery para eliminar el HTML con plantilla que contiene todos los atributos de enlace de datos, de modo que ya no está en el DOM. Cuando el usuario inicia el proceso para la segunda pasada, el HTML enlazado a datos se vuelve a agregar al DOM.
Pero como dije, cuando el HTML se vuelve a agregar al DOM y se vuelve a vincular al nuevo objeto / modelo, aún incluye datos del primer objeto / modelo, y aún obtengo el error JS que no ocurre durante el primer pase.
La conclusión parece ser que Knockout se aferra a estas propiedades vinculadas, aunque el marcado se elimine del DOM.
Entonces, lo que estoy buscando es un medio para eliminar estas propiedades vinculadas de Knockout; diciendo noqueado que ya no hay un modelo observable. ¿Hay alguna forma de hacer esto?
EDITAR
El proceso básico es que el usuario carga un archivo; el servidor responde con un objeto JSON, el HTML enlazado a datos se agrega al DOM, luego el modelo de objetos JSON se une a este HTML usando
mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);
Una vez que el usuario ha hecho algunas selecciones en el modelo, el mismo objeto se publica en el servidor, el HTML vinculado a datos se elimina de DOM y luego tengo el siguiente JS
mn.AccountCreationModel = null;
Cuando el usuario desea hacer esto una vez más, todos estos pasos se repiten.
Me temo que el código es demasiado ''complicado'' para hacer una demostración de jsFiddle.
¿Has intentado llamar al método de nodo limpio knockout en tu elemento DOM para eliminar los objetos vinculados a la memoria?
var element = $(''#elementId'')[0];
ko.cleanNode(element);
Luego, aplicar los enlaces knockout nuevamente solo en ese elemento con sus nuevos modelos de vista actualizaría su enlace de vista.
Creo que sería mejor mantener el enlace todo el tiempo y simplemente actualizar los datos asociados. Me encontré con este problema y descubrí que solo llamar usando el método .resetAll()
en la matriz en la que guardaba mis datos era la forma más efectiva de hacerlo.
Básicamente puede comenzar con una var global que contiene datos para ser renderizados a través del ViewModel:
var myLiveData = ko.observableArray();
Me tomó un tiempo darme cuenta de que no podía hacer que myLiveData
una matriz normal, la parte ko.oberservableArray
era importante.
Luego puede seguir adelante y hacer lo que quiera con myLiveData
. Por ejemplo, haga una llamada $.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);
});
Una vez que haya hecho esto, puede continuar y aplicar enlaces usando su ViewModel como de costumbre:
function MyViewModel() {
var self = this;
self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());
Luego, en el HTML, simplemente use myData
como lo haría normalmente.
De esta forma, puedes usar myLiveData desde cualquier función. Por ejemplo, si desea actualizar cada pocos segundos, simplemente $.getJSON
línea $.getJSON
en una función y llame a setInterval
sobre ella. Nunca tendrá que eliminar el enlace siempre que recuerde mantener el myLiveData.removeAll();
en linea.
A menos que sus datos sean realmente enormes, los usuarios ni siquiera podrán notar el tiempo entre el restablecimiento de la matriz y la posterior incorporación de los datos más actuales.
En lugar de utilizar las funciones internas de KO y lidiar con la eliminación del manejador de eventos generales de JQuery, una idea mucho mejor es utilizar with
enlaces de template
. Cuando haces esto, ko vuelve a crear esa parte de DOM y se limpia automáticamente. Esta también es una forma recomendada, consulte aquí: https://.com/a/15069509/207661 .
Has pensado sobre esto:
try {
ko.applyBindings(PersonListViewModel);
}
catch (err) {
console.log(err.message);
}
Se me ocurrió esto porque en Knockout, encontré este código
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);
}
Así que para mí no es realmente un problema que ya esté vinculado, es que el error no fue capturado y tratado ...
He encontrado que si el modelo de vista contiene muchos enlaces div, la mejor manera de borrar los ko.applyBindings(new someModelView);
es usar: ko.cleanNode($("body")[0]);
Esto le permite llamar a una nueva ko.applyBindings(new someModelView2);
dinámicamente sin la preocupación de que el modelo de vista anterior aún esté encuadernado.
Para un proyecto en el que estoy trabajando, escribí una función simple ko.unapplyBindings
que acepta un nodo jQuery y remove boolean. Primero desenlaza todos los eventos jQuery ya que el método ko.cleanNode
no se ocupa de eso. He probado fugas de memoria, y parece funcionar bien.
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]);
}
};
Podría intentar usar el enlace con las ofertas de knockout: http://knockoutjs.com/documentation/with-binding.html La idea es usar enlaces de aplicación una vez, y siempre que sus datos cambien, simplemente actualice su modelo.
Supongamos que tiene un modelo de vista de nivel superior, storeViewModel, su carro representado por cartViewModel, y una lista de artículos en ese carrito, como cartItemsViewModel.
Debería vincular el modelo de nivel superior: storeViewModel a toda la página. Luego, podría separar las partes de su página que son responsables de los artículos de carro o carrito.
Supongamos que cartItemsViewModel tiene la siguiente estructura:
var actualCartItemsModel = { CartItems: [
{ ItemName: "FirstItem", Price: 12 },
{ ItemName: "SecondItem", Price: 10 }
] }
CartItemsViewModel puede estar vacío al principio.
Los pasos se verían así:
Definir enlaces en html. Separar el enlace 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>
El modelo de tienda proviene de su servidor (o se crea de cualquier otra forma).
var storeViewModel = ko.mapping.fromJS(modelFromServer)
Defina modelos vacíos en su modelo de vista de nivel superior. Entonces, una estructura de ese modelo se puede actualizar con datos reales.
storeViewModel.cartItemsViewModel = ko.observable(); storeViewModel.cartViewModel = ko.observable();
Enlace el modelo de vista de nivel superior.
ko.applyBindings(storeViewModel);
Cuando el objeto cartItemsViewModel está disponible, asígnelo al marcador de posición previamente definido.
storeViewModel.cartItemsViewModel(actualCartItemsModel);
Si desea borrar los elementos del carrito: storeViewModel.cartItemsViewModel(null);
Knockout se ocupará de html, es decir, aparecerá cuando el modelo no esté vacío y el contenido de div (el que tiene "con enlace") desaparecerá.
Recientemente tuve un problema de pérdida de memoria y ko.cleanNode(element);
no lo haría por mí - ko.removeNode(element);
hizo. Javascript + Knockout.js memory leak - ¿Cómo asegurarse de que el objeto está siendo destruido?
Tengo que llamar a ko.applyBinding cada vez que haga clic en el botón de búsqueda, y los datos filtrados vuelvan del servidor, y en este caso seguir trabajando para mí sin usar ko.cleanNode.
Experimenté, si reemplazamos foreach con template, entonces debería funcionar bien en caso de collections / observableArray.
Puede encontrar este escenario útil.
<ul data-bind="template: { name: ''template'', foreach: Events }"></ul>
<script id="template" type="text/html">
<li><span data-bind="text: Name"></span></li>
</script>