12. Interfaz de usuario web Web_UI


El web_ui permite, de una forma sencilla, hacer enlace da datos, añadir componentes web, crear plantillas y encapsulación.

El web_ui, utiliza un compilador, el cual se encarga de traducir el código a html y script, luego ejecuta la versión compilada.



El web_ui forma una capa intermedia entre el código y el navegador, dando nuevas habilidades como componentes web y plantillas.

Nueva aplicación con web_ui

El primer paso es crear una nueva aplicación 'littleben' marcando la opción Web application (using the web_ui libray)



El editor Dart al crear una aplicación con web_ui, añade todos los elementos necesarios:



  • Dependencias del paquete web_ui y del navegador
  • Librerías del paquete web_ui
  • Archivos de la aplicación web
  • En el archivo pubscepc.yaml se añaden las dependencias.
  • Archivo build.dart se encarga de compilar el archivo littleben.html, cada vez que se cambia el código:
En genera se puede hacer caso omiso de los archivos de la carpeta web/out que son creados por build.dart.

Para ejecutar la aplicación, se selecciona el archivo littleben.html original y se pulsa en Run.

Los ficheros de trasfondo contenidos en web/out, aseguran estar usando siempre la ultima versión de littleben.

Modificación del código por defecto

Cambiamos los siguientes archivos por este código:

littleben.html :

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Little Ben</title>
    <link rel="stylesheet" href="littleben.css">
  </head>
  <body>
    <h1>Little Ben</h1>

    <div>
      The current time is: {{currentTime}}
    </div>

    <script type="application/dart" src="littleben.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>


littleben.dart :

String currentTime;

void main() {
  Date today = new Date.now();
  currentTime = formatTime(today.hour, today.minute, today.second);
}

String formatTime(int h, int m, int s) {
  if (h > 12) { h = h - 12; }
  String minute = (m <= 9) ? '0$m' : '$m';
  String second = (s <= 9) ? '0$s' : '$s';
  return '$h:$minute:$second';
}


Guardamos los todos los archivos y damos a Run:


Inserción de datos en un sólo sentido: dart->html

Web_ui  permite vincular datos con una variable de la página html.
En este caso, la variable es una cadena que contiene la hora actual formato de una manera estándar.


  • La función principal main() obtiene la hora actual de la función Date().
  • La función formatTime devuelve una cadena con el formato de horas, minutos y segundos.
  • La varible global currentTime de tipo String guarda el resultado de formatTime.
  • En el fichero littleben.html  se usa directamente la variable currentTime.
La expresión a evaluar se encierra entre doble llames {{expresión}} y se integra como si fuera código HTML.

Esto se llama una sola vía de enlace de datos, porque los datos sólo se puede cambiar en el archivo .dart.
El web_ui utiliza un sistema de observadores (watchers) para sincronizar la interfaz HTML con los datos.
Al enviar observadores, las expresiones se evalúan y se actualiza el interfaz de usuario.


Observadores

El código creado con web_ui asigna un observador a toda expresión de la plantilla y se encarga de actualizara su valor en la interfaz.
En la aplicación littleben, la función main() sólo llama una vez a la expresión {{currentTime}} al ejecutar la aplicación. Siguiendo este proceso:


  • La aplicación littleben.dart establece el valor de la cadena  CurrentTime dentro de la función main().
  • La función main() termina su ejecución.
  • La interfaz de usuario lanza los observadores
  • El observador de los datos {{CurrentTime}} reconoce el cambio de datos, evalúa la expresión. y lanza la actualización de la interfaz.
  • La hora actual se muestra en la página, pero una sóla vez.


Versión mejorada littleben

Cambiar el archivo littleben.dart de la carpeta de la aplicación por este:

import 'dart:html';
import 'package:web_ui/watcher.dart' as watchers;

String currentTime;

void main() {
  window.setInterval(updateTime, 1000);
  updateTime();
}

void updateTime() {
  Date today = new Date.now();
  currentTime = formatTime(today.hour, today.minute, today.second);
  watchers.dispatch();
}

String formatTime(int h, int m, int s) {
  if (h > 12) { h = h - 12; }
  String minute = (m <= 9) ? '0$m' : '$m';
  String second = (s <= 9) ? '0$s' : '$s';
  return '$h:$minute:$second';
}


Los cambios entre la primera versión y esta nueva son:

  • Se han añadido dos importaciones:
    • dart:html : Clase window
    • Observadores del paquete web_ui
  • En main() se ha añadido la función updateTime()
  • En main() un temporazador windows.setInterval(), que llama a updateTime() cada 1000 milisegundos.
  • La función updateTime() es similar a formatTime(), tomando Date, dando formato y actualizando la variable global currentTime.
  • Se ha añadido la llamada watchers.disatch(), ya que la web_ui no envía automáticamente observadores a los eventos de intervalo de ventana. 
  • En muchos casos no hará falta incluir watchers.dispatch(). Si no funciona el código -> añadir watchers.dispatch()

Enlace de un ELEMENTO con una variable Dart de forma bi-direccional.

Creamos una nueva aplicación con web_ui, añadimos estos dos archivos y registramos paquete con Pub install:

shout.html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Shout This!</title>
    <link rel="stylesheet" href="shout.css">
  </head>
  <body>
    <h1>Shout This!</h1>

      <input type="text" bind-value="shoutThis" placeholder="Shout This!">
        
      <div>      Length: {{ shoutThis.length }} </div>
      <div>     Shouted: {{ shoutThis.toUpperCase() }} </div>
      <div>   Substring: {{ (shoutThis.length >= 6) ?
                             shoutThis.substring(1, 5) :
                             shoutThis.substring(0, 0) }} </div>
      <div> Palindromic: {{ palindrome() }} </div>
      
    <script type="application/dart" src="shout.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

shout.dart
import 'package:web_ui/watcher.dart' as watchers;
String shoutThis='';

void main() {
}

String palindrome() {
  var buffer = new StringBuffer(shoutThis);
  for (int i = shoutThis.length - 1; i >= 0; i--) {
    buffer.add(shoutThis[i]);
  }
  return buffer.toString();
}

Dando a Run veremos como a medida que se escribe, se actualizan el resto de datos del interfaz.



  • La variable string se declara en el código .dart y esta enlazado con el input del .html con bind-value.
  • Esto se llama enlace bi-direccional, pudiendos cambiar la cadena en el .html tecleando ó en el código .dart.
  • Los observadores mantienen sincronizados los los extremos de la conexión.
  • El web_ui se encarga automáticamente de lanzar los observadores para evaluar la variables y actualizar la interfaz.
  • bind-value es compatible con input, text area, drop-list
  • bind-checked es compatible con  radio button, checkbox.
  • También puede usarse bind-selectedIndex en selección de elementos.

Una expresión de plantilla {{}}, puede ser cualquier expresión .dart, que será convertida en cadena:

  • {{shoutThis.length}} devuelve la longitud de la cadena como entero pero convertido en cadena.
  • {{shoutThis.toUpperCase ()}} devuelve la cadena en mayúsculas.
  • Substring, si la cadena tiene más de 5 caracteres, devuelve la subcadena del 1 a 5 carácter. Si no, da nada.  
  • La última expresión llama a una función palindrome definida en .dart que resuelve el reverso de shoutThis.
  • La función palindrome no modifica shoutThis, de lo contrario entraría en un bucle infinito
Manipuladores de eventos de enlace

El paquete web_ui proporciona una manera de enlazar controles de eventos (onclick) con elementos de la interfaz.

Creamos una nueva aplicación con web_ui y cambiamos el código por este:

stopwatch.html
<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Stop Watch</title>
    <link rel="stylesheet" href="stopwatch.css">
  </head>
  <body>
    <h1>Stop Watch</h1>

    <div id="stopwatch">
      <div>
      {{counter}}
      </div>
      <div>
      <button on-click="startwatch()" id="startbutton">Start</button>
      <button on-click="stopwatch()"  id="stopbutton">Stop</button>
      <button on-click="resetwatch()" id="resetbutton">Reset</button>
      </div>
    </div>
    
    <script type="application/dart" src="stopwatch.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>


stopwatch.dart
import 'dart:html';
import 'package:web_ui/watcher.dart' as watchers;

String counter='00:00';
Stopwatch mywatch = new Stopwatch();

ButtonElement stopButton;
ButtonElement startButton;
ButtonElement resetButton;

void main() {
  startButton = query("#startbutton");
  stopButton = query("#stopbutton");
  stopButton.disabled = true;
  resetButton = query("#resetbutton");
  resetButton.disabled = true;
}

void startwatch() {
  mywatch.start();
  window.setInterval(updateTime, 1000);
  startButton.disabled = true;
  stopButton.disabled = false;
  resetButton.disabled = true;
}

void stopwatch() {
  mywatch.stop();
  startButton.disabled = false;
  resetButton.disabled = false;
  stopButton.disabled = true;
}

void resetwatch() {
  mywatch.reset();
  counter = '00:00';
  resetButton.disabled = true;
}

void updateTime() {
  var s = mywatch.elapsedMilliseconds~/1000;
  var m = 0;
  
  if (s >= 60) { m = s ~/ 60; s = s % 60; }
    
  String minute = (m <= 9) ? '0$m' : '$m';
  String second = (s <= 9) ? '0$s' : '$s';
  counter = '$minute:$second';
  watchers.dispatch();
}



En el archivo stopwatch.html, el botón con id startbutton asocia el control del evento clic con la función startwatch del archivo stopwatch.dart.

Otros eventos comunes son on-double-clic o on-change.

Estos eventos llaman a una función, en este ejemplo a startwatch().
La función startwatch() esta declarada como void (nula) y no toma ningún argumento.
Para enviar argumento se define asi: on-click = "startwatch($event)" y la función se declara void startwatch(Event e).

No hay comentarios: