Javascript para Desarrollo Web Client Side

Introducción

Hasta ahora vimos cómo las páginas presentan la información al usuario que puede disparar peticiones hacia el servidor. También podemos agregar comportamiento del lado del cliente: el código que escribamos en la página va a ser interpretado y ejecutado por el browser, esto nos permite manejar un cierto juego de eventos y acciones sin que entre en juego el servidor.

¿Qué vamos a querer hacer en el cliente? Manipular la página para
  • habilitar o inhabilitar acciones/campos
  • completar campos calculados
  • modificar propiedades de los controles 
  • crear y eliminar controles en forma dinámica
  • modificar el action del form para que el submit se dispare a distintas URLs
Pero también podemos querer:
  • manipular información local sobre la sesión del usuario
    • por ejemplo, para conocer el tiempo restante antes de que expire
  • conocer el tipo de navegador que tiene el cliente para chequear
    • si nuestra aplicación puede correr en ella o no
    • si podemos tratar de mejorar la estética
  • enriquecer la capacidad que tienen los controles estándar de HTML para mejorar "la experiencia de usuario", como 
    • los controles calendario, spinner, gauge, etc. 
    • búsquedas con autocompletado
  • hacer validaciones sencillas, en particular: 
    • campos no vacíos, 
    • formatos correctos (números, fechas), 
    • aritmética simple (números o fechas menores o mayores a), 
    • validaciones de campos compuestos que deben actuar en forma combinada: si seleccionaron buscar por DNI que hayan ingresado DNI. Más difícil suele ser validaciones del estilo: "dos clientes no pueden tener el mismo número de celular". En este artículo puede verse alternativas de validación.
 ¿Qué no vamos a querer hacer en el cliente?
  • Tomar la responsabilidad de resolver las consultas y las actualizaciones de los orígenes de datos (lo que comunmente se denomina "pegarle a la base")
  • Manejar transacciones
  • En general, no conocemos los recursos del servidor (sólo lo que podemos pedirle), por lo tanto vamos a pensar dentro del contexto de la página donde estamos programando.
El lenguaje en el que vamos a trabajar es Javascript [1], que es el estándar de todos los navegadores (las otras opciones trabajan sobre tecnología propietaria, como el VBScript y el JScript).

Antes que nada, es posible ejecutar código javascript del lado del servidor (RhinoSpider Monkey son algunos ejemplos), pero en esta materia nos vamos a concentrar la parte client-side. ¿Dónde vamos a poner el código javascript? En la vista.

Diferencias entre Java vs. javascript

Al definir una variable en javascript no digo de qué tipo es (el tipado es dinámico, en el momento de asignar un valor a esa variable toma el tipo del valor de lo que le mando).

Un ejemplo: en la calculadora.jsp escribimos

var nombre = "Fer";
alert(nombre.length);
nombre = 3;
alert(nombre + 2);

Vemos que nombre tiene una referencia a un objeto String, entonces le puedo pedir la longitud. Luego pasa a tener una referencia a un objeto Integer, entonces le puedo sumar 2. 
Esto en Java / Xtend no compila.
No compila.

Entonces anotamos:
  1. Java tiene chequeo estático de tipos (en tiempo de compilación) 
  2. mientras que javascript tiene chequeo dinámico de tipos (en tiempo de ejecución)
Porque efectivamente si yo quiero hacer

var nombre = "Fer";
alert(nombre.metodoInexistente());

esto va a dar error en runtime, mientras que en Java el compilador hace el chequeo de tipos y detecta el error en tiempo de compilación.

Java es un lenguaje orientado a objetos basado en clases, javascript es un lenguaje orientado a objetos basado en prototipos. No existe el concepto de clase en javascript:

var alumno = new Alumno(); <-- OJO QUE NO ES TAN ASI

no es una instrucción válida: tenemos que partir de la copia de un objeto conocido. Eso es algo que van a ver con más profundidad en Programación de Herramientas Modernas.

Resumen

En definitiva javascript nace como una especificación (ECMAScript) para ejecutar código en el browser y evitar así continuas idas y vueltas hacia el servidor que obliguen a recargar toda la página.

Javascript puede manipular los elementos gráficos o widgets del formulario a través del Document Object Model (DOM). Entonces podemos acceder a la página actual, o a un control HTML por medio de mensajes a objetos. De esa manera podemos averiguar qué valor tiene cierta propiedad de un control o bien modificarle dinámicamente alguna propiedad o comportamiento (como veremos más adelante). La modificación client-side del HTML de una página se conoce como DHTML (Dynamic HTML). 

Ejemplo de la calculadora client-side

(esta parte está basada en el proyecto calculadora-ui-js que pueden encontrar en los ejemplos client-side)

En cualquier link o botón (usando input type="button" en lugar de "submit")  podemos ejecutar una función de Javascript (en lugar de invocar a una nueva página). Escribimos:

<input type="button" value="Sumar" onClick="alert('hola');">

Además, la función confirm nos permite aceptar o confirmar una acción:
<input type="button" value="Sumar" onClick="if (confirm('te saludo?')) alert('hola');">

También podemos capturar eventos de los botones como onBlur (pérdida de foco), onChange (cualquier cambio) y onKeyup (se presionó una tecla). A partir de estas herramientas podemos comenzar a pensar una aplicación muy básica, una minicalculadora:

<form method="post" action="search">
    Ingrese un valor: <input id="op1" type="text">
    Ingrese otro valor: <input id="op2" type="text">
    <input type="button" id="sumar" value="Sumar" onClick="javascript:calcular();" >
    <span id="resultado" />
</form>

La función calcular tiene la siguiente forma:
<script language="javascript">
    function calcular() {
        var op1 = parseFloat(document.getElementById("op1").value);
        var op2 = parseFloat(document.getElementById("op2").value);
        document.getElementById("resultado").innerHTML = op1 + op2;
    }
</script>

La función getElementById nos permite acceder a un elemento del documento (el formulario HTML). La pseudo variable document nos permite acceder a la totalidad de la página (ver referencia de los mensajes que entiende document y otras pseudovariables disponibles en JavaScript). Podemos invalidar el botón si no tenemos números en los sumandos. Para eso agregamos una función actualizar estado:

function actualizarEstado() {
    var op1 = isNaN(parseFloat(document.getElementById("op1").value));
    var op2 = isNaN(parseFloat(document.getElementById("op2").value));
    document.getElementById("sumar").disabled = (op1 || op2);
}

Y a los texts les agregamos un listener para ese evento: onKeyup="javascript:actualizarEstado();"
Adicionalmente podemos hacer que el botón comience deshabilitado: disabled="true"

Otras pruebas que pueden hacer:
  • Manipular el HTML mediante los objetos del DOM, en lugar de innerHTML. Esto es un poco más prolijo si uno hace cosas complejas o con mucha lógica. Por ejemplo:
var resultado = document.createElement("input");
resultado.setAttribute("type", "text");
resultado.setAttribute("value", op1 + op2);
document.getElementById("resultado").appendChild(resultado);

Para jugar un poco, mover el resultado por la pantalla:
div = document.getElementById("resultado");
div.style.position = "absolute";
div.style.top = op1 + "px";
div.style.left = op2 + "px";

Automatizar el movimiento:

setInterval(moverResultado, 25);


function moverResultado() {
    currentLeft += 1;
    document.getElementById("resultado").style.left = currentLeft + "px";
}


Como hemos visto, javascript nos permite jugar bastante con el formulario:
  • podemos agregar o sacar controles
  • podemos habilitarlos/deshabilitarlos, hacerlos visibles/invisibles, en general cualquier propiedad de un control se puede modificar
  • las acciones que se disparan ante eventos de usuario son funciones, que son objetos. Entonces modificar el comportamiento de un botón o de cualquier otro control es simplemente cambiarle el nombre de la función hacia la cual apunta
Ejemplos en línea para descargar: en la página de la Unidad 3 - Client Side

Dándole más power a la calculadora

(este ejemplo está en la página CalculadoraDyn.jsp)

Creamos cuatro botones para sumar, restar, dividir y multiplicar. Para no definir cuatro funciones distintas en el botón enviamos la operación como un parámetro String:

<input type="button" id="suma" value=" + " onclick="calcula('+')" disabled="true"/>
<input type="button" id="resta" value=" - " onclick="calcula('-')" disabled="true"/>
<input type="button" id="multiplicacion" value=" x " onclick="calcula('*')" disabled="true"/>
<input type="button" id="division" value=" : " onclick="calcula('/')" disabled="true"/>

Cosas a tener en cuenta:
  • el id es importante para poder manipularlo vía javascript mediante la función getElementById
  • la propiedad name se usa cuando enviamos parámetros hacia el servidor, entonces es una buena estrategia que el id y el name conserven el mismo nombre
  • pasamos la operación como un objeto string hacia la función calcula: el botón con id suma pasa el string '+', el botón con id resta pasa el string '-', etc. 
  • la propiedad value define qué texto va a tener cada botón, puede diferir del string que pasamos para hacer el cálculo

Mmm... ¿y cómo queda en javascript el método calcula?

function calcula(operacion) {
    var operando1 = value("op1");
    var operando2 = value("op2");
    var result = eval(operando1 + operacion + operando2);
    $("resultado").innerHTML = result;
}

a) Como encontrar un control por el id es algo muy común y repetitivo, lo escribimos en una función $ (sí, "pesos")
b) La función "value" delega la búsqueda del control por id a "$" pero además devuelve la propiedad value
c) La función eval permite evaluar un string como código javascript:

"2 + 3" --> eval de eso me da 5
"80 * 2" --> eval de eso me da 160

Eval is Evil?

Como vemos, el eval me permite generar porciones de código y mandarlas a ejecutar: eso tiene mucho power pero es también motivo de debate filosófico: Douglas Crockford afirma "Eval is evil. Avoid it" casi sin explicación. John Riesig aporta una visión más ecléctica. No queremos participar por el momento de esa discusión, sólo decir:
  • que la necesidad de generar código dinámico existe
  • y que es antagónica a las cualidades de 
    • seguridad: tenemos menor control sobre el código que se ejecuta y estamos más expuestos a que el String haga cosas que no debiera
    • simplicidad: eval(operando1 + operacion + operando2) es menos directo que operando1 + operando2;
    • facilidad de depuración: al ser más compleja la solución es más difícil de debuggear/tracear
d) Un chiche más, para saber si tengo que habilitar el botón de división chequeo que el segundo operando no sea cero.

function actualizarEstado() {
$("resultado").style.display = "none";
var op1 = isNaN(value("op1"));
var op2 = isNaN(value("op2"));
var inhabilitado = (op1 || op2);
$("suma").disabled = inhabilitado;
$("resta").disabled = inhabilitado;
$("multiplicacion").disabled = inhabilitado;
$("division").disabled = inhabilitado || $("op2") == 0;
}