Creación de Layouts con Vue y Bootstrap para Mejorar la UI

Clase 10 de 27Curso Avanzado de Vue.js 2

Aunque la app se ve mejor sigue estando la falta de estilo, ya que se ve todo alineado a la izquierda. AlLeft

Para arreglar esto vamos a hacer uso de las clases CSS de Bootstrap y a crear un Layout. Con los layouts conseguimos reutilizar código o componentes que se utilizan en todas las páginas, implementándolo una sola vez. Creamos, dentro de la carpeta /layouts un archivo con el nombre MainLayout.vue

Y le damos el siguiente contenido:

<template> <div class="container"> <router-view/> </div> </template> <script> export default { name: 'MainLayout' } </script>

> 📗 La etiqueta <router-view /> es parte de la API de Vue Router. Lee la documentación aquí: https://router.vuejs.org/api/#router-view

Todas nuestras vistas que hagan match a una ruta se van a inyectar dentro de esta etiqueta, <router-view />.
Por lo tanto, en el layout principal podemos poner componentes como la barra de navegación (Navbar) o el footer, que van a ser iguales para todas las páginas.
Lo más probable es que dichos componentes no cambien entre páginas, por lo que es perfecto que estén aquí.

Los componentes de tipo layout (recuerda, en Vue todo son componentes), son útiles cuando queremos tener varios tipos de páginas, por ejemplo, podemos tener un layout específico para la página del Login, otro para las páginas de error y otro layout para el resto de páginas.

En este curso vamos a crear 2 layouts distintos, uno para el loading y otro para el resto de páginas.
Debes crear los layouts según las necesidades de tu proyecto.


Ya tenemos el layout principal de nuestra app. Vamos a crear el layout de Loading, y para ello tenemos que crear otro componente dentro de la carpeta /layouts, con el nombre LoadLayout.vue y le dotamos del siguiente contenido:

<template> <div class="loading-layout"> <slot/> </div> </template> <script> export default { name: 'LoadLayout' } </script>

> 📗 Un Slot en Vue es una funcionalidad inspirada en la especificación de Web Components con la finalidad de poder definir zonas de contenido distribuido. Puede contener cualquier plantilla de código, incluyendo HTML.
> Lee más acerca de los slots de Vue aquí: https://es.vuejs.org/v2/guide/components-slots.html

Bien, ya tenemos nuestro componente Loading, nuestro MainLayout y nuestro LoadingLayout, pero nos sigue faltando un elemento. ¿Cómo controlamos si debemos mostrar el layout de Loading o el layout principal?
Vamos a crear una variable en nuestro Store que nos indique el estado de nuestra app, es decir, si está loading o no.

Esto lo vamos a usar durante la petición del token de autenticación, es decir, mientras hacemos la llamada HTTP estaremos mostrando el LoadingLayout con el componente Loading.

Por lo tanto, vamos a nuestra carpeta de /store/modules y creamos un nuevo módulo (archivo) llamado loading.js, que es el que va a gestionar el estado Loading inicial de nuestra app.
No olvidemos que esto es solo una práctica y lo más probable es que hacer esto en un proyecto pequeño (como este) no tenga sentido.

// store/modules/loading.js export default { namespaced: true, state: { isLoading: false }, mutations: { SET_LOADING (state, payload) { state.isLoading = payload } } }

Hemos creado una variable (estado) isLoading con un espacio de nombres y una función (mutación) que cambia el valor de isLoading. Extremadamente sencillo, pero vamos a ver como trabajar con módulos y namespaces con Vuex.

Ahora sí, tenemos todos los ingredientes necesarios para crear nuestro Loading mientras se hace la petición POST para obtener el token.

Por defecto, isLoading tiene valor false, por lo tanto, el primer paso es cambiarlo a true cuando estemos haciendo la petición de token. Esto lo hacemos desde nuestro módulo oauth.js del Store, en nuestra función (acción) getToken.

export default { // ... actions: { getToken ({ commit }) { commit('loading/SET_LOADING', true, { root: true }) oauth.getToken() .then(({ data }) => { commit('SET_ACCESS_TOKEN', data.access_token) }) .catch((err) => { commit('SET_ACCESS_TOKEN', null) console.log('Error OAuth: ', err) }) .finally(() => { commit('loading/SET_LOADING', false, { root: true }) }) } } }

Mientras dure la llamada HTTP ponemos el valor a true y cuando termine (con error o con éxito) lo ponemos a false. Lo bueno de tener namespaces con Vuex, aparte de modularizar por funcionalidad, es que no tendremos problemas con el nombrado de nuestras acciones y mutaciones porque llevan el prefijo del módulo, en este caso loading/XXX.

Ahora sí, ya tenemos todo listo para ir a nuestro fichero App.vue y decirle que mientras este esperando al token muestre el loading, y que cuando termine, muestre el layout principal, que contiene las vistas.

Aprovechamos para borrar los enlaces que venían por defecto. Nuestro fichero App.vue, en el bloque de template, es decir el HTML, se vería así:

<template> <div id="app"> <LoadLayout v-if="isLoading"> <BaseLoading/> </LoadLayout> <MainLayout v-else/> </div> </template>

Y la parte del <script> tendría este contenido:

<script> import { mapState } from 'vuex' import LoadLayout from './layouts/LoadLayout' import MainLayout from './layouts/MainLayout' import BaseLoading from '@/components/BaseLoading.vue' export default { name: 'App', components: { MainLayout, LoadLayout, BaseLoading }, computed: { // Uso: mapState(moduleName, { state }) ...mapState('loading', { isLoading: 'isLoading' }) } } </script>

> 📗 Lee más acerca de los mapState de vuex aquí: https://vuex.vuejs.org/guide/state.html#the-mapstate-helper

El proceso es el siguiente:

  • Desde Vuex nos traemos la funcionalidad de mapState
  • Importamos los 3 componentes que necesitamos (los 2 layouts que acabamos de crear y el componente de Loading)
  • A través de mapState podemos acceder a los valores del estado de nuestro módulo (en este caso nos interesa el módulo de loading) y utilizarlos en nuestro componente.
    • Para usar mapState con namespaces lo único que tenemos que hacer es indicarle, antes de empezar a mapear el estado, a qué módulo nos referimos, es decir, 'loading'. El nombre del módulo se corresponde con el nombre del fichero.
      Como el módulo se llama loading.js tendremos que usar de nombre loading (sin la extensión). Con esto le decimos que solo nos interesa el estado del loading.
  • Con las directivas v-if y v-else mostramos un componente u otro según esté cargando o no. Si está loading mostramos loading, en caso contrario mostramos la vista.

En el navegador, abrimos las Herramientas para desarrolladores (o DevTools), vamos a la pestaña de console y recargamos la app. Deberías ver el siguiente error: Vuex-Err ¿Adivinas por qué es? Efectivamente, no hemos dado de alta el módulo en nuestro Store. Vamos a ello. Desde el fichero de /store/index.js agregamos lo siguiente:

import Vue from 'vue' import Vuex from 'vuex' import oauth from './modules/oauth' import loading from './modules/loading' Vue.use(Vuex) export default new Vuex.Store({ modules: { oauth, loading } })

Si vas al navegador deberías ver, si no hay ningún error, el componente Loading durante un pequeño espacio de tiempo y que después se carga la vista de la página Home Loading

El código completo de App.vue es el siguiente:

<template> <div id="app"> <LoadLayout v-if="isLoading"> <BaseLoading/> </LoadLayout> <MainLayout v-else/> </div> </template> <script> import { mapState } from 'vuex' import LoadLayout from './layouts/LoadLayout' import MainLayout from './layouts/MainLayout' import BaseLoading from '@/components/BaseLoading.vue' export default { name: 'App', components: { MainLayout, LoadLayout, BaseLoading }, computed: { ...mapState('loading', { isLoading: 'isLoading' }) } } </script> <style lang="stylus"> #app padding 60px 0 font-family 'Avenir', Helvetica, Arial, sans-serif -webkit-font-smoothing antialiased -moz-osx-font-smoothing grayscale color #ffffff background-color #15202b </style>

Vamos a la siguiente lectura, en la que veremos cómo mejorar nuestro _Layout