Single Page Applications – Parte 5 – Comunicación entre ViewModels

En este post veremos como comunicar las 2 vistas que hemos creado anteriormente(Seleccionar Producto e Ingresar Pedido), de tal manera que cuando seleccionemos un producto en la primera pantalla, desaparezca esta pantalla y se muestre la pantalla de ingreso de pedido con los datos del producto seleccionado.

Para lograr que nuestros 2 viewmodels se comuniquen tenemos las siguientes opciones: crear un objeto padre que se encargue de coordinar todas los view models; utilizar las urls, hasbangs (#!) y routing; o utilizar el patrón publish/suscribe entre las vistas.

En esta oportunidad utilizaremos un objeto padre para coordinar todos los viewmodels del proceso de delivery, pero en los siguientes posts veremos las otras alternativas.

Implementamos este objeto dentro del archivo /Scripts/App/ViewModels/deliveryviewmodel.js, este objeto tendrá una propiedad por cada uno de los viewmodels que esté coordinando.

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

    self.chooseProduct = ko.observable();
    self.placeOrder = ko.observable();
};

Asimismo, este objeto tendrá métodos que serán responsables de mostrar cada una de las vistas del proceso de delivery.

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

    self.chooseProduct = ko.observable();
    self.placeOrder = ko.observable();

    self.showChooseProduct =
function
() {
       
//TODO: Mostrar la primera vista y ocultar las demás
    };

    self.showPlaceOrder =
function
(productId) {
       
//TODO: Mostrar la segunda vista y ocultar las demás
    };

    self.showConfirmation =
function
() {
       
//TODO: Mostrar la tercera vista y ocultar las demás
    };

    self.showChooseProduct();

};

Los viewmodels ChooseProductViewModel y PlaceOrderViewModel dependerán este nuevo objeto padre y le delegarán la responsabilidad de mostrar la vista adecuada.

var ChooseProductViewModel = function (parent) {
   
var self = this
;
    self.products = ko.observableArray();

    self.goToPlaceOrder =
function
(product) {
        parent.showPlaceOrder(product.id);
    };

    self.init =
function
() {
        ProductsDataSource.getAll(
function (data) {
            self.products(data);
        });
    };

    self.init();
};
var PlaceOrderViewModel = function (productId, parent) {
   
var self = this
;

    self.order = ko.observable();

    self.postOrder =
function
() {
        OrdersDataSource.create(self.order,
function
() {
            parent.showConfirmation();
        });
    };

    self.init =
function
() {
        ProductsDataSource.get(productId,
function
(product) {
            self.order(
new Order(product));
        });
    };

    self.init();
};

El objeto DeliveryViewModel, cada vez que los objetos hijos le delegen mostrar una nueva vista, instanciará el viewmodel adecuado para esta vista.

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

    self.chooseProduct = ko.observable();
    self.placeOrder = ko.observable();

    self.showChooseProduct =
function
() {
        self.chooseProduct(
new
ChooseProductViewModel(self));
        self.placeOrder(
null
);
    };

    self.showPlaceOrder =
function
(productId) {
        self.chooseProduct(
null
);
        self.placeOrder(
new
PlaceOrderViewModel(productId, self));
    };

    self.showConfirmation =
function
() {
       
//TODO: Mostrar la Tercera vista y ocultar las demás
    };

    self.showChooseProduct();
};

Podemos observar que para ocultar una vista simplemente asignamos null a la propiedad, esto se debe a que Knockoutjs automáticamente eliminará toda la sección HTML si es que el valor del binding es null.

Necesitamos enlazar toda la aplicación al nuevo objeto DeliveryViewModel, para esto modificamos el archivo /Scripts/application.js donde se inicializan los viewmodels.

function initializeApplication() {
    initializeViewModels();
}

function
initializeViewModels() {
    ko.applyBindings(
new DeliveryViewModel());
}

Enlazamos cada vista de manera individual a una propiedad del DeliveryViewModel, para esto modificamos el archivo /Views/Home/Index.cshtml.

<div data-bind="with: chooseProduct">
    @Html.Partial("_ChooseProduct")
</div>

<div data-bind="with: placeOrder">
    @Html.Partial("_PlaceOrder")
</div>

<script type="text/javascript">
    $(function
() {
        initializeApplication();
    });

</script
>

Por último agregamos las referencias a todos los nuevos scripts dentro del archivo /Views/Shared/Layout.cshtml.

<script src="~/Scripts/Lib/jquery-1.6.2.min.js" type="text/javascript"></script>
<
script src="~/Scripts/Lib/knockout-2.1.0.js" type="text/javascript"></script
>
<
script src="~/Scripts/App/Data/productsdatasource.js" type="text/javascript"></script
>
<
script src="~/Scripts/App/Data/ordersdatasource.js" type="text/javascript"></script
>
<
script src="~/Scripts/App/Models/order.js" type="text/javascript"></script
>
<
script src="~/Scripts/App/ViewModels/deliveryviewmodel.js" type="text/javascript"></script
>
<
script src="~/Scripts/App/ViewModels/chooseproductviewmodel.js" type="text/javascript"></script
>
<
script src="~/Scripts/App/ViewModels/placeorderviewmodel.js" type="text/javascript"></script
>
<
script src="~/Scripts/application.js" type="text/javascript"></script>

Si ejecutamos la aplicación podremos ver el listado de productos.

ChooseProd_Small

Si seleccionamos alguno de los productos aparecerá la pantalla para completar los datos de la orden.

placeorder

En el siguiente post veremos la tercera pantalla del proceso de delivery.

Saludos
Angel Núñez Salazar