Two-way data binding con WinJS

- 5 minute read

El enlace de datos bidireccional o Two-way Data Binding es una de las características más demandadas en WinJS, sobre todo porque es una característica que tenemos de serie en otros frameworks JavaScript como es el caso de KnockouJS. Descrito en pocas palabras, este modo de enlace nos permite que los cambios realizados en un control HTML se actualicen automáticamente en el modelo de datos origen, evitando de esta forma que tengamos que acceder directamente al elemento del DOM para poder obtener el valor actualizado.

A pesar de que por defecto el enlace de datos en WinJS es unidireccional, podemos extender el comportamiento del inicializador del enlace a datos mediante la función WinJS.Binding.initializer. En esta entrada vamos a ver el ejemplo de implementación más sencillo, que es el que Josh Williams explicó durante su sesión de la BUILD 2012. Pero antes de entrar en materia, repasemos brevemente cómo funciona el enlace de datos en WinJS.

Enlazando datos mediante “Observables”

Por un lado tenemos el modelo de datos en JavaScript. En nuestro caso vamos a utilizar el modelo más simple posible, que es en un objeto con una propiedad name.

var data = { name: IdleBit };

Para poder enlazar este objeto con un elemento HTML tenemos que hacerlo “observable”, es decir que notifique cuando el valor de la propiedad cambie. Esto lo conseguimos mediante la función WinJS.Binding.as que devuelve un objeto observable a partir del objeto JS. Este nuevo objeto tiene la misma propiedad name, pero ahora dispará una notificación cuando se modifique su valor.

var data = WinJS.Binding.as({ name: "IdleBit" });

En el código HTML, tenemos que utilizar el atributo data-win-bin en los elementos que queremos enlazar con el objeto observable que acabamos de crear, el modelo JavaScript. Por ejemplo, en el siguiente ejemplo definimos un elemento <div> y un TextBox enlazados a la propiedad name.

<div data-win-bind="textContent: name"></div>
<input data-win-bind="value: name" type="text" />

En el caso del <div> estamos enlazando el valor de la propiedad name con la propiedad textContent del control y en el caso del input con la propiedad value.

Para que todo esto funcione, tenemos que llamar a la función WinJS.Binding.processAll. Esta función se encarga de parsear todos los elementos del DOM que tienen el atributo data-win-bind y enlazarlos con el objeto JavaScript. El primer parámetro que pasamos a la función es el elemento raíz por el cual comenzará a buscar. En nuestro caso le pasamos el todo el cuerpo del documento, y el segundo parámetro es el contexto de datos, nuestro modelo JS.

WinJS.Binding.processAll(document.body, data);

Creando el modo Two-way

Hasta aquí nada nuevo, hemos descrito el comportamiento normal del enlace a datos con WinJS. Si modificamos desde JS el contenido de la propiedad *name, *el nuevo valor se verá actualizado en todos los controles en los que se esté utilizando.

Llegamos ahora al motivo principal que quería tratar en esta entrada. Si modificamos el valor en el Textbox, comprobaremos que el valor no se actualiza en el modelo, ya que el enlace se está realizando en un solo sentido. Aquí es donde entra en juego la función WinJS.Binding.initializer, con la que podemos extender la forma en que se inicializa el enlace a datos entre el objeto JS y el elemento HTML. 

var twoWay = WinJS.Binding.initializer(function (source, sourceProps, dest, destProps) {
  WinJS.Binding.defaultBind(source, sourceProps, dest, destProps);
  
  dest.onchange = function () {
    var d = dest[destProps[0]];
    var s = source[sourceProps[0]];
    if (s!==d) {
      source[sourceProps[0]] = d;
  }
});

Al utilizar el inicializador en declaración del binding se pasan los objetos y propiedades origen y destino que intervienen en el enlace de datos. La propiedad source es el modelo JavaScript al que se está enlazando y en el valor dest tenemos el elemento HTML o el control WinJS destino. El parámetro destProps es un array que contiene las propiedades del elemento modificado. Lo primero que tenemos que hacer es llamar a la función defaultBind para establecer el enlace a datos por defecto (unidireccional), pero el cambio que vemos en el código es que estamos añadiendo un manejador para el evento “onchange” para el el elemento de destino, pensando que lo utilizaremos en un Textbox.

En este manejador comprobamos si el valor de la propiedad del elemento es igual al modelo JavaScript y si no lo es, actualizamos el valor del modelo. Para poder utilizar este inicializador, podemos exponerlo definiendo un Namespace de la siguiente forma:

WinJS.Namespace.define("AppJS.Binding", {
  TwoWay: twoWay
});

De esta forma podremos aplicarlo en la declaración del enlace a datos en el elemento HTML del control en el que queramos aplicar el enlace en bidireccional. El siguiente código muestra la forma de hacerlo:

<input data-win-bind="value: text AppJS.Binding.twoWay" type="text" />

Si ahora escribimos en el campo de texto y cambiamos de control, el valor se actualizará en el modelo JavaScript y en todos los elementos en los que la propiedad este enlazada.

Referencias

Deep dive into WinJS
Data Binding in a Windows Store App with JavaScript