KnockOutJS - Múltiples modelos de vista en una sola vista

201

Estoy pensando que mi aplicación se está volviendo bastante grande ahora, demasiado grande para manejar cada vista con un único modelo de vista.

Así que me pregunto qué difícil sería crear múltiples ViewModels y cargarlos todos en una sola Vista. Con una nota de que también necesito poder pasar los datos de X ViewModel a los datos de Y ViewModel , por lo que los ViewModels individuales deben poder comunicarse entre sí o al menos ser conscientes de ellos.

Por ejemplo, tengo un <select>menú desplegable, ese menú desplegable de selección tiene un estado seleccionado que me permite pasar la ID del elemento seleccionado en <select>otra llamada Ajax en un ViewModel separado ...

Cualquier punto sobre el trato con numerosos ViewModels en una sola Vista apreciado :)

CLiown
fuente
12
Para aquellos que lleguen a esta pregunta, pase la respuesta aceptada. Knockout ahora admite múltiples contextos vinculantes . No hay necesidad de un gigante masterVM.
Carrie Kendall

Respuestas:

150

Si todos necesitan estar en la misma página, una forma fácil de hacerlo es tener un modelo de vista maestra que contenga una matriz (o lista de propiedades) de los otros modelos de vista.

masterVM = {
    vmA : new VmA(),
    vmB : new VmB(),
    vmC : new VmC(),
}

Entonces masterVMpuede tener otras propiedades si es necesario, para la página en sí. La comunicación entre los modelos de vista no sería difícil en esta situación, ya que podría transmitir a través de masterVM, o podría usar los enlaces $parent/ $rootin, o algunas otras opciones personalizadas.

John papa
fuente
2
Entonces, ¿podría hacer algo como: data-bind = "text: masterVM.vmA", supongo que aún podría usar ko.applyBindings con el elemento DOM adjunto. Suponga que eso también significaría que podría hacer: data-bind = "$ parent.masterVm"?
CLiown
12
@CLiown Puede usar el with:enlace, por lo que no se repetirá
AlfeG
44
@CLiown Sí, puede hacerlo si está vinculado al masterVM. También puede usar el enlace "con" para ayudar a evitar la sintaxis de puntos cuando se sumerge en los modelos de vista secundaria.
John Papa
1
Creo que este enfoque es muy restrictivo ... Ahora, en mi caso, estoy usando ASP.Net MVC4, esto no ayuda, ya que habrá vistas parciales que tengan sus propios ViewModels, y las secciones parcial / Contenido, no deberían interferir entre sí , y debido a la representación condicional Será muy difícil utilizar este enfoque.
bhuvin
1
@bhuvin usando <! - ko stopBinding: true -> lo ayudará con esos modelos de vista múltiple y secciones de vistas parciales. Consulte knockmeout.net/2012/05/quick-tip-skip-binding.html para obtener más información.
Micaël Félix
285

Knockout ahora admite el enlace de varios modelos. El ko.applyBindings()método toma un parámetro opcional: el elemento y sus descendientes a los que se activará el enlace.

Por ejemplo:

ko.applyBindings(myViewModel, document.getElementById('someElementId'))

Esto restringe la activación al elemento con ID someElementIdy sus descendientes.

Ver documentación para más detalles.

sanatgersappa
fuente
72
Si desea utilizar un selector jQuery, deberá agregarlo [0]para especificar un elemento DOM real (en lugar del objeto jQuery) así:ko.applyBindings(myViewModel, $('#someElementId')[0])
MrBoJangles
3
Esta debería ser la respuesta aceptada. Todavía podría usar un objeto maestro como lo tiene la respuesta actualmente aceptada, y luego vincular los modelos de vista individuales a sus elementos apropiados en la página. Esto ahorrará en rendimiento y limitará el alcance necesario para la vinculación de datos.
Kevin Heidt
¿Es posible comunicar viewModels entre sí con este enfoque? es decir, tengo TaskVM y NoteVM. La tarea puede tener notas. Por lo tanto, mi TaskVM debe tener una matriz observable, es decir, notas cuyo tipo es TaskVM. ¿Puedes compartir un ejemplo para un caso como ese?
ahmet
Probablemente sea mejor preguntar sobre la comunicación entre máquinas virtuales en una nueva pregunta.
Richard Nalezynski
21

Esta es mi respuesta después de completar un proyecto muy grande con muchos ViewModels en una sola vista.

Vista HTML

    <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <div id="container1">
        <ul>
            <li >Container1 item</li>
            <!-- ko foreach: myItems -->
            <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <div id="container2">
        <ul>
            <li >Container2 item</li>
            <!-- ko foreach: myItems -->
                <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <script src="js/jquery-1.11.1.js"></script>
    <script src="js/knockout-3.0.0.js"></script>
    <script src="js/DataFunction.js"></script>
    <script src="js/Container1ViewModel.js"></script>
    <script src="js/Container2ViewModel.js"></script>

</body>
</html>

Para esta vista, estoy creando 2 modelos de vista para id = container1 e id = container2 en dos archivos javascript separados.

Container1ViewModel.js

function Container1ViewModel()
{
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("ABC");
    self.myItems.push("CDE");

} 

Container2ViewModel.js

function Container2ViewModel() {
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("XYZ");
    self.myItems.push("PQR");

}

Luego, después de que estos 2 modelos de vista se registren como modelos de vista separados en DataFunction.js

var container1VM;
var container2VM;

$(document).ready(function() {

    if ($.isEmptyObject(container1VM)) {
        container1VM = new Container1ViewModel();
        ko.applyBindings(container1VM, document.getElementById("container1"));
    }

    if ($.isEmptyObject(container2VM)) {
        container2VM = new Container2ViewModel();
        ko.applyBindings(container2VM, document.getElementById("container2"));
    }
});

De esta manera, puede agregar cualquier número de modelos de vista para divs separados. Pero asegúrese de no crear un modelo de vista separado para un div dentro del div registrado.

Janith Widarshana
fuente
¿Es posible hacerlo como un modelo de vista dentro de otro en lugar de ser elementos separados del DOM?
UserEsp
4

Verifique el complemento MultiModels para Knockout JS - https://github.com/sergun/Knockout-MultiModels

Sergey Zwezdin
fuente
66
¿Qué ventaja tiene esto sobre solo ko.applyBindings (viewModel, document.getElementById ("divName"))? ¿No es solo azúcar sintáctico?
Paolo del Mundo
1
@Paolo del Mundo También agrega una dependencia en el complemento LiveQuery.
Lars Gyrup Brink Nielsen
@PaolodelMundo el propósito del complemento es poder usar un conjunto de modelos de vista de manera descalificativa
Sergey Zwezdin