2012-04-12 17 views
46

Estoy trabajando con knockout.js para crear listas dinámicas y estoy tratando de averiguar cómo puedo obtener el objeto DOM asociado con un objeto en mi matriz observable. Específicamente, quiero obtener el jQuery para una fila.Knockout.js obtener el objeto dom asociado con los datos

Ejemplo:

<ul data-bind="foreach: Item"> 
    <li data-bind="events: {click: getDomObject}, text: 'text: ' + text"> 
    </li> 
</ul> 

en la función getDomObject, me gustaría ser capaz de conseguir el objeto específico <li></li> DOM para que pueda hacer alguna manipulación jQuery con él.

He pensado en agregar un miembro id al Item ViewModel y luego agregar el id como html id de la línea de pedido y luego seleccionar en función de eso, pero creo que debería haber una manera más fácil.

¿Cuál es la forma correcta de referenciar el HTML dinámico generado por knockout.js?

Respuesta

64

Los manejadores de eventos como el clon pasan dos argumentos. Es a) el elemento al que pertenece este evento, como la entrada de una matriz observable que estás renderizando con el enlace foreach ("Artículo" en tu caso). Yb) un objeto de evento, que le proporciona más información sobre el evento real. Este objeto contiene el elemento DOM que consiguió hace clic ("objetivo" clave):

getDomObject = function(item, event) { 
    var $this = $(event.target); 
    // ... 
} 

Sólo una nota: No mezcle nocaut y manipulaciones de DOM nativos jQuery - si se puede conseguir el mismo resultado con fijaciones knockout inteligentes , Recomendaría ir con eso.

Y aquí es una demostración sencilla: http://jsfiddle.net/KLK9Z/213/

+0

¡Muchas gracias! Esto funciona genial! Gracias por la información sobre la manipulación de jQuery. Estoy usando el autocompletado en filas específicas, así que no creo que pueda hacerlo a través de ko. La gran solución –

+0

usa los enlaces personalizados para el padawan de autocompletar. : Una cosa que aprendí después de 3 meses devolviendo con ko: usar enlaces personalizados con jquery/jquery ui. –

+0

He trabajado con knockout por edades y no estaba al tanto del parámetro del evento - ¡genial! gracias –

20

El $ (event.target) solución es buena si está relacionado a un evento que ya está ocurriendo en el que el elemento DOM del elemento está en blanco. Pero a veces no tiene el elemento de destino porque no hay ningún evento (por ejemplo, desea desplazar una lista a un elemento que el usuario no indicó).

En tal caso, se puede dar del elemento elemento DOM id del atributo un valor único que contiene el ID de artículo:

<li data-bind="attr: {id: 'item_' + id}"> 

y luego getDomObject() se parece a:

getDomObject = function(item) { return $("#item_" + item.id); } 
+0

en mi humilde opinión, esta respuesta es mucho más completa por exactamente la razón yuvalr80 declaró. –

+0

Esta respuesta fue la que necesitaba, pero la respuesta aceptada es mejor para la pregunta original. Gracias por publicar esta respuesta también, exactamente, lo que necesitaba. No estoy loco por cruzar las líneas de MVVM, pero solo necesitaba una manera eficiente de desplazar un elemento para verlo, y no tengo ganas de agregar un observable extra a cada objeto en mi lista solo por esto. – eselk

+1

Funciona, pero pasa el ID y luego obtiene el DOM por ese ID. Rompe algunas reglas. Cualquier solución sin ID sería muy apreciada. –

7

Para añadir aún una tercera opción, también para casos en los que no tiene un evento para trabajar (si tiene un evento, la respuesta aceptada es la mejor/optimizada).

crear una vinculación tales como la costumbre:

ko.bindingHandlers.scrollTo = { 
    update: function(element, valueAccessor) { 
     var value = ko.utils.unwrapObservable(valueAccessor()); 
     if (value) { 
      var scrollParent = $(element).closest("div"); 
      var newTop = $(element).position().top + scrollParent.scrollTop(); 
      scrollParent.scrollTop(newTop); 
     } 
    } 
}; 

uso es el siguiente:

<li data-bind="scrollTo: $parent.scrollTo() && $parent.scrollTo().id == id"> 

En el caso anterior, los padres $ es mi vista del modelo. Tengo un objeto observable que contiene una identificación única. Cada vez que configuro ese objeto scrollTo(), la lista se desplaza a ese elemento.

Tenga en cuenta que mi código asume que el DIV padre de LI tiene la barra de desplazamiento (overflow: auto/scroll). Puede ajustar para sus necesidades, posiblemente use una clase en el padre y la use para su selector jQuery, o para hacer que sea muy flexible, podría pasar el selector a través de sus opciones de enlace de datos ...para mí, esto fue suficiente, ya que siempre uso divs para mis secciones desplazables.

+0

¡He hecho algo similar y funciona bastante bien! Gracias por la publicacion –

1

Tuve un problema similar. Se me ocurre una solución similar al uso de Backbone.js de las referencias el y $ el.

en su modelo de vista:

var myViewModel = function(){ 
    var self = this; 

    //html element 
    self.el = ko.observable(); 

    //jquery wrapped version 
    self.$el = ko.observable(); 
} 

en html (por ejemplo, lista de elementos):

<!-- left side is the name of the handler, right side is name of the observable --> 
<li class="myclass" data-bind="el: el, $el: $el"></li> 

en bindingHandlers (mostrando todos los argumentos posibles para init):

ko.bindingHandlers.el = { 
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
    var value = valueAccessor(); 
    //assign value to observable (we specified in html) 
    value(element); 
    } 
}; 

ko.bindingHandlers.$el = { 
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
    var value = valueAccessor(); 
    //here we first create a jQuery object by using $(myelem) 
    //before updating observable value 
    value($(element).first()); 
    } 
}; 

Por ejemplo, entonces puedes usar $ el como:

var myViewModel = function(){ 
    var self = this; 

    //plain DOM element reference 
    self.el = ko.observable(); 

    //jquery object reference 
    self.$el = ko.observable(); 

    self.myFunction = function() { 
    console.log(self.$el().html()); 
    self.$el().addClass("myCssClass"); 
    } 
} 

Hope this helps!

1

Mi solución (válido para "valor" de unión)

ko.bindingHandlers.value.preprocess = function(val, name, cb) { 
    /* every time I set a data-bind="value: xxxx" with an 
    * observable xxxx add also a data-bind="domElement: xxxx" */ 
    cb('domElement', val); 
    return val; 
} 

ko.bindingHandlers.domElement = { 
    /* For each data-bind="domElement: xxxx" add an extension "element" */ 
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
     valueAccessor().extend({element: element }); 
    } 
    }; 

ko.extenders.element = function (target, element) { 
    /* element extension add el and $el to observable xxxx */ 
    target.el = element; 
    target.$el = $(element); 
} 

Ahora usted tiene yourobservable. $ Y el yourobservable.el que se unen a jQuery y el elemento DOM.

Cuestiones relacionadas