| Tabla de contenidos |
|---|
...
Para el UI del Portal de Servicios unificado, cada servicio definirá sus rutas en un archivo module.routes.js, que se situará en el directorio raíz del servicio, y tendrá esta estructura:
Rutas de un servicio
Para la navegación en Vue utilizamos el paquete vue-router. Podemos ver la documentación oficial con todos los detalles aquí.
...
| Bloque de código | ||
|---|---|---|
| ||
import router{ defineAsyncComponent } from 'vue'; const ExpedientesView = defineAsyncComponent(() => import(/* webpackChunkName: "CEXP_ExpedientesView" */'./views/ExpedientesView./router';vue')); const rutaProyecto = '/consulta/expedienteacademico'; export default [ { path: `${rutaProyecto}/`, name: 'CEXP Expedientes', component: ExpedientesView, meta: { requiresAuth: true, }, }, { ... }, { ... }, ]; |
Todos las rutas serán lazy, se cargarán los componentes cuando se navegue a ellos, para evitar que se tenga que cargar todo al inicio de la aplicación. Para eso, como vemos, utilizamos defineAsyncComponent y en el import especificamos webpackChunkName, especificando un nombre distintivo (no poner "home", por ejemplo, mejor poner como en el ejemplo, algo distintivo de tu servicio)Y también en la parte delnew Vue:
| Bloque de código | ||
|---|---|---|
| ||
newimport Vue({ router, render: h{ defineAsyncComponent } from 'vue'; const nombreInterno1 = defineAsyncComponent(() => h(App), }).$mount('#app') |
Definir rutas
import(/* webpackChunkName: "nombreFragmentoEnDist1" */'./views/Componente1.vue'));
const nombreInterno2 = defineAsyncComponent(() => import(/* webpackChunkName: "nombreFragmentoEnDist2" */'./views/Componente2.vue')); |
Después, como vemos, definimos la ruta base del proyecto, que utilizaremos en cada una de las rutas.
Añadir variables del proyecto (meta) para nuestras rutas. Funcionalidades extra
Por último, para las rutas que requieran estar autenticado, especificamos incluimos esto:
| Bloque de código | ||
|---|---|---|
| ||
meta: {
requiresAuth: true,
}, |
Además, existen (edición 21/02/2023) dos nuevas variables meta que se pueden añadir a cada una de nuestras rutas, :
- needs2FA: Se añadirá a aquellas rutas que necesiten un factor doble de autenticación por parte del usuario, aunque todo este factor de autenticación deberá comprobarse en cada llamada al backend. Es para darle una mejor experiencia al usuario cuando navegue entre pantallas.
- hiddenFeature: Se utilizará cuando queramos mostrar algo a algún usuario y que llegue hasta producción pero no esté preparado para funcionar al 100% (bien porque no se tengan datos definitivos, bien porque no haya ni backend) y se podrá activar para navegar visitando
/feature-flag/nuestra/rutaSi no la activamos, enviará al usuario a un error 404.
Como con requiresAuth, no es necesario añadirlas si su valor va a ser false.
Al final, una ruta completa quedaría de este estiloPara definir las rutas, tendremos un directorio router/dentro de src/, que contendráindex.js. En él podemos definir una lista con un objeto por cada ruta, de la siguiente forma:
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
meta: { requiresAuth: true, path: '/frameworkFundeweb'needs2FA: true, hiddenFeature: true, namegroup: 'framework fundewebuser', componentapplication: FrameworkFundeweb'profile-data', } |
Aquí nos definimos la ruta que tendremos en la url, un nombre que incluiremos opcionalmente, para referenciarlo desde otras partes del código en lugar de escribir la ruta, y por último el componente que se mostrará cuando naveguemos a la ruta.
, |
| Advertencia |
|---|
El name de la ruta debe ser ÚNICO EN TODO EL PROYECTO. Si hay nombres de rutas repetidos, no funcionará. Por lo tanto, tendremos que evitar nombres generales com "Home". Lo mejor que podemos hacer es poner un prefijo característico de nuestro servicio. Por ejemplo, para el servicio Consulta de Expedientes, el nombre de la única ruta que tiene es "CEXP Expedientes", utilizando CEXP para distinguirlo. |
Añadir rutas al portal
Para añadir las rutas al router global de POSE, tendremos que modificar el archivo app.routes.js, situado en src/. Hay que importar el archivo previo e incluirlo al final del objeto que devuelve el router, de esta forma.
ImportUn ejemplo de fichero completo sería este:
| Bloque de código | ||
|---|---|---|
| ||
import VuenombreRutasComponente from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import Profile from './nombreGrupo/nombreProyecto/nombreComponente'; |
Declaración:
| Bloque de código | ||
|---|---|---|
| ||
export default [ ...PoseRoutes, ...MNCSRoutes, ...ultimoficheroDeRutas, <--------../views/Profile.vue' Vue.use(VueRouter); const routes = [ { path: "/", name: "home"'/:catchAll(.*)', component: Home... } ]; |
En la siguiente imagen se puede ver cómo ha quedado el fichero tras añadir el módulo de Mis Certificados:
Definir sub-rutas
Podemos definir rutas hijas de una ruta ya existente, añadiéndolas al parámetro children de la ruta padre:
| Bloque de código | ||
|---|---|---|
| ||
, { path: '"/profile'about", name: 'profile'"about", component: ProfileAbout, props: true } ] const router = new VueRouter({ mode: 'history', routes }) export default router children: [{path: "detalle", name: "Detalle", component: Detalle }] }, |
En este ejemplo la ruta padre sería /about, y la ruta hija /about/detalle. A la hora de definir rutas hijas, es importante tener en cuenta que el path no debe incluir la barra ("/"), pues si lo hace lo toma como lo que hay inmediatamente después de la url de nuestro sitioSi nos fijamos, podemos poner en cada ruta un campo props a true. Con esto, cuando naveguemos a esa ruta, los parámetros que se le pasen cuyo nombre coincida con un prop definido en el componente se asignará a este directamente. Es importante tener en cuenta que para hacer esto, tendremos que hacer la navegación utilizando el name, y no el path, pues si no no funciona.
Vista del router
El router habrá que incluirlo como un componente más . Así, podemos definir la parte de la pantalla que cambiará cuando se cambie de ruta, pudiendo dejar fijo, por ejemplo, un menú lateral, y que sólo se tenga que cargar el contenido de la página.Para esto, en el archivo .vue inicial, el que se carga al principio, basta con incluir (en POSE ya está incluido), así:
| Bloque de código | ||
|---|---|---|
| ||
<router-view></router-view> |
Así, esta parte de la página se sustituirá por el componente correspondiente, cambiando sólo la parte necesaria de la página sin tener que volver a cargarlo todo.
Navegar
Para relizar realizar la navegación, podríamos hacerlo concon href, pero es mejor utilizar router-link:
| Bloque de código | ||
|---|---|---|
| ||
<router-link :to="{ name: 'nuevaComunicacion', params: { idReenviar: fila.data.id }}"> |
Como vemos, se le indica a dónde navegar con el nombre que hemos definido previamente para la ruta. Además podemos pasar datos como parámetro, con params. Después, en el componente al que se navega, podemos coger dichos datos referenciando a this.$route.params. En este ejemplo, el objeto que se ha pasado se cogería con this.$route.params.idReenviar.
Si queremos hacerlo de forma programática, en la parte de JavaScript concon this.$router.pusthpush():
| Bloque de código | ||
|---|---|---|
| ||
// path
this.$router.push('/home')
// path
this.$router.push({ path: '/home' })
// nombre + parámetrosname
this.$router.push({ name: 'user', params: { userId: '123' } })Home' }) |
En Composition API, la sintaxis cambia, en vez de acceder a this.$router y this.$route, disponemos de useRouter() y useRoute():
| Bloque de código | ||
|---|---|---|
| ||
<script setup>
import { userRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
</script> |
Y ya se accede como visto anteriormente, pero usando router o route:
| Bloque de código |
|---|
// path
router.push('/home')
// path
router.push({ path: '/home' })
// name
router.push({ name: 'Home' }) |
Interceptores de navegación
Vue router ofrece la posibilidad de añadir código para ejecutarlo cuando se produzca la navegación, con los navigation guards. Son una serie de métodos que podemos definir. El proceso de navegación, y el momento en el que se ejecuta cada método es el siguiente:
...
Estos métodos pueden ser globales (no los utilizaremos) o locales a cada ruta.
Interceptores
...
beforeEach
Podemos registrar interceptores de manera global para definir un comportamiento que se ejecute justo antes de producirse la navegación a cualquier ruta. Esto lo hacemos conrouter.beforeEach, de esta manera:
| Bloque de código | ||
|---|---|---|
| ||
router.beforeEach((to, from, next) => {
// ...
}) |
Los parámetros de esta función son:
to: objeto con la información de la ruta a la que se navega.
from: objeto con la información de la ruta desde la que se navega.
next: es una función que permite reanudar la navegación. Nos permite llamarla de diferentes maneras para tener diferentes comportamientos:
next()reanuda la navegación hacia la ruta indicada ento.next('/')redirige a la ruta que se le pase como parámetro.next(false)aborta la navegación.next(new Error())aborta la navegación y se ejecuta el callbackonError.
Hay que asegurarse de que sólo se llama anext()una vez, en caso contrario podemos encontrarnos errores. Por ejemplo, esto estaría mal:
| Bloque de código | ||
|---|---|---|
| ||
// BAD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// if the user is not authenticated, `next` is called twice
next()
}) |
La forma correcta sería esta:
| Bloque de código | ||
|---|---|---|
| ||
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
}) |
Se pueden registrar múltiples interceptores del mismo tipo, y estos se ejecutarán en orden de aparición, es decir, el primero en registrarse es el primero que se ejecuta.
Los interceptores nos pueden venir bien para comprobar algún estado global de la aplicación, como la autorización del usuario.
beforeResolve
Se pueden registrar estos interceptores con router.beforeResolve, de la misma forma que los beforeEach. La diferencia es que estos se ejecutan justo antes de que se confirme la navegación, después de haber ejecutado los interceptores de ruta y de componentes, y que los componentes de ruta asíncronos se resuelvan.
afterEach
También se pueden registrar globalmente interceptoresafterEachque se ejecutarán después de confirmar la navegación. Se definen como losbeforeEach, pero sin un parámetronext, pues ya se ha confirmado la navegación:
| Bloque de código | ||
|---|---|---|
| ||
router.afterEach((to, from) => {
// ...
}) |
...
por ruta
Se pueden definir interceptores beforeEnter directamente en el objeto que define las rutas, con la misma notación que los interceptores globales before:
| Bloque de código | ||
|---|---|---|
| ||
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
} |
Interceptores por componente
Se pueden definir interceptores de navegación dentro de los componentes que se pasan a las rutas, con las siguientes opciones:
...
| Bloque de código | ||
|---|---|---|
| ||
beforeRouteEnter (to, from, next) {
// called before the route that renders this component is confirmed.
// does NOT have access to `this` component instance,
// because it has not been created yet when this guard is called!
},
beforeRouteUpdate (to, from, next) {
// called when the route that renders this component has changed.
// This component being reused (by using an explicit `key`) in the new route or not doesn't change anything.
// For example, for a route with dynamic params `/foo/:id`, when we
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
// will be reused (unless you provided a `key` to `<router-view>`), and this hook will be called when that happens.
// has access to `this` component instance.
},
beforeRouteLeave (to, from, next) {
// called when the route that renders this component is about to
// be navigated away from.
// has access to `this` component instance.
} |
Volver arriba tras navegar
Por defecto, al navegar a otra ruta, se mantiene el scroll que teníamos en la ruta anterior. Si queremos que al hacerlo la página se ponga de nuevo al inicio, arriba del todo, tenemos que definir así el scrollBehavior al crear la instancia del router:
| Bloque de código | ||
|---|---|---|
| ||
const router = new VueRouter({ ..., scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 }; } }) |
...
