knockoutjs para hacerlo fácil
Igor Támara
Senior Software EngineerUna de las primeras librerías con las que un programador que inicia en la web es JQuery, madura, con millones de líneas de código de ejemplos. Cuando la página tiene más y más interacciones se tiende a buscar backbone y otras contemporáneas, incluso llegando a Angular. En esta entrada mostraremos un ejemplo sencillo con Knockoutjs para tener interacciones con código declarativo en una página web.
Primero le invito a
jugar Picas y Fijas, si no lo ha jugado antes, en esta versión es admisible la repetición de dígitos, para hacerlo un poco más interesante ;) .
La lógica
La lógica resulta bastante sencilla y no hace parte de este post, básicamente se tiene un motor de juego que tiene las siguientes funciones:
guess
: Que recibe una cadena con el intento y retorna un arreglo que en la primera posición tiene la cantidad de picas y la cantidad de fijas en la segunda posición.
won
: No recibe parámetros y retorna el estado del juego, es decir si ha ganado o no.
Adicionalmente tiene el atributo
history
que retorna un arreglo de elementos de tipo Attempt
de la cual se puede obtener el valor con el intento (guess
), la cantidad de picas (cows
) y la cantidad de fijas (bulls
). Puede acceder a la lógica del juego en github.
La presentación
Para hacer reactivo el juego se usa knockoutjs, tal librería tiene documentación maravillosa, ejemplos sencillos y tutoriales para comenzar a trabajar en línea. Es una librería opensource y es bastante declarativa para indicar qué elementos deben reaccionar y de qué forma. Está basada en el paradigma de observadores, de forma tal que se puede hacer que los modelos de datos y las interacciones estén en el servidor o que toda la lógica esté en javascript. De esta forma se puede cambiar la lógica de forma independiente de la capa de presentación.
Es usual que en ambientes web la capa de presentación sea la que genere más tracción, para efectos prácticos nos enfocaremos únicamente en los aspectos de la lógica de presentación, pero la librería puede combinarse con librerías externas para generar diferentes interacciones como mover elementos, mostrar imágenes, colocar sonidos.
El corazón de la interacción
El html necesario para que dado un intento podamos ver cuantas picas, fijas y cantidad de intentos luce de la siguiente forma:
<p>You are guessing a number of <span data-bind="text: sizecnb"></span> digits</p>
<p>Cows: <span data-bind="text: cows"></span></p>
<p>Bulls: <span data-bind="text: bulls"></span></p>
<p>Your guess:<input data-bind="value: guess"></p>
<p data-bind="visible: won" style="display:none;">Winner!!!!!!</p>
<p>Attempts: <span data-bind="text: attempts"></span></p>
Es un html que tiene
data-bind
en algunos de los campos básicos de html, en aquellos sitios donde deseamos que el usuario ingrese algo, serán value
como en <p>Your guess:<input data-bind="value: guess"></p>
, en aquellos donde queramos que el usuario pueda ver resultados colocamos text
como en <p>Attempts: <span data-bind="text: attempts"></span></p>
, para controlar la visibilidad usamos visible
como en <p data-bind="visible: won" style="display:none;">Winner!!!!!!</p>
El código que cablea la interacción de la capa lógica con la vista del usuario luce así:
var ViewModel = function(nthing) {
var self = this;
var logic = new CowsBulls(nthing);
self.won = ko.observable(false);
self.cows = ko.observable(0);
self.bulls = ko.observable(0);
self.attempts = ko.observable(0);
.
.
.
}
Se define una clase, en este caso definimos
ViewModel
, y definimos los atributos para que sean observables, por ejemplo bulls
se convirtió en observable al hacer self.bulls = ko.observable(0);
, de esta forma se inicializa con el valor 0
. Puede notar que también definimos logic
el motor de nuestro Picas y Fijas.
Respuesta al ingreso de datos de un usuario
Vamos a tomar el dato que ingresa el usuario en el campo de texto y procesarlo de forma tal que mostremos cuántas picas, fijas e intentos ha hecho el usuario. Cabe notar que no vamos usar un
observable
, si no un computed
, de esta forma cuando el usuario lo escriba usando la función write
podremos actualizar los observables
que previamente habíamos definido.
self.guess = ko.computed({
read: function() {
return '';
},
write: function(value) {
result = logic.guess(value);
self.won(logic.won());
self.bulls(result[0]);
self.cows(result[1]);
self.attempts(logic.history.length);
.
}
});
Como mencionamos anteriormente,
result
va a tener el resultado del intento, es decir la cantidad de picas y la cantidad de fijas, asignaremos sus valores a cows
y bulls
respectivamente, adicionalmente a won
le asignaremos el valor que nos ofrece la función won
de nuestro motor y a attempts
la cantidad de intentos que se han efectuado.
knockoutjs hace lo suyo
Una vez que hemos hecho nuestra vista, la conectamos mediante una sentencia como:
ko.applyBindings(new ViewModel(4));
Y así la se actualizará la información del html a partir de la lógica programada en nuestra vista.
Templates y la ayuda de Picas y Fijas
Generalmente para ganar, la gente tiende a escribir en un papelito, pero como estamos en modo conservación de árboles, hemos dotado a nuestro juego de un tablero de historia que ahorra posiciones de memoria en el cerebro del jugador en función de permitirle ganar en menor cantidad de intentos, es así como introducimos:
<p>Help me!!! <input type="checkbox" data-bind="checked: usehelp" /></p>
<p>Attempts: <span data-bind="text: attempts"></span></p>
<div data-bind="visible: usehelp" style="display:none;">
<table>
<thead>
<tr><th>Guessing</th><th>Cows</th><th>Bulls</th></tr>
</thead>
<tbody data-bind="foreach: helper">
<tr>
<td data-bind="text: guess"></td>
<td data-bind="text: cows"></td>
<td data-bind="text: bulls"></td>
</tr>
</tbody>
</table>
</div>
El checkbox
nos permitirá tener la visibilidad de la tabla que ofrecerá la ayuda que prometimos, y aquí es cuando hacemos uso de la forma más básica de templates
iterando sobre helper
con un foreach
.
self.helper(logic.history);
Referentes
- Código fuente del juego
- El juego
- knockoutjs
- Otros motores de juegos
- La foto de Neil Amstrong gracias a la NASA
Si quiere, puede hacerme un pull request para colocar css, o tal vez para hacer más flujo sobre el juego, por ejemplo: Jugar otra vez o elegir nivel de dificultad para evitar que se repitan los dígitos o para jugar en línea contra otra persona, colocar animaciones o tal vez para corregir algún bug que encuentre.
Nota: El hecho de que aquí se haya ilustrado un juego sencillo no quiere decir que Knockout sea solamente para hacer cosas de juguete.
Nota: La foto
Written by Igor Támara
A seasoned developer, Igor brings expertise in designing and building complex software systems. With a focus on quality and performance, they lead projects that drive innovation and deliver reliable solutions to meet user needs.