Compilando una App con PhoneGap Build

Adobe PhoneGap

Tal como ya sabemos, el uso de dispositivos móviles ha crecido de manera exponencial en los últimos años. Como consecuencia, el número de aplicaciones para dichos dispositivos se han incrementado en la misma medida. Esto ha motivado a su vez que se haya realizado un esfuerzo muy importante para desarrollar frameworks como IONIC que facilitan y mejoran tanto el proceso de programación de aplicaciones híbridas, como el resultado ofrecido por las mismas. Es aquí donde PhoneGap juega un papel importante.

Las aplicaciones híbridas nos proporcionan una gran ventaja respecto a las las nativas: con un sólo código fuente, podremos usar nuestra aplicación en cualquier dispositivo, independientemente del sistema operativo que tenga (iOS, Android, etc).

PhoneGap es un framework que brinda la posibilidad de desarrollar aplicaciones híbridas de manera gratuita utilizando tecnologías web muy simples tales como HTML, CSS y JavaScript. También podremos incluir IONIC en nuestros proyectos y compilar de la misma forma, obteniendo un resultado muy satisfactorio con un esfuerzo mínimo.

Además, dentro de las principales características de PhoneGap, nos encontramos con una serie de especificaciones para controlar los diferentes recursos del dispositivo, como por ejemplo:

  • Cámara
  • Acelerómetro
  • GPS
  • Notificaciones
  • Vibrador
  • Almacenamiento
  • Sistema de ficheros

Y por último queremos destacar que Adobe nos ofrece de forma gratuita el servicio online Phonegap Build. Se trata de un compilador al que podemos acceder vía web. Nos permite adjuntar un archivo a través de una cuenta en GitHub o un archivo .zip desde nuestro equipo:

Phonegap Build cuenta con varios tipos de planes, uno gratuito y otros de pago. En nuestro caso, podemos utilizar el servicio gratuito, ya que cubre perfectamente nuestras necesidades. La principal limitación que presenta es que sólo permite almacenar una aplicación privada, pero para nosotros será más que suficiente. También puedes encontrar más información aquí (en castellano) o en la página oficial (en inglés).

Desde mi punto de vista, PhoneGap Build nos proporciona una magnífica  herramienta para desarrollar aplicaciones multiplataformaya que nos brinda una manera de compilar nuestro código fuente de manera gratuita, sin necesidad de instalar nada en nuestros equipos. Incluso se puede utilizar desde cualquier ordenador por cualquier usuario, ya que no precisa disponer de software adicional ni permisos de administrador.

En resumen, PhoneGap nos brinda toda la funcionalidad del compilador, y no presenta ningún requisito. Sólo nos pide nuestros archivos HTML, CSS y JavaScript, y nos proporciona automáticamente el archivo APK con toda la funcionalidad nativa que necesitemos.

Cambios en el archivo index.html

En primer lugar, deberemos enlazar desde nuestro código HTML (index.html) el archivo JavaScript cordova.js (

<script src="cordova.js"></script>
<script src="cordova.js"></script>) que nos permitirá acceder a la funcionalidad nativa desde nuestras propias funciones:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
<head>
...
<script src="cordova.js"></script>
<script src="calculator.js"></script>
...
</head>
<body>
...
</body>
</html>
<!doctype html> <html lang="en"> <head> ... <script src="cordova.js"></script> <script src="calculator.js"></script> ... </head> <body> ... </body> </html>
<!doctype html>
<html lang="en">
  <head>
    ...
    <script src="cordova.js"></script>
    <script src="calculator.js"></script>
    ...
  </head>
  <body>
  ...
  </body>
</html>

El archivo cordova.js se generará automáticamente por PhoneGap Build y se incluirá en nuestro proyecto. Nos permitirá acceder por ejemplo a recursos tales como la cámara, las notificaciones, etc., desde nuestras propias funciones JavaScript utilizando las especificaciones ampliamente documentadas de Cordova. En este punto convienen aclarar que PhoneGap es simplemente una distribución de Apache Cordova, y en la actualidad ambos nombres se utilizan para referirse al mismo framework.

Para poder apreciar la diferencia entre la ejecución en el navegador y la ejecución como app en el móvil, añadiremos por ejemplo la funcionalidad de vibración a nuestra calculadora. Además, para poder ajustar el nivel de vibración, vamos a añadir un control adicional:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-list>
...
<ion-item>
<ion-range min="0" max="150" value="50" id="vibration">
<ion-icon slot="start" name="trending-down"></ion-icon>
<ion-icon slot="end" name="trending-up"></ion-icon>
</ion-range>
</ion-item>
</ion-list>
<ion-list> ... <ion-item> <ion-range min="0" max="150" value="50" id="vibration"> <ion-icon slot="start" name="trending-down"></ion-icon> <ion-icon slot="end" name="trending-up"></ion-icon> </ion-range> </ion-item> </ion-list>
<ion-list>
  ...
  <ion-item>
    <ion-range min="0" max="150" value="50" id="vibration">
      <ion-icon slot="start" name="trending-down"></ion-icon>
      <ion-icon slot="end" name="trending-up"></ion-icon>
    </ion-range>
  </ion-item>
</ion-list>

Podemos añadir dicho código por ejemplo justo después de los desplegables para ajustar el tamaño y tipo de los botones.

Utilizaremos el elemento

<ion-range></ion-range>
<ion-range></ion-range> para especificar la duración de la vibración por cada pulsación de tecla. En el ejemplo proponemos utilizar un tiempo de vibración que varíe de 0 (
min
min) a 150 (
max
max) milisegundos, con un valor por defecto de 50 (
value
value). Además añadiremos el icono
trending-down
trending-down al principio y
trending-up
trending-up al final del control, añadiendo los respectivos elementos
<ion-icon></ion-icon>
<ion-icon></ion-icon>:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-icon slot="start" name="trending-down"></ion-icon>
<ion-icon slot="end" name="trending-up"></ion-icon>
<ion-icon slot="start" name="trending-down"></ion-icon> <ion-icon slot="end" name="trending-up"></ion-icon>
<ion-icon slot="start" name="trending-down"></ion-icon>
<ion-icon slot="end" name="trending-up"></ion-icon>

Se puede consultar la documentación oficial de IONIC para ampliar detalles sobre todas las opciones de configuración del elemento range, y los iconos disponibles.

Resumiendo, desde nuestro archivo principal index.html deberemos enlazar el fichero cordova.js, y añadir el control para el ajuste del tiempo de la vibración:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
<head>
...
<script src="cordova.js"></script>
<script src="calculator.js"></script>
...
</head>
<body>
...
<ion-list>
...
<ion-item>
<ion-range min="0" max="150" value="50" id="vibration">
<ion-icon slot="start" name="trending-down"></ion-icon>
<ion-icon slot="end" name="trending-up"></ion-icon>
</ion-range>
</ion-item>
</ion-list>
...
</body>
</html>
<!doctype html> <html lang="en"> <head> ... <script src="cordova.js"></script> <script src="calculator.js"></script> ... </head> <body> ... <ion-list> ... <ion-item> <ion-range min="0" max="150" value="50" id="vibration"> <ion-icon slot="start" name="trending-down"></ion-icon> <ion-icon slot="end" name="trending-up"></ion-icon> </ion-range> </ion-item> </ion-list> ... </body> </html>
<!doctype html>
<html lang="en">
  <head>
    ...
    <script src="cordova.js"></script>
    <script src="calculator.js"></script>
    ...
  </head>
  <body>
  ...
    <ion-list>
      ...
      <ion-item>
        <ion-range min="0" max="150" value="50" id="vibration">
          <ion-icon slot="start" name="trending-down"></ion-icon>
          <ion-icon slot="end" name="trending-up"></ion-icon>
        </ion-range>
      </ion-item>
    </ion-list>  
  ...
  </body>
</html>

Cambios en el archivo calculator.js

En este ejercicio vamos a añadir una pequeña vibración cuando se pulse alguna tecla de la calculadora. Conseguiremos acceder a la vibración simplemente llamando a la función nativa desde nuestro código JavaScript. Por ejemplo, para provocar una vibración en nuestro dispositivo de 50 ms, bastaría con utilizar la siguiente línea de código:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
navigator.vibrate(50);
navigator.vibrate(50);
navigator.vibrate(50);

Ahora deberemos añadir esa línea a nuestro código JavaScript. Además, para poder ajustar el valor de la vibración, en vez de poner un valor fijo, leeremos el valor seleccionado en el nuevo control implementado con el elemento

<ion-range>
<ion-range> e identificado con el id
vibration
vibration. Por ejemplo, lo podemos hacer de la siguiente forma:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function setResult(value) {
navigator.vibrate(document.getElementById("vibration").value);
document.getElementById("result").innerHTML = value;
}
function setResult(value) { navigator.vibrate(document.getElementById("vibration").value); document.getElementById("result").innerHTML = value; }
function setResult(value) {
    navigator.vibrate(document.getElementById("vibration").value);
    document.getElementById("result").innerHTML = value;
}

Dicho código provocará una vibración cada vez que se pulse cualquier tecla y se actualice el contenido de la pantalla de la calculadora.

Se puede encontrar la documentación completa relativa a la funcionalidad de vibración en la página correspondiente del plugin en Github.

El archivo config.xml

PhoneGap Build nos pide un archivo de configuración donde le podremos especificar por ejemplo el nombre de la aplicación, el icono a utilizar, o la funcionalidad nativa del móvil a la que nos gustaría acceder.

El contenido básico del archivo config.xml podría ser el siguiente:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8" ?>
<widget xmlns = "http://www.w3.org/ns/widgets"
xmlns:gap = "http://phonegap.com/ns/1.0"
id = "com.fernandoruizrico.calculator"
versionCode = "1"
version = "0.0.1" >
<name>Calculator!</name>
<description>
A simple calculator.
</description>
<author href="https://fernandoruizrico.com" email="fernando@iessanvicente.com">
Fernando Ruiz Rico
</author>
<access origin="*" />
<plugin name="cordova-plugin-whitelist" />
<plugin name="cordova-plugin-vibration" />
</widget>
<?xml version="1.0" encoding="UTF-8" ?> <widget xmlns = "http://www.w3.org/ns/widgets" xmlns:gap = "http://phonegap.com/ns/1.0" id = "com.fernandoruizrico.calculator" versionCode = "1" version = "0.0.1" > <name>Calculator!</name> <description> A simple calculator. </description> <author href="https://fernandoruizrico.com" email="fernando@iessanvicente.com"> Fernando Ruiz Rico </author> <access origin="*" /> <plugin name="cordova-plugin-whitelist" /> <plugin name="cordova-plugin-vibration" /> </widget>
<?xml version="1.0" encoding="UTF-8" ?>
<widget xmlns   = "http://www.w3.org/ns/widgets"
    xmlns:gap   = "http://phonegap.com/ns/1.0"
    id          = "com.fernandoruizrico.calculator"
    versionCode = "1"
    version     = "0.0.1" >

  <name>Calculator!</name>

  <description>
      A simple calculator.
  </description>

  <author href="https://fernandoruizrico.com" email="fernando@iessanvicente.com">
      Fernando Ruiz Rico
  </author>

  <access origin="*" />
  <plugin name="cordova-plugin-whitelist" />
  <plugin name="cordova-plugin-vibration" />
</widget>

El elemento

<widget>
<widget> debe encontrarse en el nivel principal del archivo. Dentro de dicho elemento, podremos especificar el valor de los siguientes atributos:

  • id: El identificador único de nuestra aplicación. Se debe utilizar la notación de nombre de dominio inverso (por ejemplo,
    com.tuempresa.tuapp
    com.tuempresa.tuapp).
  • version: Código de versión de tres cifras (cambio mayor/cambio menor/corrección errores), como por ejemplo
    0.0.1
    0.0.1. Se incrementará la primera cifra cuando realicemos grandes cambios y mejoras. La segunda cuando hagamos pequeños cambios y correcciones de errores, y la tercera cuando simplemente se realice alguna corrección.
  • versionCode: Este atributo nos permite especificar el código de versión. Es opcional y sólo se utiliza cuando compilamos la aplicación para Android. Para más información puedes consultar la documentación oficial aquí.

Utilizando el elemento

<name></name>
<name></name> podremos especificar el nombre de la aplicación que aparecerá debajo del icono. PhoneGap Build colocará de momento un icono por defecto, y ya más adelante (en otro ejercicio) proporcionaremos los detalles sobre cómo especificar y proporcionar el icono de la app.

El elemento

<access />
<access /> nos permite especificar si damos permiso para acceder a archivos externos, como por ejemplo, para enlazar librerías directamente desde un servidor utilizando la conexión de Internet del móvil. Por ejemplo, nos permitirá acceder al servidor de IONIC desde nuestro archivo index.html, sin necesidad de instalar o descargar nada, tal como hemos hecho en la calculadora:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/>
...
... <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script> <script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/> ...
...
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/>
...

De momento permitiremos acceso a cualquier servidor (

<access origin="*" />
<access origin="*" />), y más adelante veremos cómo incluir directamente en la app el código de IONIC para poder prescindir incluso de la conexión de Internet en aplicaciones que no requieran de la misma.

En las últimas versiones de Cordova, resulta necesario añadir el plugin  

cordova-plugin-whitelist
cordova-plugin-whitelist para que se puedan hacer efectivos los permisos especificados con la etiqueta anterior. Le indicaremos a PhoneGap Build que añada automáticamente el código del plugin mediante la siguiente línea:

<plugin name="cordova-plugin-whitelist" />
<plugin name="cordova-plugin-whitelist" />

Además, utlizaremos el plugin

cordova-plugin-vibration
cordova-plugin-vibration para hacer que el móvil vibre cada vez que se pulse una tecla. Para pedirle a PhoneGap Build que incluya en nuestra aplicación la funcionalidad de vibración, deberemos añadir la línea correspondiente al archivo config.xml:

<plugin name="cordova-plugin-vibration" />
<plugin name="cordova-plugin-vibration" />

Puedes encontrar aquí un resumen de la documentación original (en inglés) relativa al fichero config.xml, y aquí la documentación completa (también en inglés).

Sobre los dos plugins utilizados en este ejemplo, puedes obtener documentación adicional aquí: cordova-plugin-whitelist y cordova-plugin-vibration.

¿Cómo generar el archivo APK?

En este ejercicio seguiremos sin instalar nada en nuestros ordenadores. Para compilar nuestra aplicación y obtener el archivo APK, utilizaremos el servicio online gratuito PhoneGap Build, tal como hemos sugerido. Para ello, deberemos acceder a la página principal de dicho servicio y pulsar en la opción Sign in (en la esquina superior derecha).

Puesto que en los últimos meses han estado realizando algunos cambios en la página web de PhoneGap Build y en los servicios que ofrecen, a continuación indicamos un par de capturas antiguas. Si aparecieran de nuevo las opciones de pago, en nuestro caso deberemos escoger la opción gratuita (Free Plan):

Para poder compilar la aplicación deberemos autentificarnos utilizando usuario y contraseña. Para ello, se puede crear una cuenta directamente desde dicha página web, o también podemos acceder mediante una cuenta de Facebook o Google.

Una vez hayamos accedido, si hacemos clic en Apps -> private, veremos la siguiente pantalla:

Ahí podemos seleccionar si queremos generar la aplicación utilizando el código disponible desde un enlace de Github, o utilizando un archivo zip, que será nuestro caso. Para ello utilizaremos el botón Upload a .zip file.

Deberemos haber comprimido previamente todos los archivos de nuestra aplicación en un mismo fichero ZIP. Por ejemplo, para nuestro ejercicio de la calculadora con IONIC 4, el archivo comprimido debería contener tres ficheros: index.html, calculator.jsconfig.xml.

Si se ha subido correctamente el archivo, estaréis delante de la siguiente pantalla:

Bastará con hacer click en Ready to build, y esperar un poco, hasta que PhoneGap Build compile la aplicación y genere el archivo APK.

Si todo ha ido bien, podréis descargar e instalar directamente vuestra aplicación mediante un código QR, o haciendo click en el icono de Android:

Finalmente, conviene destacar que podemos repetir este proceso tantas veces como queramos. Podemos cambiar, subir, compilar e instalar de nuevo la aplicación utilizando el botón Update Code. Y todo eso de forma gratuita y sin instalar nada en nuestros equipos… no se puede pedir más.

En resumen…

El archivo index.html

Desde nuestro archivo principal index.html deberemos enlazar el fichero cordova.js, y añadir el control para el ajuste del tiempo de la vibración:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
<head>
...
<script src="cordova.js"></script>
<script src="calculator.js"></script>
...
</head>
<body>
...
<ion-list>
...
<ion-item>
<ion-range min="0" max="150" value="50" id="vibration">
<ion-icon slot="start" name="trending-down"></ion-icon>
<ion-icon slot="end" name="trending-up"></ion-icon>
</ion-range>
</ion-item>
</ion-list>
...
</body>
</html>
<!doctype html> <html lang="en"> <head> ... <script src="cordova.js"></script> <script src="calculator.js"></script> ... </head> <body> ... <ion-list> ... <ion-item> <ion-range min="0" max="150" value="50" id="vibration"> <ion-icon slot="start" name="trending-down"></ion-icon> <ion-icon slot="end" name="trending-up"></ion-icon> </ion-range> </ion-item> </ion-list> ... </body> </html>
<!doctype html>
<html lang="en">
  <head>
    ...
    <script src="cordova.js"></script>
    <script src="calculator.js"></script>
    ...
  </head>
  <body>
  ...
    <ion-list>
      ...
      <ion-item>
        <ion-range min="0" max="150" value="50" id="vibration">
          <ion-icon slot="start" name="trending-down"></ion-icon>
          <ion-icon slot="end" name="trending-up"></ion-icon>
        </ion-range>
      </ion-item>
    </ion-list>  
  ...
  </body>
</html>

El archivo calculator.js

Desde nuestro propio archivo donde hayamos ubicado nuestras funciones de JavaScript (por ejemplo, calculator.js), deberemos recoger el valor seleccionado en el nuevo control, y llamar a la funcion

vibrate
vibrate para acceder a la funcionalidad nativa correspondiente:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
function setResult(value) {
navigator.vibrate(document.getElementById("vibration").value);
document.getElementById("result").innerHTML = value;
}
...
... function setResult(value) { navigator.vibrate(document.getElementById("vibration").value); document.getElementById("result").innerHTML = value; } ...
...
function setResult(value) {
    navigator.vibrate(document.getElementById("vibration").value);
    document.getElementById("result").innerHTML = value;
}
...

El archivo config.xml

Para poder compilar la aplicación desde PhoneGap Build, debemos proporcionar un archivo de configuración config.xml, donde especificaremos el nombre de la aplicación y los plugins utilizados:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8" ?>
<widget xmlns = "http://www.w3.org/ns/widgets"
xmlns:gap = "http://phonegap.com/ns/1.0"
id = "com.fernandoruizrico.calculator"
versionCode = "1"
version = "0.0.1" >
<name>Calculator!</name>
<description>
A simple calculator.
</description>
<author href="https://fernandoruizrico.com" email="fernando@iessanvicente.com">
Fernando Ruiz Rico
</author>
<access origin="*" />
<plugin name="cordova-plugin-whitelist" />
<plugin name="cordova-plugin-vibration" />
</widget>
<?xml version="1.0" encoding="UTF-8" ?> <widget xmlns = "http://www.w3.org/ns/widgets" xmlns:gap = "http://phonegap.com/ns/1.0" id = "com.fernandoruizrico.calculator" versionCode = "1" version = "0.0.1" > <name>Calculator!</name> <description> A simple calculator. </description> <author href="https://fernandoruizrico.com" email="fernando@iessanvicente.com"> Fernando Ruiz Rico </author> <access origin="*" /> <plugin name="cordova-plugin-whitelist" /> <plugin name="cordova-plugin-vibration" /> </widget>
<?xml version="1.0" encoding="UTF-8" ?>
<widget xmlns   = "http://www.w3.org/ns/widgets"
    xmlns:gap   = "http://phonegap.com/ns/1.0"
    id          = "com.fernandoruizrico.calculator"
    versionCode = "1"
    version     = "0.0.1" >

  <name>Calculator!</name>

  <description>
      A simple calculator.
  </description>

  <author href="https://fernandoruizrico.com" email="fernando@iessanvicente.com">
      Fernando Ruiz Rico
  </author>

  <access origin="*" />
  <plugin name="cordova-plugin-whitelist" />
  <plugin name="cordova-plugin-vibration" />
</widget>

El archivo .zip

A PhoneGap Build le deberemos proporcionar un fichero comprimido con formato ZIP que contenga los tres archivos especificados: index.html, calculator.js y config.xml.

El resultado

Calculadora con estilo personalizable utilizando IONIC 4

¿Cómo podemos utilizar IONIC 4?

Si deseamos utilizar IONIC 4, sólo necesitaremos enlazar los archivos JavaScript y CSS, tal como se especifica en la documentación y en github:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/>
...
... <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script> <script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/> ...
...
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/>
...

De esta forma, quedarán definidos de forma automática los nuevos elementos, haciéndolos completamente accesibles al navegador para que nosotros sólo tengamos que escribir la etiqueta correspondiente en nuestro archivo HTML. A su vez además podremos interactuar con ellos utilizando código JavaScript.

Esta magnífica innovación que nos proporciona IONIC en su versión 4 simplifica muchísimo el acceso a sus archivos, ya que podemos utilizarlos sin necesidad de descargar ni instalar absolutamente nada.

Como se puede observar, estamos utilizamos la versión 4.9.1 de IONIC, que es la última que actualmente podemos encontrar en repositorio. Por otro lado, si deseáramos utilizar siempre la última versión, también podríamos acceder a dichos archivos utilizando la referencia «@ionic/core@latest». En tal caso, debemos tener en cuenta que con el paso del tiempo deberíamos comprobar si los últimos cambios pudieran afectar al código que escribimos inicialmente.

Más adelante describiremos el procedimiento para acceder a la funcionalidad de IONIC sin enlazar el código directamente desde Internet y así evitaremos que se descargue cada vez. Sin embargo, de momento podemos centrarnos en el código y el resultado, utilizando un simple editor de texto, sin necesidad de tener que instalar nada en nuestros equipos para poder utilizar IONIC.

Colocando los botones

En el ejemplo anterior hemos utilizado una tabla de HTML para distribuir los botones en filas y columnas. Sin embargo, las tablas se usan cada vez menos con este propósito. En su lugar se suele utilizar código CSS para ubicar o distribuir los diferentes elementos.

En este aspecto, IONIC nos proporciona una herramienta increíble conocida como grid, que tiene un comportamiento muy similar al de otras librerías de desarrollo web, como por ejemplo Bootstrap.

IONIC ha creado para nosotros tres nuevos elementos HTML que nos permiten establecer una cuadrícula muy fácilmente. Delimitamos los elementos que vamos a colocar mediante

<ion-grid></ion-grid>
<ion-grid></ion-grid>  y luego establecemos las filas mediante 
<ion-row></ion-row>
<ion-row></ion-row> y las columnas con
<ion-col></ion-col>
<ion-col></ion-col>.

Gracias a esto, podemos distribuir los botones de la calculadora utilizando el elemento grid de IONIC con un código casi idéntico al que utilizaríamos con una simple tabla de HTML, simplemente reemplazando las etiquetas de la siguiente forma:

  • <table></table>
    <table></table> ->
    <ion-grid></ion-grid>
    <ion-grid></ion-grid>
  • <tr></tr>
    <tr></tr> -> 
    <ion-row></ion-row>
    <ion-row></ion-row>
  • <td></td>
    <td></td> -> 
    <ion-col></ion-col>
    <ion-col></ion-col>

Y no sólo nos proporciona una forma sencilla de realizar dicha distribución, sino que nos aporta mucha más flexibilidad que las tablas de HTML, tal como se puede observar en la documentación oficial. Además, por defecto ocupará todo el ancho de la pantalla (el comportamiento esperado en cualquier dispositivo móvil) y cada fila podrá tener un número diferente de columnas, cuya anchura se distribuirá por defecto de manera uniforme.

Por ejemplo, para dibujar la siguiente tabla:

0
123+
456
789*
0/
AC.=

En el ejercicio anterior hemos utilizado el siguiente código HTML:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<table>
<tr>
<td colspan="4">0</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>+</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
<td>-</td>
</tr>
<tr>
<td>7</td>
<td>8</td>
<td>9</td>
<td>*</td>
</tr>
<tr>
<td colspan="3">0</td>
<td>/</td>
</tr>
<tr>
<td colspan="2">AC</td>
<td>.</td>
<td>=</td>
</tr>
</table>
<table> <tr> <td colspan="4">0</td> </tr> <tr> <td>1</td> <td>2</td> <td>3</td> <td>+</td> </tr> <tr> <td>4</td> <td>5</td> <td>6</td> <td>-</td> </tr> <tr> <td>7</td> <td>8</td> <td>9</td> <td>*</td> </tr> <tr> <td colspan="3">0</td> <td>/</td> </tr> <tr> <td colspan="2">AC</td> <td>.</td> <td>=</td> </tr> </table>
<table>
  <tr>
    <td colspan="4">0</td>
  </tr>
  <tr>
    <td>1</td>
    <td>2</td>
    <td>3</td>
    <td>+</td>
  </tr>
  <tr>
    <td>4</td>
    <td>5</td>
    <td>6</td>
    <td>-</td>
  </tr>
  <tr>
    <td>7</td>
    <td>8</td>
    <td>9</td>
    <td>*</td>
  </tr>
  <tr>
    <td colspan="3">0</td>
    <td>/</td>          
  </tr>
  <tr>
    <td colspan="2">AC</td>
    <td>.</td>
    <td>=</td>            
  </tr>
</table>

Si utilizamos los nuevos elementos que nos proporciona IONIC 4, obtendríamos el mismo resultado con el siguiente código:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-grid>
<ion-row>
<ion-col>0</ion-col>
</ion-row>
<ion-row>
<ion-col>1</ion-col>
<ion-col>2</ion-col>
<ion-col>3</ion-col>
<ion-col>+</ion-col>
</ion-row>
<ion-row>
<ion-col>4</ion-col>
<ion-col>5</ion-col>
<ion-col>6</ion-col>
<ion-col>-</ion-col>
</ion-row>
<ion-row>
<ion-col>7</ion-col>
<ion-col>8</ion-col>
<ion-col>9</ion-col>
<ion-col>*</ion-col>
</ion-row>
<ion-row>
<ion-col size="9">0</ion-col>
<ion-col>/</ion-col>
</ion-row>
<ion-row>
<ion-col size="6">AC</ion-col>
<ion-col>.</ion-col>
<ion-col>=</ion-col>
</ion-row>
</ion-grid>
<ion-grid> <ion-row> <ion-col>0</ion-col> </ion-row> <ion-row> <ion-col>1</ion-col> <ion-col>2</ion-col> <ion-col>3</ion-col> <ion-col>+</ion-col> </ion-row> <ion-row> <ion-col>4</ion-col> <ion-col>5</ion-col> <ion-col>6</ion-col> <ion-col>-</ion-col> </ion-row> <ion-row> <ion-col>7</ion-col> <ion-col>8</ion-col> <ion-col>9</ion-col> <ion-col>*</ion-col> </ion-row> <ion-row> <ion-col size="9">0</ion-col> <ion-col>/</ion-col> </ion-row> <ion-row> <ion-col size="6">AC</ion-col> <ion-col>.</ion-col> <ion-col>=</ion-col> </ion-row> </ion-grid>
<ion-grid>
  <ion-row>
    <ion-col>0</ion-col>
  </ion-row>
  <ion-row>
    <ion-col>1</ion-col>
    <ion-col>2</ion-col>
    <ion-col>3</ion-col>
    <ion-col>+</ion-col>
  </ion-row>
  <ion-row>
    <ion-col>4</ion-col>
    <ion-col>5</ion-col>
    <ion-col>6</ion-col>
    <ion-col>-</ion-col>
  </ion-row>
  <ion-row>
    <ion-col>7</ion-col>
    <ion-col>8</ion-col>
    <ion-col>9</ion-col>
    <ion-col>*</ion-col>
  </ion-row>
  <ion-row>
    <ion-col size="9">0</ion-col>
    <ion-col>/</ion-col>
  </ion-row>
  <ion-row>
    <ion-col size="6">AC</ion-col>
    <ion-col>.</ion-col>
    <ion-col>=</ion-col>
  </ion-row>
</ion-grid>

Tenemos que destacar la forma que nos ofrece IONIC 4 para establecer el tamaño de una celda en concreto. Por ejemplo, en nuestro caso, para conseguir que la tecla del cero ocupe 3/4 de la pantalla, y que la tecla de borrado ocupe la mitad, ajustamos el valor del atributo size en el elemento ion-col:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...
<ion-row>
<ion-col size="9">0</ion-col>
<ion-col>/</ion-col>
</ion-row>
<ion-row>
<ion-col size="6">AC</ion-col>
<ion-col>.</ion-col>
<ion-col>=</ion-col>
</ion-row>
...
... <ion-row> <ion-col size="9">0</ion-col> <ion-col>/</ion-col> </ion-row> <ion-row> <ion-col size="6">AC</ion-col> <ion-col>.</ion-col> <ion-col>=</ion-col> </ion-row> ...
...
<ion-row>
  <ion-col size="9">0</ion-col>
  <ion-col>/</ion-col>
</ion-row>
<ion-row>
  <ion-col size="6">AC</ion-col>
  <ion-col>.</ion-col>
  <ion-col>=</ion-col>
</ion-row>
...

IONIC establece por defecto que en la pantalla caben 12 columnas. Por lo tanto, al especificar 

<ion-col size="9">0</ion-col>
<ion-col size="9">0</ion-col>, estamos diciéndole al navegador que esa celda ocupará 9 columnas de un total de 12, es decir, 3/4 de la pantalla. Y al especificar 
<ion-col size="6">AC</ion-col>
<ion-col size="6">AC</ion-col>, estamos diciéndole al navegador que esa celda ocupará 6 columnas de un total de 12, es decir, la mitad de la pantalla.

Observamos además que en la primera fila, donde mostramos el resultado, no hace falta especificar el tamaño, ya que por defecto, la celda se expande automáticamente para ocupar todo el ancho de la fila.

Veremos más adelante que el número de columnas por defecto se puede configurar cambiando el valor de la variable CSS

--ion-grid-columns
--ion-grid-columns.

Los nuevos botones

Con IONIC 4 disponemos de un nuevo elemento

<ion-button></ion-button>
<ion-button></ion-button>. Se utiliza de forma similar al
<button></button>
<button></button> de HTML, pero como podemos leer en la documentación, ahora podemos cambiar el aspecto del botón mediante atributos específicos tales como expand, fill, size o color. Por ejemplo, podríamos definir el botón de borrado de la siguiente forma, sin necesidad de utilizar ningún otro código adicional:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-button color="danger" expand="block" onclick="del()">AC</ion-button>
<ion-button color="danger" expand="block" onclick="del()">AC</ion-button>
<ion-button color="danger" expand="block" onclick="del()">AC</ion-button>

El navegador nos mostrará automáticamente un botón de color rojo que ocupará todo el ancho de la columna.

Como podemos observar, la sencillez y similitud respecto al código básico HTML son obvias. El mayor cambio en este nuevo elemento es que ahora no necesitamos ajustar ninguna propiedad CSS para obtener el aspecto deseado, tal como hacíamos antes:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
.ac {
color: red;
}
.ac { color: red; }
.ac {
  color: red;
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<button class="ac">AC</button>
<button class="ac">AC</button>
<button class="ac">AC</button>

Para cambiar el estilo

En este apartado comenzaremos a apreciar la verdadera magia de IONIC 4. Fijémos en la similitud entre este código:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<select>
<option value="clear">Sin borde ni color</option>
<option value="outline">Sólo borde</option>
<option value="solid" selected>Con color de fondo</option>
</select>
<select> <option value="clear">Sin borde ni color</option> <option value="outline">Sólo borde</option> <option value="solid" selected>Con color de fondo</option> </select>
<select>
    <option value="clear">Sin borde ni color</option>
    <option value="outline">Sólo borde</option>
    <option value="solid" selected>Con color de fondo</option>
</select>

y este otro:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-select value="solid">
<ion-select-option value="clear">Sin borde ni color</ion-select-option>
<ion-select-option value="outline">Sólo borde</ion-select-option>
<ion-select-option value="solid">Con color de fondo</ion-select-option>
</ion-select>
<ion-select value="solid"> <ion-select-option value="clear">Sin borde ni color</ion-select-option> <ion-select-option value="outline">Sólo borde</ion-select-option> <ion-select-option value="solid">Con color de fondo</ion-select-option> </ion-select>
<ion-select value="solid">
  <ion-select-option value="clear">Sin borde ni color</ion-select-option>
  <ion-select-option value="outline">Sólo borde</ion-select-option>
  <ion-select-option value="solid">Con color de fondo</ion-select-option>
</ion-select>

A simple vista podemos intuir que el resultado debería ser el mismo. Sin embargo, al pulsar sobre el segundo select, nos aparecerá un cuadro de diálogo con un diseño muy cuidado que además será diferente en móviles IOS y en Android. Además, por defecto dispondremos de un botón para aceptar y otro para cancelar.

En la documentación de IONIC podemos deleitarnos observando el resultado que llegaremos a obtener haciendo uso de este nuevo elemento. Encontraremos además en ese enlace todos los posibles atributos con sus posibles respectivos valores, que nos permitirán ajustar el comportamiento del mismo a nuestros gustos y necesidades.

Debemos matizar que para obtener una correcta visualización, deberemos incluir cada elemento dentro de una lista, tal como se observa en los ejemplos, y que utilizaremos el elemento

<ion-label></ion-label>
<ion-label></ion-label> para establecer el texto descriptivo, que también disparará el evento onclick automáticamente al pulsar encima. Por ejemplo, en nuestro caso:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-list>
<ion-item>
<ion-label>Tamaño botones</ion-label>
<ion-select value="default" id="size">
<ion-select-option value="small">Pequeños</ion-select-option>
<ion-select-option value="default">Medianos</ion-select-option>
<ion-select-option value="large">Grandes</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label>Tipo botones</ion-label>
<ion-select value="solid" id="type">
<ion-select-option value="clear">Sin borde ni color</ion-select-option>
<ion-select-option value="outline">Sólo borde</ion-select-option>
<ion-select-option value="solid">Con color de fondo</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list> <ion-item> <ion-label>Tamaño botones</ion-label> <ion-select value="default" id="size"> <ion-select-option value="small">Pequeños</ion-select-option> <ion-select-option value="default">Medianos</ion-select-option> <ion-select-option value="large">Grandes</ion-select-option> </ion-select> </ion-item> <ion-item> <ion-label>Tipo botones</ion-label> <ion-select value="solid" id="type"> <ion-select-option value="clear">Sin borde ni color</ion-select-option> <ion-select-option value="outline">Sólo borde</ion-select-option> <ion-select-option value="solid">Con color de fondo</ion-select-option> </ion-select> </ion-item> </ion-list>
<ion-list>
  <ion-item>
    <ion-label>Tamaño botones</ion-label>
    <ion-select value="default" id="size">
      <ion-select-option value="small">Pequeños</ion-select-option>
      <ion-select-option value="default">Medianos</ion-select-option>
      <ion-select-option value="large">Grandes</ion-select-option>
    </ion-select>
  </ion-item>
  <ion-item>
    <ion-label>Tipo botones</ion-label>
    <ion-select value="solid" id="type">
      <ion-select-option value="clear">Sin borde ni color</ion-select-option>
      <ion-select-option value="outline">Sólo borde</ion-select-option>
      <ion-select-option value="solid">Con color de fondo</ion-select-option>
    </ion-select>
  </ion-item>
</ion-list>

¿Estilo IOS o Android?

La verdad es que resulta difícil contestar a esa pregunta, y por eso los desarrolladores de IONIC nos permiten elegir el aspecto que van a tener los diferentes elementos de nuestros formularios. Para ello han añadido el atributo mode a muchos elementos, de forma que podamos especificar los valores «ios» o «md».

En caso de que queramos cambiar el estilo de todos los elementos de la calculadora al mismo tiempo, IONIC también nos permite hacerlo modificando la url, tal como se especifica en la documentación:

  • index.html?ionic:mode=md
  • index.html?ionic:mode=ios

Para mejorar el diseño de la calculadora, añadiremos a la misma un encabezado (elemento

<ion-header></ion-header>
<ion-header></ion-header>) que contendrá un título y dos botones con un atributo href que apuntarán a esas dos urls. Además, observaremos al probar la nueva calculadora que el propio estilo del encabezado que contiene esos dos botones también se actualizará:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ion-header>
<ion-toolbar>
<ion-title>Calculator</ion-title>
<ion-buttons slot="secondary">
<ion-button href="index.html?ionic:mode=md">Android</ion-button>
</ion-buttons>
<ion-buttons slot="primary">
<ion-button href="index.html?ionic:mode=ios">IOS</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-header> <ion-toolbar> <ion-title>Calculator</ion-title> <ion-buttons slot="secondary"> <ion-button href="index.html?ionic:mode=md">Android</ion-button> </ion-buttons> <ion-buttons slot="primary"> <ion-button href="index.html?ionic:mode=ios">IOS</ion-button> </ion-buttons> </ion-toolbar> </ion-header>
<ion-header>
  <ion-toolbar>
    <ion-title>Calculator</ion-title>
    <ion-buttons slot="secondary">
      <ion-button href="index.html?ionic:mode=md">Android</ion-button>
    </ion-buttons>
    <ion-buttons slot="primary">
      <ion-button href="index.html?ionic:mode=ios">IOS</ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

Cuando iniciemos la aplicación, ésta se mostrará automáticamente utilizando el estilo de la plataforma en la que se esté ejecutando.

¿Código JavaScript adicional?

Pues la verdad es que muy poco:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function onLoad() {
document.addEventListener("ionChange", setStyle);
setStyle();
}
function setStyle() {
document.querySelectorAll("ion-content ion-button").forEach(function(b) {
b.expand = "block";
b.strong = "true";
b.fill = document.getElementById("type").value;
b.size = document.getElementById("size").value;
});
}
function onLoad() { document.addEventListener("ionChange", setStyle); setStyle(); } function setStyle() { document.querySelectorAll("ion-content ion-button").forEach(function(b) { b.expand = "block"; b.strong = "true"; b.fill = document.getElementById("type").value; b.size = document.getElementById("size").value; }); }
function onLoad() {
    document.addEventListener("ionChange", setStyle);
    setStyle();
}

function setStyle() {
    document.querySelectorAll("ion-content ion-button").forEach(function(b) {
        b.expand = "block";
        b.strong = "true";
        b.fill = document.getElementById("type").value;
        b.size = document.getElementById("size").value;
    });
}

Los elementos

<ion-select></ion-select>
<ion-select></ion-select> generarán un evento ionChange automáticamente cada vez que cambie el valor seleccionado. Lo único que tenemos que hacer es estar atentos y volver a ajustar el diseño de la calculadora cuando se dispare dicho evento. Para que se ejecute una acción automáticamente al detectar cualquier cambio, utilizamos la siguiente línea:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
document.addEventListener("ionChange", setStyle);
document.addEventListener("ionChange", setStyle);
document.addEventListener("ionChange", setStyle);

Una vez se ejecute la función setStyle(), lo único que tendremos que hacer es recorrer todos los botones y actualizar las propiedades que queramos. En nuestro caso podemos cambiar las siguientes propiedades:

  • expand: «block»
  • strong: «true»
  • fill: «clear», «outline» o «solid»
  • size: «small», «default» o «large»

Podemos volver a consultar la respectiva documentación si deseamos realizar cualquier modificación en el código.

En resumen…

El fichero index.html

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/>
<script src="calculator.js"></script>
<title>Calculator!</title>
</head>
<body onload="onLoad()">
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Calculator</ion-title>
<ion-buttons slot="secondary">
<ion-button href="index.html?ionic:mode=md">Android</ion-button>
</ion-buttons>
<ion-buttons slot="primary">
<ion-button href="index.html?ionic:mode=ios">IOS</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-label>Tamaño botones</ion-label>
<ion-select value="default" id="size">
<ion-select-option value="small">Pequeños</ion-select-option>
<ion-select-option value="default">Medianos</ion-select-option>
<ion-select-option value="large">Grandes</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label>Tipo botones</ion-label>
<ion-select value="solid" id="type">
<ion-select-option value="clear">Sin borde ni color</ion-select-option>
<ion-select-option value="outline">Sólo borde</ion-select-option>
<ion-select-option value="solid">Con color de fondo</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
<ion-grid>
<ion-row>
<ion-col><ion-button color="dark"><span id="result">0</span></ion-button></ion-col>
</ion-row>
<ion-row>
<ion-col><ion-button color="primary" onclick="add('1')">1</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('2')">2</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('3')">3</ion-button></ion-col>
<ion-col><ion-button color="tertiary" onclick="add('+')">+</ion-button></ion-col>
</ion-row>
<ion-row>
<ion-col><ion-button color="primary" onclick="add('4')">4</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('5')">5</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('6')">6</ion-button></ion-col>
<ion-col><ion-button color="tertiary" onclick="add('-')">-</ion-button></ion-col>
</ion-row>
<ion-row>
<ion-col><ion-button color="primary" onclick="add('7')">7</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('8')">8</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('9')">9</ion-button></ion-col>
<ion-col><ion-button color="tertiary" onclick="add('*')">*</ion-button></ion-col>
</ion-row>
<ion-row>
<ion-col size="9"><ion-button color="primary" onclick="add('0')">0</ion-button></ion-col>
<ion-col><ion-button color="tertiary" onclick="add('/')">/</ion-button></ion-col>
</ion-row>
<ion-row>
<ion-col size="6"><ion-button color="danger" onclick="del()">AC</ion-button></ion-col>
<ion-col><ion-button color="primary" onclick="add('.')">.</ion-button></ion-col>
<ion-col><ion-button color="tertiary" onclick="calc()">=</ion-button></ion-col>
</ion-row>
</ion-grid>
</ion-content>
</ion-app>
</body>
</html>
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script> <script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/> <script src="calculator.js"></script> <title>Calculator!</title> </head> <body onload="onLoad()"> <ion-app> <ion-header> <ion-toolbar> <ion-title>Calculator</ion-title> <ion-buttons slot="secondary"> <ion-button href="index.html?ionic:mode=md">Android</ion-button> </ion-buttons> <ion-buttons slot="primary"> <ion-button href="index.html?ionic:mode=ios">IOS</ion-button> </ion-buttons> </ion-toolbar> </ion-header> <ion-content> <ion-list> <ion-item> <ion-label>Tamaño botones</ion-label> <ion-select value="default" id="size"> <ion-select-option value="small">Pequeños</ion-select-option> <ion-select-option value="default">Medianos</ion-select-option> <ion-select-option value="large">Grandes</ion-select-option> </ion-select> </ion-item> <ion-item> <ion-label>Tipo botones</ion-label> <ion-select value="solid" id="type"> <ion-select-option value="clear">Sin borde ni color</ion-select-option> <ion-select-option value="outline">Sólo borde</ion-select-option> <ion-select-option value="solid">Con color de fondo</ion-select-option> </ion-select> </ion-item> </ion-list> <ion-grid> <ion-row> <ion-col><ion-button color="dark"><span id="result">0</span></ion-button></ion-col> </ion-row> <ion-row> <ion-col><ion-button color="primary" onclick="add('1')">1</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('2')">2</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('3')">3</ion-button></ion-col> <ion-col><ion-button color="tertiary" onclick="add('+')">+</ion-button></ion-col> </ion-row> <ion-row> <ion-col><ion-button color="primary" onclick="add('4')">4</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('5')">5</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('6')">6</ion-button></ion-col> <ion-col><ion-button color="tertiary" onclick="add('-')">-</ion-button></ion-col> </ion-row> <ion-row> <ion-col><ion-button color="primary" onclick="add('7')">7</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('8')">8</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('9')">9</ion-button></ion-col> <ion-col><ion-button color="tertiary" onclick="add('*')">*</ion-button></ion-col> </ion-row> <ion-row> <ion-col size="9"><ion-button color="primary" onclick="add('0')">0</ion-button></ion-col> <ion-col><ion-button color="tertiary" onclick="add('/')">/</ion-button></ion-col> </ion-row> <ion-row> <ion-col size="6"><ion-button color="danger" onclick="del()">AC</ion-button></ion-col> <ion-col><ion-button color="primary" onclick="add('.')">.</ion-button></ion-col> <ion-col><ion-button color="tertiary" onclick="calc()">=</ion-button></ion-col> </ion-row> </ion-grid> </ion-content> </ion-app> </body> </html>
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

  <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.esm.js"></script>
  <script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/dist/ionic/ionic.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@4.9.1/css/ionic.bundle.css"/>

  <script src="calculator.js"></script>

  <title>Calculator!</title>
</head>

<body onload="onLoad()">
  <ion-app>
    <ion-header>
      <ion-toolbar>
        <ion-title>Calculator</ion-title>
        <ion-buttons slot="secondary">
          <ion-button href="index.html?ionic:mode=md">Android</ion-button>
        </ion-buttons>
        <ion-buttons slot="primary">
          <ion-button href="index.html?ionic:mode=ios">IOS</ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>

    <ion-content>
      <ion-list>
        <ion-item>
          <ion-label>Tamaño botones</ion-label>
          <ion-select value="default" id="size">
            <ion-select-option value="small">Pequeños</ion-select-option>
            <ion-select-option value="default">Medianos</ion-select-option>
            <ion-select-option value="large">Grandes</ion-select-option>
          </ion-select>
        </ion-item>
        <ion-item>
          <ion-label>Tipo botones</ion-label>
          <ion-select value="solid" id="type">
            <ion-select-option value="clear">Sin borde ni color</ion-select-option>
            <ion-select-option value="outline">Sólo borde</ion-select-option>
            <ion-select-option value="solid">Con color de fondo</ion-select-option>
          </ion-select>
        </ion-item>
      </ion-list>
      <ion-grid>
        <ion-row>
          <ion-col><ion-button color="dark"><span id="result">0</span></ion-button></ion-col>
        </ion-row>
        <ion-row>
          <ion-col><ion-button color="primary" onclick="add('1')">1</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('2')">2</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('3')">3</ion-button></ion-col>
          <ion-col><ion-button color="tertiary" onclick="add('+')">+</ion-button></ion-col>
        </ion-row>
        <ion-row>
          <ion-col><ion-button color="primary" onclick="add('4')">4</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('5')">5</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('6')">6</ion-button></ion-col>
          <ion-col><ion-button color="tertiary" onclick="add('-')">-</ion-button></ion-col>
        </ion-row>
        <ion-row>
          <ion-col><ion-button color="primary" onclick="add('7')">7</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('8')">8</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('9')">9</ion-button></ion-col>
          <ion-col><ion-button color="tertiary" onclick="add('*')">*</ion-button></ion-col>
        </ion-row>
        <ion-row>
          <ion-col size="9"><ion-button color="primary" onclick="add('0')">0</ion-button></ion-col>
          <ion-col><ion-button color="tertiary" onclick="add('/')">/</ion-button></ion-col>
        </ion-row>
        <ion-row>
          <ion-col size="6"><ion-button color="danger" onclick="del()">AC</ion-button></ion-col>
          <ion-col><ion-button color="primary" onclick="add('.')">.</ion-button></ion-col>
          <ion-col><ion-button color="tertiary" onclick="calc()">=</ion-button></ion-col>
        </ion-row>
      </ion-grid>
    </ion-content>
  </ion-app>
</body>
</html>

El fichero calculator.js

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function onLoad() {
document.addEventListener("ionChange", setStyle);
setStyle();
}
function setStyle() {
document.querySelectorAll("ion-content ion-button").forEach(function(b) {
b.expand = "block";
b.strong = "true";
b.fill = document.getElementById("type").value;
b.size = document.getElementById("size").value;
});
}
function setResult(value) {
document.getElementById("result").innerHTML = value;
}
function getResult() {
return(document.getElementById("result").innerHTML);
}
function add(key) {
var result = getResult();
if (result!="0" || isNaN(key)) setResult(result + key);
else setResult(key);
}
function calc() {
var result = eval(getResult());
setResult(result);
}
function del() {
setResult(0);
}
function onLoad() { document.addEventListener("ionChange", setStyle); setStyle(); } function setStyle() { document.querySelectorAll("ion-content ion-button").forEach(function(b) { b.expand = "block"; b.strong = "true"; b.fill = document.getElementById("type").value; b.size = document.getElementById("size").value; }); } function setResult(value) { document.getElementById("result").innerHTML = value; } function getResult() { return(document.getElementById("result").innerHTML); } function add(key) { var result = getResult(); if (result!="0" || isNaN(key)) setResult(result + key); else setResult(key); } function calc() { var result = eval(getResult()); setResult(result); } function del() { setResult(0); }
function onLoad() {
    document.addEventListener("ionChange", setStyle);
    setStyle();
}

function setStyle() {
    document.querySelectorAll("ion-content ion-button").forEach(function(b) {
        b.expand = "block";
        b.strong = "true";
        b.fill = document.getElementById("type").value;
        b.size = document.getElementById("size").value;
    });
}

function setResult(value) {
    document.getElementById("result").innerHTML = value;
}

function getResult() {
    return(document.getElementById("result").innerHTML);
}

function add(key) {
    var result = getResult();
    if (result!="0" || isNaN(key)) setResult(result + key);
    else setResult(key);
}

function calc() {
    var result = eval(getResult());
    setResult(result);
}

function del() {
    setResult(0);
}

El resultado

Puedes hacer clic aquí para observar el aspecto que tiene la calculadora y probar la funcionalidad resultante utilizando el código especificado.

Calculadora básica con HTML+CSS+Javascript

Esqueleto HTML

La estructura básica de nuestro fichero principal será la siguiente:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="calculator.css" rel="stylesheet">
<script src="calculator.js"></script>
<title>Calculator!</title>
</head>
<body>
...
</body>
</html>
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="calculator.css" rel="stylesheet"> <script src="calculator.js"></script> <title>Calculator!</title> </head> <body> ... </body> </html>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link href="calculator.css" rel="stylesheet">
    <script src="calculator.js"></script>
    <title>Calculator!</title>
  </head>
  <body>

  ...

  </body>
</html>

HTML5 doctype

La primera línea que aparece en el archivo ( <!doctype html> ) no es tanto una etiqueta HTML, sino una declaración del lenguaje y la versión que vamos a utilizar (HTML 5). Además, como no se trata de una etiqueta, no necesitamos cerrar la declaración:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
...
</html>
<!doctype html> <html lang="en"> ... </html>
<!doctype html>
<html lang="en">
  ...
</html>

Etiqueta para obtener una visualización responsive

El viewport es el área de una página web que visualiza un usuario. Puede variar dependiendo del dispositivo, y será menor por ejemplo en un teléfono móvil que en la pantalla de un ordenador.

Antes de que aparecieran las tabletas y los teléfonos móviles, las páginas web se diseñaban sólo para pantallas de ordenadores, y era muy común que tuvieran un diseño estático y un tamaño fijo.

Cuando comenzamos a utilizar los navegadores de los dispositivos móviles, las páginas de tamaño fijo eran demasiado grandes para ajustarse al viewport. Para solucionar esto, los navegadores modernos reducían el tamaño de todos los elementos de la página web para ajustarse a la pantalla, con lo que resultaba muy difícil leer el contenido de la misma.

Un elemento  <meta> de tipo viewport le dice al navegador cómo controlar las dimensiones de la página para hacer zoom automáticamente de forma adecuada, utilizando por ejemplo un mayor tamaño de letra y habilitando a su vez scroll vertical para poder acceder a todo el contenido.

En resumen, para asegurar una correcta visualización, tanto en dispositivos móviles como en ordenadores de escritorio, debemos añadir una etiqueta <meta name="viewport"> para especificar el tamaño y la escala del contenido de la pantalla:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">

Más concretamente, con la cadena especificada dentro del atributo content del ejemplo, estaríamos diciéndole al WebView (el navegador interno del dispositivo) que aproveche al máximo el área visible de la pantalla (width=device-width). Y puesto que la visualización debería ser óptima, el usuario no necesitará realizar zoom (initial-scale=1).

Puedes encontrar más información aquí. Además, también puedes consultar algunas recomendaciones al respecto de los desarrolladores de Ionic, o de Safari o de Firefox. Además, debido a la constante actualización en el mercado de dispositivos móviles puede resultar necesario realizar algún ajuste, como por ejemplo, para obtener una mejor visualización en el iPhone X. También es interesante observar la cadena de configuración utilizada por  algunas librerías, como por ejemplo Bootstrap.

Colocando los botones en una tabla

Para distribuir los botones de la calculadora en varias filas y columnas utilizaremos una tabla. Por ejemplo, para obtener la siguiente distribución:

0
123+
456
789*
0/
AC.=

Podemos utilizar el siguiente código:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<table>
<tr>
<td colspan="4">0</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>+</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
<td>-</td>
</tr>
<tr>
<td>7</td>
<td>8</td>
<td>9</td>
<td>*</td>
</tr>
<tr>
<td colspan="3">0</td>
<td>/</td>
</tr>
<tr>
<td colspan="2">AC</td>
<td>.</td>
<td>=</td>
</tr>
</table>
<table> <tr> <td colspan="4">0</td> </tr> <tr> <td>1</td> <td>2</td> <td>3</td> <td>+</td> </tr> <tr> <td>4</td> <td>5</td> <td>6</td> <td>-</td> </tr> <tr> <td>7</td> <td>8</td> <td>9</td> <td>*</td> </tr> <tr> <td colspan="3">0</td> <td>/</td> </tr> <tr> <td colspan="2">AC</td> <td>.</td> <td>=</td> </tr> </table>
<table>
  <tr>
    <td colspan="4">0</td>
  </tr>
  <tr>
    <td>1</td>
    <td>2</td>
    <td>3</td>
    <td>+</td>
  </tr>
  <tr>
    <td>4</td>
    <td>5</td>
    <td>6</td>
    <td>-</td>
  </tr>
  <tr>
    <td>7</td>
    <td>8</td>
    <td>9</td>
    <td>*</td>
  </tr>
  <tr>
    <td colspan="3">0</td>
    <td>/</td>          
  </tr>
  <tr>
    <td colspan="2">AC</td>
    <td>.</td>
    <td>=</td>            
  </tr>
</table>

Como se puede observar, se utiliza el elemento <tr></tr> (table row) para establecer las filas de la tabla, y el elemento <td></td> (table data) para delimitar las celdas de cada fila.

Añadimos el atributo colspan="2" y colspan="3" para hacer que una determinada celda abarque 2 y 3 columnas respectivamente, y por lo tanto, el botón que contiene sea más grande que el resto.

Botones básicos para capturar las pulsaciones

Utilizaremos el elemento

<button></button>
<button></button> para implementar las teclas de la calculadora, y los colocaremos dentro de cada celda de la tabla. Por ejemplo:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<tr>
<td><button onclick="...">1</button></td>
<td><button onclick="...">2</button></td>
<td><button onclick="...">3</button></td>
<td><button onclick="...">+</button></td>
</tr>
<tr> <td><button onclick="...">1</button></td> <td><button onclick="...">2</button></td> <td><button onclick="...">3</button></td> <td><button onclick="...">+</button></td> </tr>
<tr>
  <td><button onclick="...">1</button></td>
  <td><button onclick="...">2</button></td>
  <td><button onclick="...">3</button></td>
  <td><button onclick="...">+</button></td>
</tr>

El atributo onclick nos servirá para ejecutar una función de JavaScript cada vez que el usuario pulse ese botón. Lo veremos con detalle un poco más adelante.

Utilizando archivos CSS y JavaScript externos

Para poder cambiar el aspecto de la calculadora, utilizaremos un fichero externo que contendrá algunas líneas de código CSS en un fichero externo, que incluiremos en el archivo principal (index.html).

Además, también utilizaremos diversas funciones y código JavaScript que también ubicaremos en un archivo externo.

Para incluir ambos archivos en el código HTML principal y poder utilizar el código que hayamos colocado allí, utilizaremos las siguientes etiquetas:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<link href="calculator.css" rel="stylesheet">
<script src="calculator.js"></script>
<link href="calculator.css" rel="stylesheet"> <script src="calculator.js"></script>
<link href="calculator.css" rel="stylesheet">
<script src="calculator.js"></script>

Modificando el aspecto de los botones

Para conseguir que los botones sean un poco más grandes y se puedan pulsar más fácilmente, añadiremos algunas propiedades CSS al archivo calculator.css:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
table {
width: 100%;
}
button {
width: 100%;
font-size: 150%;
}
.ac {
color: red;
}
table { width: 100%; } button { width: 100%; font-size: 150%; } .ac { color: red; }
table {
  width: 100%;
}
button {
  width: 100%;
  font-size: 150%;
}
.ac {
  color: red;
}

Mediante la propiedad width: 100%; haremos que la tabla ocupe todo el ancho de la pantalla, y que los botones ocupen todo el ancho de cada celda de la tabla.

Con la modificación font-size: 150%; incrementaremos el tamaño del texto que pongamos dentro de los botones.

Mediante  la definición de una clase, conseguiremos aplicar un estilo concreto (color del texto en rojo) al botón AC (All Clear):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
.ac {
color: red;
}
.ac { color: red; }
.ac {
  color: red;
}

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<button class="ac">AC</button>
<button class="ac">AC</button>
<button class="ac">AC</button>

Añadiendo la funcionalidad para realizar los cálculos

El código HTML ya incluye todos los elementos básicos para interactuar con el usuario, pero todavía no disponemos del código JavaScript necesario para capturar las pulsaciones de cada tecla, actualizar la pantalla de la calculadora y calcular el resultado.

Proponemos utilizar cinco funciones, cuyo propósito justificamos debajo del cuadro:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function setResult(value) {
document.getElementById('result').innerHTML = value;
}
function getResult() {
return(document.getElementById('result').innerHTML);
}
function add(key) {
var result = getResult();
if (result!='0' || isNaN(key)) setResult(result + key);
else setResult(key);
}
function calc() {
var result = eval(getResult());
setResult(result);
}
function del() {
setResult(0);
}
function setResult(value) { document.getElementById('result').innerHTML = value; } function getResult() { return(document.getElementById('result').innerHTML); } function add(key) { var result = getResult(); if (result!='0' || isNaN(key)) setResult(result + key); else setResult(key); } function calc() { var result = eval(getResult()); setResult(result); } function del() { setResult(0); }
function setResult(value) {
    document.getElementById('result').innerHTML = value;
}

function getResult() {
    return(document.getElementById('result').innerHTML);
}

function add(key) { 
    var result = getResult();
    if (result!='0' || isNaN(key)) setResult(result + key);
    else setResult(key);
}

function calc() {
    var result = eval(getResult()); 
    setResult(result);
}

function del() { 
    setResult(0);
}
  • setResult(): Actualiza la pantalla de la calculadora poniendo el valor que se pase como parámetro.
  • getResult(): Recoge el valor del último resultado obtenido, o de la expresión matemática que se debe calcular, y que se está visualizando en la pantalla de la calculadora.
  • add(): Añade a la pantalla la tecla pulsada (por ejemplo, el dígito o la operación a realizar). Si la pantalla ya contiene algún dato o la tecla que se pulsa no es un dígito, el carácter de la tecla pulsada se añadirá a la pantalla. En caso contrario (por ejemplo, si la pantalla está a cero, y se pulsa otro dígito), el contenido de la pantalla se reemplazará con la tecla pulsada.
  • calc(): Realiza el cálculo de la expresión que se encuentre en la pantalla (utilizando la función eval()), y escribe el resultado.
  • del(): Pone a cero el contenido de la pantalla de la calculadora.

Las tres últimas funciones de la lista se ejecutarán desde el código HTML utilizando el atributo onclick:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<button onclick="add('1')">1</button>
...
<button onclick="calc()">=</button>
...
<button onclick="del()">AC</button>
<button onclick="add('1')">1</button> ... <button onclick="calc()">=</button> ... <button onclick="del()">AC</button>
<button onclick="add('1')">1</button>
...
<button onclick="calc()">=</button>
...
<button onclick="del()">AC</button>

En resumen…

El fichero index.html

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="calculator.css" rel="stylesheet">
<script src="calculator.js"></script>
<title>Calculator!</title>
</head>
<body>
<table>
<tr>
<td colspan="4"><button id="result">0</button></td>
</tr>
<tr>
<td><button onclick="add('1')">1</button></td>
<td><button onclick="add('2')">2</button></td>
<td><button onclick="add('3')">3</button></td>
<td><button onclick="add('+')">+</button></td>
</tr>
<tr>
<td><button onclick="add('4')">4</button></td>
<td><button onclick="add('5')">5</button></td>
<td><button onclick="add('6')">6</button></td>
<td><button onclick="add('-')">-</button></td>
</tr>
<tr>
<td><button onclick="add('7')">7</button></td>
<td><button onclick="add('8')">8</button></td>
<td><button onclick="add('9')">9</button></td>
<td><button onclick="add('*')">*</button></td>
</tr>
<tr>
<td colspan="3"><button onclick="add('0')">0</button></td>
<td><button onclick="add('/')">/</button></td>
</tr>
<tr>
<td colspan="2"><button onclick="del()" class="ac">AC</button></td>
<td><button onclick="add('.')">.</button></td>
<td><button onclick="calc()">=</button></td>
</tr>
</table>
</body>
</html>
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="calculator.css" rel="stylesheet"> <script src="calculator.js"></script> <title>Calculator!</title> </head> <body> <table> <tr> <td colspan="4"><button id="result">0</button></td> </tr> <tr> <td><button onclick="add('1')">1</button></td> <td><button onclick="add('2')">2</button></td> <td><button onclick="add('3')">3</button></td> <td><button onclick="add('+')">+</button></td> </tr> <tr> <td><button onclick="add('4')">4</button></td> <td><button onclick="add('5')">5</button></td> <td><button onclick="add('6')">6</button></td> <td><button onclick="add('-')">-</button></td> </tr> <tr> <td><button onclick="add('7')">7</button></td> <td><button onclick="add('8')">8</button></td> <td><button onclick="add('9')">9</button></td> <td><button onclick="add('*')">*</button></td> </tr> <tr> <td colspan="3"><button onclick="add('0')">0</button></td> <td><button onclick="add('/')">/</button></td> </tr> <tr> <td colspan="2"><button onclick="del()" class="ac">AC</button></td> <td><button onclick="add('.')">.</button></td> <td><button onclick="calc()">=</button></td> </tr> </table> </body> </html>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link href="calculator.css" rel="stylesheet">
    <script src="calculator.js"></script>    

    <title>Calculator!</title>
  </head>
  <body>
    <table>
      <tr>
        <td colspan="4"><button id="result">0</button></td>
      </tr>
      <tr>
        <td><button onclick="add('1')">1</button></td>
        <td><button onclick="add('2')">2</button></td>
        <td><button onclick="add('3')">3</button></td>
        <td><button onclick="add('+')">+</button></td>
      </tr>
      <tr>
        <td><button onclick="add('4')">4</button></td>
        <td><button onclick="add('5')">5</button></td>
        <td><button onclick="add('6')">6</button></td>
        <td><button onclick="add('-')">-</button></td>
      </tr>
      <tr>
        <td><button onclick="add('7')">7</button></td>
        <td><button onclick="add('8')">8</button></td>
        <td><button onclick="add('9')">9</button></td>
        <td><button onclick="add('*')">*</button></td>
      </tr>
      <tr>
        <td colspan="3"><button onclick="add('0')">0</button></td>
        <td><button onclick="add('/')">/</button></td>          
      </tr>
      <tr>
        <td colspan="2"><button onclick="del()" class="ac">AC</button></td>
        <td><button onclick="add('.')">.</button></td>
        <td><button onclick="calc()">=</button></td>            
      </tr>
    </table>
  </body>
</html>

El fichero calculator.css

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
table {
width: 100%;
}
button {
width: 100%;
font-size: 150%;
}
.ac {
color: red;
}
table { width: 100%; } button { width: 100%; font-size: 150%; } .ac { color: red; }
table {
    width: 100%;
}

button {
    width: 100%;
    font-size: 150%;
}

.ac {
    color: red;
}

El fichero calculator.js

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function setResult(value) {
document.getElementById('result').innerHTML = value;
}
function getResult() {
return(document.getElementById('result').innerHTML);
}
function add(key) {
var result = getResult();
if (result!='0' || isNaN(key)) setResult(result + key);
else setResult(key);
}
function calc() {
var result = eval(getResult());
setResult(result);
}
function del() {
setResult(0);
}
function setResult(value) { document.getElementById('result').innerHTML = value; } function getResult() { return(document.getElementById('result').innerHTML); } function add(key) { var result = getResult(); if (result!='0' || isNaN(key)) setResult(result + key); else setResult(key); } function calc() { var result = eval(getResult()); setResult(result); } function del() { setResult(0); }
function setResult(value) {
    document.getElementById('result').innerHTML = value;
}

function getResult() {
    return(document.getElementById('result').innerHTML);
}

function add(key) { 
    var result = getResult();
    if (result!='0' || isNaN(key)) setResult(result + key);
    else setResult(key);
}

function calc() {
    var result = eval(getResult()); 
    setResult(result);
}

function del() { 
    setResult(0);
}

El resultado

Puedes hacer clic aquí para observar el aspecto que tiene la calculadora y probar la funcionalidad resultante utilizando el código especificado.

IONIC 4 ya está aquí

¿Por qué IONIC?

Ya desde la primera versión vinieron pisando fuerte, y apostaron por utilizar Angular, que ya había demostrado ser un framework muy recomendable, ya que proporcionaba una estabilidad, modularidad y escalabilidad únicas en el código generado. Gracias a ese alto potencial, los creadores de IONIC predijeron que esa fusión permitiría el desarrollo de aplicaciones híbridas que podrían llegar a competir incluso con las nativas (https://ionicframework.com/present-ionic/slides).

Angular hace posible que podamos desarrollar aplicaciones complejas con tecnología web. Esto se consigue gracias a que permite al programador crear nuevas etiquetas HTML, y componentes específicos incrustados en una arquitectura vista-controlador, muy adecuada en el desarrollo de aplicaciones para dispositivos móviles.

Además, desde un principio se ha mantenido la filosofía de no obligar al programador a adquirir conocimientos complejos ni específicos de cada plataforma, ni a tener que desarrollar un código diferente para acceder al hardware de los distintos dispositivos móviles:

  • Por un lado, se utiliza Angular para aprovechar las habilidades de muchos desarrolladores web que ya conocen los conceptos de componentes, directivas y servicios, que además son completamente compatibles con cualquier navegador, y permiten por lo tanto utilizar un sólo código fuente compatible con dispositivos muy diferentes.
  • Por otro lado, se utiliza Cordova para acceder al hardware de los dispositivos, permitiendo interactuar con el mismo utilizando código Javascript.

IONIC destacó principalmente porque aportaba una interfaz de usuario muy atractiva que combinada con Angular y Cordova, proporcionaba al programador un entorno único en su época para el desarrollo de aplicaciones híbridas:

Fuimos muchos los que descubrimos y utilizamos esas primeras versiones para desarrollar aplicaciones (https://showcase.ionicframework.com/apps/archive), y prueba de ello fue que la Play Store y la App Store comenzaron a recibir una gran cantidad de aplicaciones híbridas, que además era rápidas y estables, unas características que hace años sólo podían encontrarse en aplicaciones nativas.

Sin embargo, esas primeras versiones obligaban a los desarrolladores de IONIC a ir adaptando su código a medida que Angular iba evolucionando. Aunque eso ya ha cambiado…

¿Por qué IONIC 4?

Después de dos años de trabajo desde que nos deleitaran con la versión 3, el pasado 23 de enero, los desarrolladores de IONIC pusieron a nuestra disposición la versión 4, por lo que somos muy afortunados de poder disfrutar actualmente de todo su potencial. Además, ya podemos consultar la documentación actualizada (https://ionicframework.com/docs).

Los cambios respecto a la versión 3 son muy significativos (https://blog.ionicframework.com/introducing-ionic-4-ionic-for-everyone) y desde luego no dejan lugar a dudas para decantarnos por esta última versión de IONIC para desarrollar nuestros proyectos web, o aplicación móviles híbridas.

El objetivo original de IONIC era desarrollar una librería compatible con cualquier tecnología web, y que pudiera incluirse en cualquier proyecto independientemente de la librería o framework que ya estuviera utilizando el programador. Y esto no era posible, hasta ahora…

En la práctica, los últimos cambios hechos en la arquitectura de la librería, permiten que los componentes de IONIC se puedan utilizar con la misma simpleza que cualquier otra etiqueta HTML, pero nos proporcionan una funcionalidad ampliada. Por ejemplo, podríamos hacer uso de la etiqueta  

<ion-button></ion-button>
<ion-button></ion-button> en cualquier framework, e incluso de manera independiente (sin ninguna arquitectura o código base específico), utilizando la sencilla sintaxis de Javascript. En esta última versión, el navegador puede reconocer los nuevos elementos HTML sin realizar ningún tipo de modificación en nuestro código.

Ya que tenemos el lujo de poder utilizar IONIC 4, no vamos a dejar escapar la posibilidad de disfrutar de la sencillez, facilidad de desarrollo y versatilidad que nos proporciona esta última versión, tal como apreciaremos en los próximos ejercicios.