Extenders en KnockoutJS

A veces puede parecer que estoy un poco obsesionado con extender las librerías que utilizo, pero es que creo que es fundamental disponer de puntos de personalización a los que recurrir cuando sea necesario y eso es lo que acaba distinguiendo las librerías buenas de las menos buenas.

En la pequeña introducción/tutorial que hice sobre Knockout ya vimos cómo se podían crear nuevos bindings y observables para añadir funcionalidad a Knockout. Ahora le toca a otra opción que tenemos para personalizar Knockout: los extenders.

Los extenders permiten añadir funcionalidad a observables que ya existan (o a los que creemos nosotros) de una forma sencilla y reutilizable. Un extender puede modificar un observable interceptando sus lecturas/escrituras o incluso añadiendo nuevas propiedades al observable (maravillas de los lenguajes dinámicos).

Cómo crear un extender

Veamos un ejemplo de cómo crear y usar un extender. En este caso, vamos a crear un extender que nos permita validar que el valor que toma un observable se ajusta a una expresión regular, algo similar al RegularExpressionAttribute que se usa en ASP.NET MVC.

Para crear el extender, debemos añadir una propiedad al objeto ko.extenders con la función que se usará para crearlo:

ko.extenders.validateRegex = function(target, options) {

    target.hasError = ko.observable();
    target.errorMessage = ko.observable();

    function validate(newValue) {
        var isValid = options.regex.test(newValue);
        target.hasError(!isValid);
        target.errorMessage(isValid ? "" : options.message || 'Invalid format');
    }

    validate(target);
    target.subscribe(validate);

    return target;
};

El primer parámetro de la función, target, contiene el observable original que estamos extendiendo, y en el segundo podemos incluir los datos que necesitemos para configurar nuestro extender.

En este ejemplo estamos añadiendo al observable dos nuevas propiedades, hasError y validationMessage, que son a su vez observables y que van a indicar si hay algún error de validación y, en caso de que lo haya, el mensaje de error. Además, estamos suscribiendo la función validate al observable original para que, cada vez que se cambie el valor del observable, se lance la validación y se verifique que el nuevo valor se ajusta a la expresión regular.

Para aplicar el extender a un observable, debemos usar el siguiente código:

function AppViewModel(zipCode) {
    this.zipCode = ko.observable().extend({
        validateRegex: { regex: /\d{5}/, message: 'ZipCode must be a 5 digits string' }
    });
}

La función extend recibe como parámetro un objeto literal con una única propiedad, cuyo nombre se corresponde con el extender que queremos usar y cuyo valor contiene el parámetro que pasaremos al extender para construirlo, en nuestro ejemplo, las opciones con la expresión regular y el mensaje de error.

Lo último que nos faltaría es configurar el data binding, que se hace como ya hemos visto en otros ejemplos de data binding con Knockout:

<input type="text" data-bind="value: zipCode, valueUpdate: 'afterkeydown'"></input>
<span class="error-message" data-bind="text: zipCode.errorMessage, visible: zipCode.hasError"></span>

Extenders vs Computed Observables vs Custom Bindings

Hay problemas que se pueden resolver tanto con un computed observable como con un extender. En esos casos, ¿cuál es mejor utilizar?

Esto puede ser un poco cuestión de gustos, pero en mi opinión un custom observable debe utilizarse cuando necesitemos definir la naturaleza intrínseca del observable, como el numericObservable que usábamos en la aplicación del tutorial de Knockout.

Por su parte, creo que los extenders son más apropiados para definir responsabilidades trasversales (cross cutting concers) que se pueden reutilizar con distintos tipos de observables, como el caso de la validación que acabamos de ver. Además, podemos aplicar varios extenders a un mismo observable, lo que nos ayuda a mantener la lógica más focalizada y reutilizable.

¿Y los custom bindings? Para mi los custom bindings viven mucho más cerca de la vista y ese es el tipo de responsabilidades que deben asumir. Por ejemplo, a la hora de aplicar formatos a fechas, cambiar el tipo de control que se usa para modificar un valor, etc.

Como siempre, todo depende del contexto y no hay una respuesta tajante a qué tipo de técnica usar en cada situación, pero lo que está claro es que merece la pena conocer las tres para poder elegir.

2 comentarios en “Extenders en KnockoutJS

  1. Gabriel Villa dijo:

    Hola muy util tu tutorial, tengo un problema que supongo debo solucionar usando extenders pero no se bien que hacer, tengo 2 variables observables que guardan una fecha (dd/MM/yyyy) y quiero validar que la 2da sea mayor a la primera, me podrias ayudar?

Comentarios cerrados.