Skip to content

Latest commit

 

History

History
207 lines (165 loc) · 21.9 KB

File metadata and controls

207 lines (165 loc) · 21.9 KB

Eventos en JavaScript

El objetivo de esta práctica es conocer el sistema de eventos que ofrecen los navegadores web y que son accesibles para los programas en JavaScript.

El uso de eventos nos permitirá dotar de interactividad a la aplicación que estamos desarrollando: el usuario podrá interactuar con dicha aplicación para introducir datos y realizar modificaciones en su estado.

Repositorio de la práctica

El repositorio base de la práctica está disponible en: https://github.com/pedroprieto/practica_dwec_gestor_presupuesto

Se supone que ya está configurado el repositorio personal y el remoto secundario (profesor). Si no es así, revisa las instrucciones de las prácticas anteriores. En el apartado de Preparación se indica cómo proceder.

Requisitos de software

Para poder realizar esta práctica y que funcione adecuadamente el entorno de test será necesario instalar el siguiente software en el equipo:

Desarrollo guiado por test

Para realizar la práctica se seguirán los principios del Desarrollo Guiado por Test. Para ello se proporciona en el repositorio una serie de ficheros que permiten ejecutar tests. Dichos tests comprobarán que el programa cumple con algunos de los requisitos de la práctica.

El repositorio se ha configurado para que se ejecuten los tests automáticamente en la nube de GitHub cuando se realice un push o una pull request. Para ello se hará uso del servicio GitHub Actions.

Preparación

  1. Instalar los requisitos de software indicados
  2. Abrir un terminal
  3. Situarse en la carpeta del repositorio personal de la práctica
  4. Incorporar a tu repositorio personal los cambios realizados por el profesor correspondientes a los archivos de esta práctica. Para ello hay que ejecutar:
    git pull profesor master
        
  5. Este comando descarga los cambios que ha realizado el profesor en el repositorio base y los integra en tu repositorio personal. Tras realizar este paso, seguramente git abra el editor configurado por defecto para que introduzcas un mensaje para crear un nuevo commit que integre tus cambios y los cambios del profesor. Debes introducir el texto y guardar los cambios.
  6. En principio no deben producirse conflictos. En caso de que se produzcan (por ejemplo, porque has editado el fichero .gitignore y yo también porque lo exigía la práctica), resuélvelos y notifícamelo a través de un Issue.
  7. Ejecuta el comando git push para subir los cambios a tu repositorio personal (el remoto principal) en GitHub y que queden guardados ahí también.
  8. Ejecutar el comando npm install. Este comando instalará todas las librerías de Node necesarias para ejecutar los tests. Dichas librerías se guardarán en una carpeta con nombre node_modules dentro del repositorio. Nótese que dicha carpeta está excluida del repositorio en el archivo .gitignore.
  9. Ejecutar el comando npm run test5 para lanzar los tests de esta práctica. Este comando podrá ejecutarse tantas veces como sea necesario. Por pantalla se mostrarán los tests que se pasan y los que no, de manera que se tendrá información sobre las acciones que hay que realizar. Los tests también se ejecutarán automáticamente en GitHub Actions al subir los cambios al repositorio y al realizar la pull request.
  10. Opcionalmente (recomendable), ejecutar el comando npm test para lanzar todos los tests presentes en el repositorio. Se deberá comprobar que se pasan los tests de las prácticas anteriores a la que se esté realizando. Lógicamente, si el repositorio incluye los tests de prácticas posteriores a la que se esté realizando, dichos tests no se pasarán (ya que el trabajo está todavía por hacer). Este último caso puede darse si la persona no está realizando la práctica propuesta en la semana actual (va con “retraso”, por así decirlo). En GitHub Actions se ejecutarán todos los tests en tareas independientes: así se podrá comprobar si el test de la práctica que se está realizando se ha pasado.

    ¡IMPORTANTE! Esta práctica utiliza la suite de test Cypress. Puedes ejecutar el test en consola de la manera habitual (npm run test5) o bien ejecutar el modo gráfico mediante el comando npx cypress open. Recuerda ejecutar npm install antes para instalar el paquete cypress.

    Recuerda que puedes utilizar el navegador para visualizar el trabajo que vas haciendo. Como la carga de scripts la realizamos a través de módulos, no basta con hacer doble clic en el archivo HTML, sino que es necesario visualizar la página desde un servidor. Para ello puedes utilizar la extensión Live Server de Vísual Studio Code.

Tareas a realizar

Lectura

Lee atentamente los siguientes artículos y sus correspondientes subsecciones en caso de que las tengan:

Ficheros de la aplicación

Vamos a utilizar los ficheros de la práctica anterior realizando modificaciones sobre el archivo js/gestionPresupuestoWeb.js.

Utilizaremos de nuevo el fichero interaccionHTML.html para mostrar los datos e interactuar con la aplicación a través del navegador.

La aplicación funcionará de la siguiente manera:

  • El usuario abrirá el archivo interaccionHTML.html en el navegador (a través de un servidor web, tal como se ha comentado en la sección de Preparación).
  • El archivo interaccionHTML.html cargará el programa js/generarDatosEstaticos.js. Dicho programa hará uso de dos programas (que se utilizarán como librerías):
    • Librería js/gestionPresupuestoWeb.js, que definirá una serie de funciones para interactuar con el DOM de la página y mostrar los datos en HTML. En esta práctica realizaremos modificaciones en este fichero para añadir soporte de eventos.
    • Librería js/gestionPresupuesto.js, que contiene la lógica de negocio de la aplicación (funciones para crear, editar, borrar y mostrar gastos).

El archivo js/generarDatosEstaticos.js se utilizará para crear unos gastos iniciales para poder hacer pruebas durante el desarrollo (para que no aparezca la aplicación vacía). Por tanto, en una aplicación en producción no sería necesario: el archivo HTML funcionaría cargando el archivo /js/gestionPresupuestoWeb.js directamente.

Fichero interaccionHTML.html

Deberemos añadir el siguiente código HTML como primer hijo de la capa <div id="aplicacion">:

<div id="controlesprincipales">
  <button type="button" id="actualizarpresupuesto">
    Actualizar presupuesto
  </button>
  <button type="button" id="anyadirgasto">
    Añadir gasto
  </button>
</div>

Fichero js/gestionPresupuestoWeb.js

Vamos a hacer una serie de modificaciones sobre este fichero.

Importar librería js/gestionPresupuesto.js

El fichero js/gestionPresupuestoWeb.js tiene que tener acceso a las funciones de lógica de negocio de la aplicación, ya que crearemos funciones que procesen las acciones y los datos que realice e introduzca el usuario.

Realizaremos la importación mediante import * as para utilizar un nombre de módulo que agrupe las funciones exportadas por dicho fichero.

Crear una función repintar para actualizar la página

Estamos desarrollando una aplicación JavaScript controlada por datos. Cada vez que se añade, modifica o borra un gasto, debemos mostrar el resultado en la página HTML. Recordemos que la aplicación debe mostrar:

  • El presupuesto
  • El total de gastos
  • El balance actual
  • El listado con los gastos y sus datos
  • Otra información (agrupaciones de gastos, etc.)

Las funciones de lógica de negocio (fichero js/gestionPresupuesto.js) guardan la información en objetos independientes del navegador: si cambiamos el valor de un gasto, por ejemplo, el contenido que se muestra en la página HTML no se actualiza a no ser que lo hagamos explícitamente.

Por tanto, es necesario disponer de una función que vuelva a crear toda la estructura HTML que refleje los cambios realizados en el modelo de datos. Esta función se denominará repintar, y realizará las siguientes tareas:

  • Mostrar el presupuesto en div#presupuesto (funciones mostrarPresupuesto y mostrarDatoEnId)
  • Mostrar los gastos totales en div#gastos-totales (funciones calcularTotalGastos y mostrarDatoEnId)
  • Mostrar el balance total en div#balance-total (funciones calcularBalance y mostrarDatoEnId)
  • Borrar el contenido de div#listado-gastos-completo, para que el paso siguiente no duplique la información. Puedes utilizar innerHTML para borrar el contenido de dicha capa.
  • Mostrar el listado completo de gastos en div#listado-gastos-completo (funciones listarGastos y mostrarGastoWeb)

La función repintar no actualizará el resto de capas (filtrados y agrupaciones) de la práctica anterior (lo haremos así por simplicidad).

Función actualizarPresupuestoWeb y botón actualizarpresupuesto

Esta función se utilizará como manejadora de eventos del botón actualizarpresupuesto del código HTML. Realizará las siguientes tareas:

  • Pedir al usuario que introduzca un presupuesto mediante un prompt.
  • Convertir el valor a número (recuerda que prompt siempre devuelve un string).
  • Actualicar el presupuesto (función actualizarPresupuesto)
  • Llamar a la función repintar para que se muestre la información actualizada en el archivo HTML. Recuerda que actualizar el presupuesto provoca cambios en el balance, por lo que al ejecutar repintar se actualizarán ambos campos.

Una vez definida la función, se añadirá como manejadora del evento click del botón actualizarpresupuesto mediante addEventListener. Para ello habrá que obtener el elemento botón correspondiente previamente.

Función nuevoGastoWeb y botón anyadirgasto

Esta función se utilizará como manejadora de eventos del botón anyadirgasto del código HTML. Realizará las siguientes tareas:

  • Pedir al usuario la información necesaria para crear un nuevo gasto mediante sucesivas preguntas con prompt (por orden: descripción, valor, fecha y etiquetas). Por simplicidad, de momento no se comprobará la validez de dichos datos. La fecha vendrá dada en formato internacional (yyyy-mm-dd) y las etiquetas se introducirán en un único cuadro de texto como una lista separada por comas (por ejemplo, etiqueta1,etiqueta2,etiqueta3).
  • Convertir el valor a número (recuerda que prompt siempre devuelve un string).
  • Convertir la cadena de texto de etiquetas devuelta por prompt a un array.
  • Crear un nuevo gasto (función crearGasto). ¡Ojo con la manera de pasar el parámetro ~etiquetas~!
  • Añadir el gasto a la lista (función anyadirGasto).
  • Llamar a la función repintar para que se muestre la lista con el nuevo gasto.

Una vez definida la función, se añadirá como manejadora del evento click del botón anyadirgasto mediante addEventListener. Para ello habrá que obtener el elemento botón correspondiente previamente.

Función EditarHandle

Esta función se utilizará como objeto manejador de eventos para editar un gasto.

La razón de utilizar esta función es la siguiente: queremos añadir un botón para editar cada gasto de la lista. Por tanto, necesitamos que haya una conexión entre dicho botón y el gasto asociado a él. Recordemos que si tenemos 4 gastos, tendremos 4 botones de Editar.

Hay muchas soluciones para realizar esta asociación: una podría ser añadir un id al botón Editar de dicho gasto que sea el mismo que el id del gasto asociado. Así, si se hace clic en dicho botón, inspeccionando su atributo id podremos saber el gasto al que hace referencia. No obstante, esta solución no es demasiado buena: la lógica de negocio de nuestra aplicación solo almacena el array de gastos, por lo que tendríamos que añadir una función para buscar dicho gasto dado su id.

En lugar de ello optaremos por una solución un poco más elaborada aprovechando que addEventListener nos permite utilizar un objeto como manejador de eventos.

La función EditarHandle será una función constructora que definirá exclusivamente un método llamado handleEvent. Cuando creemos un objeto basado en su prototipo, asignaremos a dicho objeto una propiedad llamada gasto, que será una referencia al gasto que estemos editando. El código de la función handleEvent podrá hacer referencia a dicho gasto a través de this.gasto, ya que es una propiedad del objeto. Esta función realizará las siguientes tareas:

  • Pedir al usuario la información necesaria para editar el gasto mediante sucesivas preguntas con prompt. Por simplicidad, de momento no se comprobará la validez de dichos datos. La fecha vendrá dada en formato internacional (yyyy-mm-dd) y las etiquetas se introducirán en un único cuadro de texto como una lista separada por comas (por ejemplo, etiqueta1,etiqueta2,etiqueta3). Recuerda que prompt admite como segundo parámetro el valor por defecto del cuadro de diálogo, por lo que puedes proporcionar el valor actual de cada propiedad del gasto.
  • Convertir el valor a número (recuerda que prompt siempre devuelve un string).
  • Convertir la cadena de texto de etiquetas devuelta por prompt a un array.
  • Actualizar las propiedades del gasto (disponible mediante this.gasto), mediante las funciones actualizarValor, actualizarDescripcion, actualizarFecha y anyadirEtiquetas.
  • Llamar a la función repintar para que se muestre la lista de gastos con los datos actualizados de la edición.

Función BorrarHandle

Esta función se utilizará como objeto manejador de eventos para borrar un gasto.

El funcionamiento de esta función es muy parecido a la anterior, con la excepción de su funcionamiento interno.

La función BorrarHandle será una función constructora que definirá exclusivamente un método llamado handleEvent. Cuando creemos un objeto basado en su prototipo, asignaremos a dicho objeto una propiedad llamada gasto, que será una referencia al gasto que estemos editando. El código de la función handleEvent podrá hacer referencia a dicho gasto a través de this.gasto, ya que es una propiedad del objeto. Esta función realizará las siguientes tareas:

  • Borrar el gasto asociado. Para ello utilizará la función borrarGasto y como parámetro utilizará el id del gasto seleccionado, disponible en this.gasto.
  • Llamar a la función repintar para que se muestre la lista actualizada de gastos.

Función BorrarEtiquetasHandle

Esta función se utilizará como objeto manejador de eventos para borrar etiquetas de un gasto.

El funcionamiento de esta función es muy parecido a la anterior, con la excepción de su funcionamiento interno.

La función BorrarEtiquetasHandle será una función constructora que definirá exclusivamente un método llamado handleEvent. Cuando creemos un objeto basado en su prototipo, asignaremos a dicho objeto una propiedad llamada gasto, que será una referencia al gasto que estemos editando y una propiedad llamada etiqueta, que hará referencia a la etiqueta que se pretenda eliminar. El código de la función handleEvent podrá hacer referencia a dicho gasto a través de this.gasto y a this.etiqueta, ya que son propiedades del objeto. Esta función realizará las siguientes tareas:

  • Borrar la etiqueta seleccionada del gasto asociado. Para ello utilizará la función borrarEtiquetas del gasto asociado (this.gasto) y como parámetro utilizará la etiqueta seleccionada, disponible en this.etiqueta.
  • Llamar a la función repintar para que se muestre la lista actualizada de gastos.

Modificación de la función mostrarGastoWeb

Una vez definidos los manejadores de eventos para editar y borrar un gasto y borrar etiquetas, es turno de modificar la función mostrarGastoWeb para que, además de la estructura HTML definida en la práctica anterior, cree dos botones para editar y borrar el gasto y añada los manejadores de eventos necesarios para realizar las acciones de edición y borrado de gastos y borrado de etiquetas.

Estas modificaciones son:

  • Botón editar:
    • Crear un botón con texto Editar de tipo button (<button type="button">) con clase gasto-editar.
    • Crear un nuevo objeto a partir de la función constructora EditarHandle.
    • Establecer la propiedad gasto del objeto creado al objeto gasto (recuerda que el objeto gasto es un parámetro pasado a la función mostrarGastoWeb).
    • Añadir el objeto recién creado como objeto manejador del evento click al botón Editar recién creado.
    • Añadir el botón al DOM a continuación de las etiquetas
  • Botón borrar:
    • Crear un botón con texto Borrar de tipo button (<button type="button">) con clase gasto-borrar.
    • Crear un nuevo objeto a partir de la función constructora BorrarHandle.
    • Establecer la propiedad gasto del objeto creado al objeto gasto (recuerda que el objeto gasto es un parámetro pasado a la función mostrarGastoWeb).
    • Añadir el objeto recién creado como objeto manejador del evento click al botón Borrar recién creado.
    • Añadir el botón al DOM a continuación del botón Editar.
  • Eventos para los span de etiquetas (no crearemos botón de borrar: el borrado se producirá si el usuario hace clic encima de una etiqueta):
    • Crear un nuevo objeto a partir de la función constructora BorrarEtiquetasHandle.
    • Establecer la propiedad gasto del objeto creado al objeto gasto (recuerda que el objeto gasto es un parámetro pasado a la función mostrarGastoWeb).
    • Establecer la propiedad etiqueta del objeto creado al texto de la etiqueta que se esté procesando (seguramente este valor lo tendrás disponible dentro del bucle que se encarga de pintar un elemento span para cada etiqueta).
    • Añadir el objeto recién creado como objeto manejador del evento click al span de la etiqueta.

La estructura HTML final que debe quedar para cada gasto es la siguiente:

<div class="gasto">
  <div class="gasto-descripcion">DESCRIPCIÓN DEL GASTO</div>
  <div class="gasto-fecha">FECHA DEL GASTO</div> 
  <div class="gasto-valor">VALOR DEL GASTO</div> 
  <div class="gasto-etiquetas">
    <!-- Este elemento span tendrá un manejador de eventos -->
    <span class="gasto-etiquetas-etiqueta">
      ETIQUETA 1
    </span>
    <!-- Este elemento span tendrá un manejador de eventos -->
    <span class="gasto-etiquetas-etiqueta">
      ETIQUETA 2
    </span>
    <!-- Etcétera -->
  </div> 
  <!-- Este botón tendrá un manejador de eventos -->
  <button class="gasto-editar" type="button">Editar</button>
  <!-- Este botón tendrá un manejador de eventos -->
  <button class="gasto-borrar" type="button">Borrar</button>
</div>

Formato de la entrega

  • Cada persona trabajará en su repositorio personal que habrá creado tras realizar el fork del repositorio base.
  • Todos los archivos de la práctica se guardarán en el repositorio y se subirán a GitHub periódicamente. Es conveniente ir subiendo los cambios aunque no sean definitivos. No se admitirán entregas de tareas que tengan un solo commit.
  • Como mínimo se debe realizar un commit por cada elemento de la lista de tareas a realizar (si es que estas exigen crear código, claro está).
  • Para cualquier tipo de duda o consulta se pueden abrir Issues haciendo referencia al profesor mediante el texto @pedroprieto dentro del texto del Issue. Los issues deben crearse en tu repositorio: si no se muestra la pestaña de Issues puedes activarla en los Settings de tu repositorio.
  • Una vez finalizada la tarea se debe realizar una Pull Request al repositorio base indicando tu nombre y apellidos en el mensaje.