Creando una modal con Vue y Typescript

gugadev

gugadev

Posted on January 22, 2019

Creando una modal con Vue y Typescript

Crear una modal con Vue y Typescript

GitHub logo gugadev / vue-modal

Simple modal built on Vue and Typescript.

Simple Modal

Simple modal built on Vue and Typescript.

Properties

  • title: Title of the modal
  • open: Flag that indicates if the modal is or not opened.

Events

  • close: Fired on close click.

Uno de los frameworks JavaScript que más ha crecido este último año ha sido Vue. Este framework, caracterizado por su sencillez y a la vez, su potencia, ha tomado por asalto la comunidad frontend.

No por nada Vue ha superado a React y Angular en popularidad en Github aunque no quiere decir que a nivel de uso por parte de los desarrolladores se mantenga la misma equivalencia. Lo cierto es que Vue es un framework increíble, flexible, potente y lleno de posibilidades. Aprovecho para felicitar a Evan You y a todo el equipo y contribuyentes detrás de Vue. Congratz guys, you're awesome!

Preparación del proyecto

Bien, empecemos. Lo primero que vamos a necesitar es inicializar el proyecto e instalar algunas dependencias. Vamos a dividir las dependencias en dos: dependencias de desarrollo y de funcionamiento.

Las dependencias de desarollo serán básicamente loaders para Typescript y Vue. Estas son:

  • typescript
  • tslint
  • ts-loader
  • vue-loader
  • vue-style-loader
  • vue-template-compiler
  • css-loader
  • style-loader
  • html-webpack-plugin
  • webpack-dev-server
  • webpack
  • webpack-cli

Y las depenencias principales son:

  • vue
  • vue-class-component
  • vue-property-decorator

Ahora que tenemos las dependencias instaladas, procedemos a crear un archivo llamado tsconfig.json, el cual leerá Typescript para tomar en cuenta algunas configuraciones.

{
  "include": [
    "./src/**/*"
  ],
  "compilerOptions": {
    "target": "esnext",
    "lib": ["dom", "esnext"],
    "module": "es2015",
    "moduleResolution": "node",
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  },
  "compileOnSave": false
}
Enter fullscreen mode Exit fullscreen mode

Lo que hacemos es, en teoría, decirle que tenga en cuenta a cualquier archivo dentro de src/, que queremos usar ES Modules y que active el uso de decoradores.

Una vez realizado este paso, lo siguiente es preparar el archivo de configuración de Webpack:

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  context: __dirname,
  entry: './src/index.ts',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  resolve: {
    alias: {
      vue$: 'vue/dist/vue.esm.js'
    },
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: {
          loader: 'ts-loader',
          options: {
            appendTsSuffixTo: [/\.vue$/]
          }
        }
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.vue$/,
        exclude: /node_modules/,
        use: {
          loader: 'vue-loader'
        }
      }
    ]
  },
  devtool: 'sourcemap',
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
}
Enter fullscreen mode Exit fullscreen mode

Utilizaremos html-webpack-plugin para levantar webpack-dev-server. El archivo index.html lo ponemos en la carpeta src de nuestro proyecto. Quedará así:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Simple Modal</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Ahora, seguimos con el entry point de nuestra aplicación. Aquí levantaremos Vue junto con el componente principal.

import Vue from 'vue'
import App from './index.vue'

new Vue({
  el: '#app',
  template: '<App/>',
  render: h => h(App)
})
Enter fullscreen mode Exit fullscreen mode

Ya tenemos todo listo para empezar con la creación de la modal.

Creación de la modal

La modal será sencilla y orientada a componentes. Realizaremos la estructura, mapearemos algunas propiedades y estableceremos los eventos que debe emitir. El contenido de la modal será insertado de acuerdo a lo que necesitemos en cada ocasión.

Lo primero será crear el template:

Nota: recuerda que tanto el template, como style y script va dentro de un archivo .vue.

<template>
  <div class="modal" :class="{ open }">
    <div class="modal-content">
      <header class="modal-header">
        <h3>{{ title }}</h3>
        <span @click="close">&times;</span>
      </header>
      <article class="modal-body">
        <slot name="content"></slot>
      </article>
      <footer class="modal-footer">
        <slot name="actions"></slot>
      </footer>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Como se puede ver a simple vista, es una estructura bastante sencilla. El título de la modal deberá ser proporcionado por
medio de una la propiedad title, además, el sabremos si está abierta o cerrada por medio de la propiedad open.

La siguiente línea:

<span @click="close">&times;</span>
Enter fullscreen mode Exit fullscreen mode

Nos dice que cuando se haga click en la "x", se ejecutará el método close de nuestro componente.

Además, para poder mostrar u ocultar la modal, nos basaremos en esta línea:

<div class="modal" :class="{ open }">
Enter fullscreen mode Exit fullscreen mode

Que nos indica que si la propiedad open es true, entonces se agregará una clase CSS llamada open, la cual mostrará la modal con un efecto transitorio, como se puede apreciar en el código CSS:

<style scoped>
  .modal {
    align-items: flex-start;
    background-color: rgba(0,0,0,.75);
    display: flex;
    height: 100vh;
    justify-content: center;
    opacity: 0;
    position: fixed;
    transition: visibility 250ms, opacity 250ms;
    width: 100%;
    z-index: -1;
  }
  .modal.open {
    opacity: 1;
    visibility: visible;
    z-index: 2;
  }
  .modal.open .modal-content {
    transform: translateY(100px);
  }
  .modal-content {
    background-color: #fff;
    border-radius: 2px;
    box-shadow: 0 8px 16px 0 rgba(0,0,0,.25);
    display: inline-block;
    min-width: 320px;
    max-width: 480px;
    transition: transform 450ms ease;
    width: 100%;
  }
  .modal-header {
    border-bottom: 1px solid #eee;
    padding: 20px;
    position: relative;
    text-align: center;
  }
  .modal-header h3 {
    color: #444;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 14px;
    font-weight: 600;
    text-transform: uppercase;
  }
  .modal-header span {
    cursor: pointer;
    font-weight: bolder;
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
  }
  .modal-body {
    padding: 40px;
  }
  .modal-footer {
    background-color: #f8f8f8;
    border-top: 1px solid #eee;
    display: flex;
    justify-content: flex-end;
    padding: 20px;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Nota: hacemos uso del atributo scoped para determinar que estos estilos serán encapsulados dentro del componente, evitando conflictos con el resto de estilos de la aplicación.

El código CSS anterior simplemente agrega una transición de opacidad a la modal, así mismo, hace que esta se deslice desde arriba hacia abajo, dando un efecto llamativo y suave.

Finalmente, escribimos nuestro componente principal, el que se comunica con el template y tiene las propiedades y métodos que el template necesitará consumir.

<script lang="ts">
  import Vue from 'vue'
  import Component from 'vue-class-component'
  import { Prop, Emit } from 'vue-property-decorator'

  @Component
  export default class Modal extends Vue {
    @Prop({ required: true, type: String }) title: string
    @Prop({ required: true, type: Boolean, default: false }) open

    @Emit('close')
    close(): void {}
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Lo primero que hacemos es importar los decoradores Component, el cual sirve para indicarle a Vue que dicha clase es un componente, Prop que indica que dicha variable es una prop que recibirá el componente y Emit que indica que ese método emitirá un evento hacia el padre.

La propiedad titley open, como dijimos, son requeridas. Así mismo, open será inicializada en false.

El método close, al ser ejecutado emitirá un evento hacia el padre que contenga la modal, notificando que se quiere cerrar la modal.

Utilizando la modal

Para utilizar la modal es bastante sencillo. Basta con incluirla en la lista de componentes y colocarla en el template. Veamos un ejempo.

<template>
  <div class="container" @keypress="catchKey" tabindex="0">
    <Modal :title="modalTitle" :open="modalOpened" @close="closeModal">  
      <template slot="content">
        <blockquote>
          <p>Debido a nuevas políticas de seguridad, a partir de hoy, 22 de Enero del 2019, usted es reponsable de la seguridad de sus archivos. Para saber como reforzar y manejar la seguridad de su cuenta, lea la <a href="#">Documentación.</a></p>
          <caption>TI & Information Security</caption>
        </blockquote>
      </template>
      <template slot="actions">
        <button class="decline">Declinar</button>
        <button class="accept">Aceptar</button>
      </template>
    </Modal>
    <h1>Presiona O para abrir la modal</h1>
  </div>
</template>

<script lang="ts">
  import Vue from 'vue'
  import Component from 'vue-class-component'
  import Modal from './modal.vue'

  @Component({
    components: {
      Modal
    }
  })
  export default class App extends Vue {
    modalTitle = 'Alerta de seguridad'
    modalOpened = false
    MODAL_TRIGGER = 111

    catchKey(e: KeyboardEvent) {
      if (e.keyCode === this.MODAL_TRIGGER) {
        this.modalOpened = true
      }
    }
    closeModal() {
      this.modalOpened = false
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Como vemos, la propiedad title de la modal está ligada con modalTitle y open con modalOpened, de modo que, cuando se presione la tecla O se cambie el estado de modalOpened a true, mostrando la modal.

Fíjate en el método closeModal, es este método el que se ejecutará en cuando se detecte que Modal ha emitido un evento de tipo close, sobre el cual estamos escuchando mediante la línea @close="closeModal".

Resultado


Conclusiones

Como vemos, crear un componente en Vue es realmente sencillo. No nos tomará más de un par de horas de tener un componente relativamente complejo y funcional. Personalmente, creo que todo desarrollador Frontend debería de darle la oportunidad a este genial framework. 😉

💖 💪 🙅 🚩
gugadev
gugadev

Posted on January 22, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Vue Primeiras Impressões
javascript Vue Primeiras Impressões

February 20, 2024

Is this stack over-engineered?
css Is this stack over-engineered?

November 19, 2019

Luka 00 - Intent and Deployment
rust Luka 00 - Intent and Deployment

September 8, 2019