Qwik — Routing

Claves al detalle para trabajar con las rutas en Qwik

Anartz Mugika Ledo🤗
12 min readFeb 6, 2023

Comenzamos un nuevo artículo en el que vamos a trabajar de manera más profunda con las rutas en Qwik, después de haber obtenido las nociones básicas en el artículo inicial.

Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:

Qwik paso a paso desde 0 al detalle

23 stories

En este artículo anterior hemos visto los primeros pasos de una aplicación de una manera muy general después de ver las características y hacer comparativa con otras tecnologías, por lo que el aspecto de las rutas lo trataremos con más cariño, ya que es un aspecto importante a la hora de trabajar con este framework.

Lo que vamos a trabajar en este artículo es lo siguiente:

  • ¿Qué es Routing (Enrutamiento)?
  • Básico. Enrutamiento basado en directorios.
  • Avanzado. Enrutamiento mediante parámetros en diferentes variantes.

Requisitos a tener en cuenta

  • Haber completado la lectura del artículo anterior.
  • Tener instalado Node 16 ó superior.
  • Ganas de seguir aprendiendo.
  • Deseable tener algo de experiencia con otras tecnologías trabajando con diferentes rutas.
  • Tener creado el proyecto de Qwik.

¿Qué es Routing?

El enrutamiento (Routing) es una forma de asignar direcciones URL públicas de un sitio a componentes específicos declarados en nuestra aplicación.

Qwik City utiliza el enrutamiento basado en directorios. Esto significa que la estructura de su directorio de rutas especificará las URL públicas que el usuario va a poder hacer uso en su aplicación.

Vamos a la práctica para implementarlo.

Enrutamiento basado en directorios

Antes de empezar a trabajar con ello, vamos al proyecto creado y eliminamos el directorio flower, para empezar únicamente con la ruta inicial simplificando lo que tenemos y así empezaremos desde 0. El resultado actual queda de la siguiente forma en el directorio routes.

En el directorio de rutas de Qwik City (src/routes), tendremos una nueva carpeta que será la carpeta-ruta-principal + un fichero index que harán que se asignen a una ruta URL de manera automática.

Por ejemplo, imaginaros que vemos una ruta con esta apariencia:

http://127.0.0.1:5173/public

Sabemos que esta ruta se mostrará con el contenido del componente exportado en src/routes/public/index (ya sea .mdx o .tsx, entre otros formatos, que comentaré a continuación de los que podemos hacer uso).

src/
└── routes/
└── public/
└── index.tsx # (.mdx, .jsx, ...) # http://127.0.0.1:5173/public

Hay que tener en cuenta que el archivo del final de la ruta se debe de llamar index. Esto es SUPER importante.

Quizás en otros frameworks con los que habéis trabajado, se hace una distinción entre páginas y endpoints de datos o rutas API. En Qwik City no hay distinción, todos son endpoints en el fichero index.[tipo-de-fichero].

Qwik City admitirá los siguientes tipos de archivos para rutas, lo que si tenemos que hacer es SIEMPRE llamar al fichero index, como hemos mencionado.

Nosotros podemos definir el fichero index.[tipo-de-fichero] donde [tipo-de-fichero] puede ser .ts, .tsx, .js, .jsx, .md, o .mdx.

Ejemplo con un caso QUE NO FUNCIONA en Qwik (por no estar creando el directorio con su fichero index dentro) pero que probablemente funcione en otros frameworks o tecnologías con las que habéis trabajado.

Creamos los siguientes ficheros y en todos añadimos el contenido del componente por defecto como este contenido en cada uno de los 4 ficheros (index.tsx no tocamos):

Siendo esta la estructura de los ficheros en estos momentos:

src/
└── routes/
├── contact.tsx
├── blog.tsx
├── portfolio.tsx
└── index.tsx

Si hacemos la prueba con los siguientes endpoints teniendo como base la URL local, que en mi caso es http://127.0.0.1:5173:

Si probamos con blog.tsx (http://127.0.0.1:5173/blog) y con contact.tsx (http://127.0.0.1:5173/contact) nos pasará como con portfolio, que no encontrará la ruta.

SOLO funcionará con index.tsx que cumple la nomenclatura del nombre de fichero, dentro de routes en la raíz de esta carpeta, por lo que como se ha podido ver, accede al contenido.

Por lo tanto, para que SI funcione en nuestro proyecto Qwik debemos de dejar el contenido estructurado de la siguiente forma, con el directorio especificado para la página y dentro un fichero index.tsx:

src/
└── routes/
├── portfolio/
│ └── index.tsx # http://127.0.0.1:5173/portfolio
├── contact/
│ └── index.tsx # http://127.0.0.1:5173/contact
├── blog/
│ └── index.tsx # http://127.0.0.1:5173/blog
└── index.tsx # http://127.0.0.1:5173/

Comprobando las rutas que nos han dado problemas.:

  • /blog

Haremos lo mismo con portfolio y contact, nos debería de aparecer el contenido concreto a esas rutas, comprobadlo.

Puede pasar lo siguiente, que al ir a cargar cualquiera de las rutas en las que hemos visualizado el error “404 Not found” se muestre el contenido de la página en blanco, cosa que no debería:

Y en la consola un mensaje similar al siguiente:

Si ocurre esto, debemos de parar la ejecución del servidor y reiniciarlo. Volved a probar.

Al principio, esto puede parecer un trabajo extra, pero este enfoque tiene sus ventajas.

Una de esas ventajas es poder definir archivos de componentes en un directorios de ruta sin que se representen, pudiendo usarlo como un componente complementario para esa ruta que se añade al componente principal representado en index. Considera lo siguiente:

src/
└── routes/
├── index.tsx # http://127.0.0.1:5173/
...
└── blog/
├── index.tsx # http://127.0.0.1:5173/blog
└── other-component.tsx # Fichero ignorado y sin mapea en cualquier URL.

El fichero other-component.tsx puede ser importado y usado dentro blog/index.tsx, donde por otro lado será completamente ignorado por Qwik City.

El contenido podremos implementarlo en formato componente o mediante el uso del renderizado de páginas con contenido markdown (extensiones md o mdx). Nos centramos en cada uno de ellos.

Implementando un componente

Para devolver HTML para una ruta específica, deberá implementar un componente. Para archivos .tsx, el componente debe exportarse por defecto que llamaremos default. De manera alternativa, podremos renderizar contenido en formato Markdown con las extensiones .md y .mdx.

import { component$ } from '@builder.io/qwik';

export default component$(() => {
return <H1>Hello World!</H1>;
});

Implementando contenido en formato Markdown

Para poder trabajar con rutas que visualizan contenido en formato Markdown, lo que es el modo de crear el path, es el mismo, creando la carpeta.

Lo único que va a cambiar es el fichero que crearemos, que en vez de llamarse index.tsx como hemos estado haciendo hasta este momento, será index.mdx (o index.md)

Añadimos una nueva ruta llamada /readme junto con su fichero index, que en este caso será con la extensión mdx.

src/
└── routes/
├── index.tsx # http://127.0.0.1:5173/
...
└── readme/
├── index.mdx # http://127.0.0.1:5173/readme
└── blog/
├── index.tsx # http://127.0.0.1:5173/blog

Una vez creado el fichero, añadimos el siguiente contenido:

Ahora si accedemos al endpoint asignado: http://127.0.0.1:5173/readme

Debería de mostrarse la información (recordad, que habrá que reiniciar para que se asigne el endpoint en el caso de no visualizarse el contenido):

Si añadimos un elemento de lista con un enlace:

# Readme Rutas

Trabajando con formato markdown
* [Twitter](https://twitter.com/mugan86)

Al recargar quedará de la siguiente forma:

Routing Avanzado — Enrutamiento mediante parámetros

Los parámetros de ruta son partes de las URL que se extraen en los parámetros.

Imaginaros que tenemos una página con las siguientes URLs donde id-del-producto puede ser cualquiera de los miles de productos que tenemos en nuestra base de datos.

Obviamente, no sería nada práctico crear una ruta para cada producto ya que no sería nada fácil de mantener y a la vez no se aplicarían buenas prácticas. En su lugar, vamos a definir un parámetro de ruta (una parte de la URL) que se va a usar para extraer el id de nuestro producto con id-del-producto de manera dinámica.

Teniendo como referencia el parámetro para el id del producto, especificamos el parámetro de ruta dentro del directorio con el formato [<nombre-parametro>] donde teniendo el id del producto llamado por ejemplo productId, se quedará asignado como [productId].

Dentro del proyecto especificaremos de la siguiente manera:

src/
└── routes/
├── index.tsx # http://127.0.0.1:5173/
...
└── product/
└── [productId]/
├── index.tsx # http://127.0.0.1:5173/product/1234
└── details/
└── index.tsx # http://127.0.0.1:5173/product/1234/details

En el proyecto se refleja así, de la siguiente manera:

Probamos ejecutando diferentes URLs (reiniciar el servidor):

Página del producto principal

Con una de las opciones, por ejemplo la opción /product/1234, como se puede apreciar se carga el contenido de la página principal del producto, tal como se ha especificado en el componente del fichero index.tsx en la ruta de ficheros src/routes/product/[productId]/index.tsx:

Página de detalles del producto seleccionado:

Con una de las opciones, por ejemplo la opción /product/1234/details, se cargará el contenido de la página de detalles del producto, tal como se ha especificado en el componente del fichero index.tsx en la ruta de ficheros src/routes/product/[productId]/details/index.tsx:

Bien, ahora ya sabemos como añadir información de manera dinámica mediante parámetros de ruta donde hemos añadido el id de un producto y lo que nos hace falta es plasmarlo en la información de nuestra página y esto lo haremos mediante la obtención del parámetro ruta desde una URL

Obtención del parámetro ruta desde una URL

Una vez que tenemos [productId]en la URL, necesitamos recuperar el valor que se adjunta en ese parámetro y esto lo podemos hacer mediante el uso de la API useLocation().

Lo aplicaré en el fichero index.tsx dentro de la carpeta details, cuya ruta es src/routes/product/[productId]/details/index.tsx

Dejando el componente de esta forma:

import { component$ } from "@builder.io/qwik";
import { useLocation } from "@builder.io/qwik-city";

export default component$(() => {
const location = useLocation();
return (
<>
<div>Product Item Details- Página</div>
<p>Href: {location.href}</p> // URL princ. + path + valor de parámetro de ruta
<p>Pathname: {location.pathname}</p> // path + valor de parámetro de ruta
<p>Product Id: {location.params.productId}</p> // Valor de parámetro de ruta
</>
);
});

Una vez guardados los cambios, se reflejará de la siguiente manera introduciendo 1234 como valor del productId en http://127.0.0.1:5173/product/1234/details/

Para poder obtener el valor del parámetro de ruta en la página principal del producto (src/routes/product/[productId]/index.tsx) deberemos de aplicar también el uso de useLocation() y hacer lo mismo. Esto lo aplicáis para afianzar lo aprendido y comprobáis el resultado que debe de mostrar esta información:

Capturar todas las rutas

También es posible crear rutas comodín agregando un directorio como [...productIds]. Esto funciona como el ejemplo descrito anteriormente, pero también considerará sub-apartados del URI.

También se va a poder aplicar en el nivel raíz de las rutas.

src/
└── routes/
└── product/
└── [...productIds]/
└── index.tsx

Y dentro del fichero index.tsx añadimos el siguiente contenido, donde en el apartado de productIds obtendrá un string concatenado con todos los ids de productos que para mostrarlo como string, lo convertiremos:

import { component$ } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';

export default component$(() => {
const location = useLocation();
return (
<>
<div>Producto - Page principal</div>
<p>Href: {location.href}</p>
<p>Pathname: {location.pathname}</p>
<p>Product Id: {JSON.stringify(location.params.productIds)}</p>
</>
);
});

Usando la configuración establecida, ejecutamos varias Url y vemos su resultado:

Podríamos “extraer” esos ids y mostrarlos de una manera más elegante, recorriendo el contenido, mediante la creación de un array desde separar elementos mediante split usando como parámetro “/”:

import { component$ } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';

export default component$(() => {
const location = useLocation();
return (
<>
....
<p>Product Id:</p>{' '}
<ul>
{location.params.productIds.split('/').map((id, index) => {
return (id !== '') ? (
<li>
Product select ({index + 1}): {id}
</li>
): undefined
})}
</ul>
</>
);
});

Visualizándose de la siguiente forma:

¿Podremos especificar un límite para introducir los datos?

Si, es posible limitar la captura de los parámetros de ruta si añadimos un directorio que lo limite. Para especificar ese límite debemos de añadir un nuevo directorio (ó más de uno) con un nombre descriptivo de la página que limitará esa captura de valores de la ruta. Por ejemplo, yo añadiré uno llamado details dentro de [...productIds], en el que cogeré todos los ids anteriores y los muestro de una manera detallada tal y como se hace en la página principal.

src/
└── routes/
└── product/
└── [...productIds]/
└── index.tsx
└── details/
└──index.tsx

Cuyo contenido será el siguiente, que servirá ya para diferenciarlo respecto al index.tsx anterior que sería la página principal:

import { component$ } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';

export default component$(() => {
const location = useLocation();
return (
<>
<div>Productos - Page con detalles</div>
<p>Href: {location.href}</p>
<p>Pathname: {location.pathname}</p>
<p>Product Id:</p>{' '}
<ul>
{location.params.productIds.split('/').map((id, index) => {
return (id !== '') ? (
<li>
Producto seleccionado ({index + 1}): {id}
</li>
): undefined
})}
</ul>
</>
);
});

La siguiente estructura dará opción a capturar esta ruta:

Extrayendo 203/204/205 como los valores de los ids de los productos ya que details ya considera el final de la ruta y no tiene en cuenta ese valor utilizando el fichero que hemos creado en la siguiente ruta: src/routes/product/[...productIds]/details/index.tsx.

En cambio, si añadimos

Extraerá 203/204/205/details/2333 como los valores de los ids de los productos ya que details NO se ha especificado como final de la ruta a la hora de la introducción de la URL. Por ese motivo no usa el fichero index.tsx que se encuentra en la ruta src/routes/product/[...productIds]/details/index.tsx.

Lo que va a usar será el fichero que se encuentra en la ruta src/routes/product/[...productIds]/index.tsx, mostrándose de la siguiente manera, como se ha especificado anteriormente:

Agrupación

En algunos casos, puede surgir la necesidad de mover nuestras rutas a un subdirectorio sin afectar el nombre de la ruta que va a generar Qwik City en función de la estructura de carpetas, para poder tener más orden.

Puede darse el caso que tengamos varias rutas en el mismo nivel que deberían usar diferentes diseños (esto lo veremos con más detalle en un artículo que se publicará en breves)

Tanto para la primera situación o la segunda, podemos crear directorios entre paréntesis (p. ej. (products)). Lo vamos a reflejar de la siguiente manera:

src/
└── routes/
└── (products)/
├── detail/
| └──index.tsx
├── preview/
└──index.tsx

Realizamos las pruebas correspondientes, donde usamos el nombre directorio ignorando completamente el directorio (products):

Como se ha mencionado products NO SERÁ parte del pathname.

Conclusión

Hemos podido aprender las deferentes formas que tenemos de como gestionar las rutas y como obtener la información de los parámetros de rutas, desde lo más sencillo a conceptos más avanzados.

Con lo aprendido en este artículo y el anterior, podremos pasar al siguiente donde nos faltaría ver los aspectos de las plantillas anidadas, donde podremos construir diferentes plantillas en base a la ruta, como por ejemplo zona privada y pública y también el enrutamiento en base a los menús. Esto lo veremos en un próximo artículo.

Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:

Qwik paso a paso desde 0 al detalle

23 stories

Presencia en redes sociales

Podéis encontrarme en las siguientes redes.

--

--

Anartz Mugika Ledo🤗

[{#frontend:[#mobile:{#android, #kotlin, #ionic}}, {#web:{#angular, #qwik, #bootstrap}}],{#backend: [{#graphql, #nestjs,#express, #mongodb, #mysql}]}]