2012-06-16 19 views
40

¿Cómo es posible hacer un trabajo de enlace de datos knockout en elementos generados dinámicamente? Por ejemplo, inserto un menú simple de selección html dentro de un div y quiero completar las opciones usando el enlace de opciones inactivas. Así es como se ve mi código:knockout data-bind en elementos generados dinámicamente

$('#menu').html('<select name="list" data-bind="options: listItems"></select>'); 

pero este método no funciona. ¿Algunas ideas?

+0

¿Está agregando esto después de haber hecho sus ko.applyBindings (yourVMHere); – PlTaylor

+0

Elimine la idea del enlace KO (automático) en este elemento DOM agregado dinámicamente y maneje esto manualmente. – Vaibhav

Respuesta

30

Si agrega este elemento sobre la marcha después de haber atado su viewmodel, no estará en el modelo de vista y no se actualizará. Puedes hacer una de las dos cosas.

  1. Añada el elemento a la DOM y volver a enlazar llamando ko.applyBindings(); nuevo
  2. o añadir la lista para el DOM desde el principio y dejar la colección de opciones en su modelo de vista vacía. Knockout no lo renderizará hasta que agregue elementos a opciones sobre la marcha más tarde.
+11

Si vuelvo a llamar a applyBindings, arroja un error: Error: no puede aplicar enlaces múltiples veces al mismo elemento. – Chris

+2

Debe ser una nueva característica de los marcos más recientes. La segunda opción sigue siendo viable, y honestamente, la mejor opción para empezar. – PlTaylor

+1

Sí, pensé que era una mala práctica aplicar enlaces más de una vez en un elemento. Como se activará 2 veces, creo que es por eso que agregaron algunas advertencias en KO 3 – Chris

3

EDIT: Parece que esto no funciona desde la versión 2.3 IIRC como se ha señalado por LosManos

Se puede agregar otro observable a su modelo de vista utilizando myViewModel [newObservable] = ko.observable (' ')

Después de eso, vuelva a llamar a ko.applyBindings.

Aquí hay una página simple donde agrego párrafos dinámicamente y el nuevo modelo de vista y las fijaciones funcionan perfectamente.

// myViewModel starts only with one observable 
 
    \t var myViewModel = { 
 
    \t  paragraph0: ko.observable('First') 
 
    \t }; 
 
    
 
    \t var count = 0; 
 
    
 
    \t $(document).ready(function() { 
 
    \t \t ko.applyBindings(myViewModel); 
 
    
 
    \t \t $('#add').click(function() { 
 
    \t \t \t // Add a new paragraph and make the binding 
 
    \t \t \t addParagraph(); 
 
    \t \t \t // Re-apply! 
 
    \t \t \t ko.applyBindings(myViewModel); \t \t \t 
 
    \t \t \t return false; \t 
 
    \t \t }); 
 
    \t }); 
 
    
 
    \t function addParagraph() { 
 
    \t \t count++; 
 
    \t \t var newObservableName = 'paragraph' + count; 
 
    \t  $('<p data-bind="text: ' + newObservableName + '"></p>').appendTo('#placeholder'); 
 
    \t \t 
 
    \t  // Here is where the magic happens 
 
    \t \t myViewModel[newObservableName] = ko.observable(''); 
 
    \t \t myViewModel[newObservableName](Math.random()); 
 
    
 
    \t \t // You can also test it in the console typing 
 
    \t \t // myViewModel.paragraphXXX('a random text') 
 
    \t }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script> 
 

 
<div id="placeholder"> 
 
    <p data-bind="text: paragraph0"></p> 
 
</div> 
 
    
 
<a id="add" href="#">Add paragraph</a>

+7

Llamar applyBindings más de una vez no es una buena idea. – gliljas

+0

@ gunteman ... ¿por qué no? – lamarant

+3

Parece que arroja el mismo error de "no se pueden aplicar enlaces dos veces". ¿Es posible decirle a ko.applyBindings() un elemento muy específico que desea agregar a los enlaces? – Chris

0

Basado en this existing answer, he achived algo similar a sus intenciones iniciales:

function extendBinding(ko, container, viewModel) { 
    ko.applyBindings(viewModel, container.children()[container.children().length - 1]); 
} 

function yourBindingFunction() { 
    var container = $("#menu"); 
    var inner = $("<select name='list' data-bind='options: listItems'></select>"); 
    container.empty().append(inner); 


    extendBinding(ko, container, { 
     listItems: ["item1", "item2", "item3"] 
    }); 
} 

Aquí es una JSFiddle para jugar.

Tenga en cuenta, una vez que el nuevo elemento es parte de la dom, no puede volver a vincularlo con una llamada a ko.applyBindings - es por eso que uso container.empty(). Si necesita conservar el nuevo elemento y hacer que cambie a medida que cambia el modelo de vista, pase un parámetro observable al viewModel del método extendBinding.

10

reescribe el código de enlace html o crea uno nuevo. Debido html la sujeción evita "inyectada fijaciones" en el HTML dinámico:

ko.bindingHandlers['html'] = { 
 
    //'init': function() { 
 
    // return { 'controlsDescendantBindings': true }; // this line prevents parse "injected binding" 
 
    //}, 
 
    'update': function (element, valueAccessor) { 
 
    // setHtml will unwrap the value if needed 
 
    ko.utils.setHtml(element, valueAccessor()); 
 
    } 
 
};

+3

Después de ver todas las respuestas y comentarios sobre este hilo, IMO esta es en realidad la mejor solución a la pregunta "vincular datos nocaut en elementos generados dinámicamente". ¡Buena solución! – MattSizzle

1

Es una vieja pregunta, pero aquí está mi esperemos hasta a la fecha de respuesta (nocaut 3.3.0):

Cuando utilice plantillas knockout o componentes personalizados para agregar elementos a las colecciones observables preincorporadas, el knockout enlazará todo automáticamente. Su ejemplo parece una colección observable de elementos de menú que haría el trabajo de la caja.

10

Knockout 3.3

ko.bindingHandlers.htmlWithBinding = { 
      'init': function() { 
      return { 'controlsDescendantBindings': true }; 
      }, 
      'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) { 
       element.innerHTML = valueAccessor(); 
       ko.applyBindingsToDescendants(bindingContext, element); 
      } 
    }; 

Por encima de fragmento de código le permite inyectar elementos HTML de forma dinámica con la propiedad "htmlWithBinding". Los elementos secundarios que se agregan también se evalúan ... es decir, sus atributos de enlace de datos.

3

Para v3.4.0 Utilice el enlace de abajo a medida:

ko.bindingHandlers['dynamicHtml'] = { 
    'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) { 
     // setHtml will unwrap the value if needed 
     ko.utils.setHtml(element, valueAccessor()); 
     ko.applyBindingsToDescendants(bindingContext, element); 
    } 
}; 
Cuestiones relacionadas