Buscador de Perfiles en Diablo III: Creación de Formulario y Título

Clase 14 de 27Curso Avanzado de Vue.js 2

Llevamos bastante tiempo trasteando con nuestro proyecto, pero apenas estamos avanzando. Hora de empezar a darle forma a nuestro buscador de perfiles de Diablo III.

Lo primero que tenemos que hacer es crear un formulario que busque el usuario según la región en la que estemos. Esto es importante ya que un usuario puede existir en una región, pero no estar en otra. Por lo tanto necesitamos los 2 parámetros:

  • BattleTag o Identificador de usuario, con el formato: NombreCool#1234
  • Región, que puede ser una de las siguientes: 'US', 'EU', 'KR', 'TW'. La región de CN (China) la dejamos fuera en este ejemplo, que tiene una configuración especial, distinta al resto.

Antes de empezar con el formulario vamos a poner un título grande, que le de nombre a nuestra app.

Para ello creamos, dentro de nuestra carpeta vista /Home, dos componentes: HomeForm.vue y HomeTitle.vue. Quedaría así:

📂 /views └──📂 /Home ├── Index.vue ├── HomeForm.vue └── HomeTitle.vue

Empezamos con el título, un componente bastante sencillo.

<template> <div class="home-title text-center"> <h1 class="my-5 font-diablo">Diablo 3 Profile Finder</h1> <p class="lead text-muted">Enter your <em> <a href="https://eu.battle.net/support/es/article/75767" target="_blank" title="Format: YourProfile#1234">battle-tag</a> </em> and select your region to see your profile!</p> <hr class="mt-5"> </div> </template> <script> export default { name: 'HomeTitle' } </script>

Ya estamos haciendo uso de nuestra tipografía DiabloHeavy. Mira la etiqueta <h1> y la clase que le hemos puesto. Dejamos el título centrado con un buen espaciado. Ya podemos incluirlo en nuestra Home y ver que tal queda.

Para ello, simplemente vamos a traer el componente que acabamos de crear y a modificar nuestra vista (corresponde al archivo /views/Home/Index.vue) para dejarla de esta forma:

<template> <div class="home"> <HomeTitle/> </div> </template> <script> import HomeTitle from './HomeTitle' export default { name: 'Home', components: { HomeTitle } } </script>

Tu aplicación se debería ver así:

con-titulo

Para el formulario, vamos al componente /Home/HomeForm.vue. Vamos a crear un formulario con 2 inputs; uno va a ser una caja de texto y el otro será un select.
En la caja de texto el usuario podrá escribir su BattleTag. En el selector, podrá elegir entre una de las regiones que le mostremos.
Necesitaremos también un botón para controlar el envío del formulario. Bootstrap nos proporciona unos buenos componentes para trabajar con formularios, que en el fondo se renderizan en etiquetas HTML válidas. Lo bueno de esto es que al pulsar la tecla Enter de tu teclado se hará el Submit (envío) del formulario. Nos aprovecharemos de esta funcionalidad por defecto que traen los formularios de HTML para mejorar la experiencia del usuario en nuestra app web.
Por último, haciendo uso del estándar de HTML, podemos indicarle a los input de nuestro formulario que son requeridos con el atributo required. Esta será nuestra pequeña (y única) validación para nuestro proyecto. En el caso de un proyecto real, deberías validar tus formularios meticulosamente: formato, tamaño, nº caracteres, etc.

> 📗 Infórmate acerca de los atributos de un <input> de un formulario HTML en este enlace: https://developer.mozilla.org/es/docs/Web/HTML/Elemento/input
> Para la etiqueta <select>, puedes leer esto: https://developer.mozilla.org/es/docs/Web/HTML/Elemento/select

La parte del HTML de nuestro componente HomeForm.vue es la siguiente:

<template> <div class="search-form my-5"> <div class="row"> <div class="col-12 col-md-8 offset-md-2"> <!-- Formulario --> <b-form @submit.prevent="onSubmit"> <!-- Grupo 1 (Input texto) --> <b-form-group id="input-group-1" label="BattleTag:" label-for="input-text" description="Format: YourProfile#1234" > <b-form-input id="input-text" v-model="form.battleTag" type="text" size="lg" required placeholder="BattleTag" /> </b-form-group> <!-- Grupo 2 (Selector de región) --> <b-form-group id="input-group-3" label="Region:" label-for="input-region"> <b-form-select id="input-region" v-model="form.region" size="lg" :options="regions" required /> </b-form-group> <!-- Botón envío --> <div class="d-flex justify-content-end mt-5"> <b-button type="submit" variant="primary" size="lg">Submit</b-button> </div> </b-form> </div> </div> </div> </template>

En la parte de JavaScript de nuestro componente, vamos a necesitar el modelo asociado al formulario, el listado de regiones que queremos pintar en el selector de región y una función que haga algo cuando el formulario se envíe.
Por lo cual, el bloque <script> de nuestro componente quedaría así:

import { regions } from '@/utils/regions' export default { name: 'MainForm', data () { return { form: { battleTag: '', region: 'eu' } } }, computed: { regions () { return regions.map(region => ({ value: region, text: region.toUpperCase() })) } }, methods: { onSubmit () { const { region, battleTag } = this.form this.$router.push({ name: 'Profile', params: { region, battleTag: battleTag.replace('#', '-') } }) } } }

Para las regiones hemos creado una computed property o propiedad computada que nos devuelve un array de objetos con las regiones en minúscula como value y en mayúscula como texto a mostrar. Tenemos que crear un nuevo archivo, de nombre regions.js, en la carpeta /utils para que esto funcione con el siguiente contenido:

const regions = ['us', 'eu', 'kr', 'tw'] const locales = { us: 'en_US', eu: 'en_GB', kr: 'ko_KR', tw: 'zh_TW' } export { regions, locales }

Código completo HomeForm.vue

<template> <div class="search-form my-5"> <div class="row"> <div class="col-12 col-md-8 offset-md-2"> <b-form @submit.prevent="onSubmit"> <b-form-group id="input-group-1" label="BattleTag:" label-for="input-text" description="Format: YourProfile#1234" > <b-form-input id="input-text" v-model="form.battleTag" type="text" size="lg" required placeholder="BattleTag" /> </b-form-group> <b-form-group id="input-group-3" label="Region:" label-for="input-region"> <b-form-select id="input-region" v-model="form.region" size="lg" :options="regions" required /> </b-form-group> <div class="d-flex justify-content-end mt-5"> <b-button type="submit" variant="primary" size="lg">Submit</b-button> </div> </b-form> </div> </div> </div> </template> <script> import { regions } from '@/utils/regions' export default { name: 'MainForm', data () { return { form: { battleTag: '', region: 'eu' } } }, computed: { regions () { return regions.map(region => ({ value: region, text: region.toUpperCase() })) } }, methods: { onSubmit () { const { region, battleTag } = this.form this.$router.push({ name: 'Profile', params: { region, battleTag: battleTag.replace('#', '-') } }) } } } </script>

Cuando el formulario se envía, estamos haciendo un cambio de ruta (a una que aún no existe, de nombre Profile) y le estamos pasando por parámetro (params) los valores que acabamos de recuperar del formulario.

Ya casi lo tenemos, ¡probemos si funciona!

En nuestro componente principal de la vista /Home, es decir, en /views/Home/Index.vue, traemos y usamos el componente formulario que acabamos de crear; quedaría así:

<template> <div class="home-view"> <HomeTitle/> <MainForm/> </div> </template> <script> import MainForm from './HomeForm' import HomeTitle from './HomeTitle' export default { name: 'HomeView', components: { HomeTitle, MainForm } } </script>

Se debería ver así:

HomePage

Para ver si el formulario hace lo que estamos esperando que haga, necesitamos comprobar varios casos:

  • No se envía el formulario

    • No escribimos nada en el input y enviamos el formulario a través del botón (submit)
    • (Con el foco en el input) no escribimos nada en el formulario y le damos a la tecla Enter del teclado input-requiredSe envía el formulario
    • Escribimos algo en el input y le damos a la tecla Enter del teclado
    • Escribimos un texto en el input y le damos a enviar warning-router

¡Bravo!
El formulario está funcionando como es debido, pero tenemos un pequeño problema (de fácil solución). No tenemos la ruta Profile definida, por lo tanto Vue no sabe qué hacer en este caso.
Lo arreglaremos en las siguientes lecturas.