En esta publicación, usaremos una demostración de una tienda de comercio electrónico que construí e implementé en Netlify para mostrar cómo podemos crear rutas dinámicas para los datos entrantes. Es un caso de uso bastante común: obtiene datos de una API y no sabe exactamente cuáles podrían ser esos datos, hay muchos o pueden cambiar. Afortunadamente para nosotros, Nuxt hace que el proceso de creación de enrutamiento dinámico sea muy fluido.
¡Empecemos!
Sitio de demostración
Repositorio de GitHub
Creando la página
En este caso, tenemos algunos datos ficticios para la tienda que creé en mockaroo y los estoy almacenando en la carpeta estática. Por lo general, usará fetch o axios y una acción en la tienda Vuex para recopilar esos datos. De cualquier manera, almacenamos los datos con Vuex en store/index.js
, junto con el estado de la interfaz de usuario y una matriz vacía para el carrito.
import data from '~/static/storedata.json'
export const state = () => ({
cartUIStatus: 'idle',
storedata: data,
cart: []
})
Es importante mencionar que en Nuxt, todo lo que tenemos que hacer para configurar el enrutamiento en la aplicación es crear un .vue
archivo en el directorio de páginas. Así que tenemos un index.vue
página para nuestra página de inicio, una cart.vue
página para nuestro carrito, y así sucesivamente. Nuxt genera automáticamente todo el enrutamiento de estas páginas para nosotros.
Para crear un enrutamiento dinámico, crearemos un directorio para alojar esas páginas. En este caso, hice un directorio llamado /products
, ya que así serán las rutas, una vista de los detalles de cada producto individual.
En ese directorio, crearé una página con un guión bajo y el indicador único que quiero usar por página para crear las rutas. Si miramos los datos que tengo en mi carrito, queda así:
[
{
"id": "9d436e98-1dc9-4f21-9587-76d4c0255e33",
"color": "Goldenrod",
"description": "Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.",
"gender": "Male",
"name": "Desi Ada",
"review": "productize virtual markets",
"starrating": 3,
"price": 50.40,
"img": "1.jpg"
},
…
]
Puede ver que la identificación para cada entrada es única, por lo que es un buen candidato para algo que usar, llamaremos a la página:
_id.vue
Ahora, podemos almacenar la identificación de la página en particular en nuestros datos usando los parámetros de ruta:
data() {
return {
id: this.$route.params.id,
}
},
Para la entrada de arriba, nuestros datos si miramos en devtools serían:
id: "9d436e98-1dc9-4f21-9587-76d4c0255e33"
Ahora podemos usar esto para recuperar toda la otra información para esta entrada de la tienda. voy a usar mapState
:
import { mapState } from "vuex";
computed: {
...mapState(["storedata"]),
product() {
return this.storedata.find(el => el.id === this.id);
}
},
Y estamos filtrando el storedata
para encontrar la entrada con nuestra identificación única!
Hágale saber a la configuración de Nuxt
Si estuviéramos creando una aplicación usando yarn build
, habríamos terminado, pero estamos usando Nuxt para crear un sitio estático para implementar, en nuestro caso en Netlify. Cuando usamos Nuxt para crear un sitio estático, usaremos el yarn generate
mando. Tenemos que informar a Nuxt sobre los archivos dinámicos con el generate
mando en nuxt.config.js
.
Este comando esperará una función que devolverá una promesa que se resuelve en una matriz que se verá así:
export default {
generate: {
routes: [
'/product/1',
'/product/2',
'/product/3'
]
}
}
Para crear esto, en la parte superior del archivo traeremos los datos del directorio estático y crearemos la función:
import data from './static/storedata.json'
let dynamicRoutes = () => {
return new Promise(resolve => {
resolve(data.map(el => `product/${el.id}`))
})
}
Luego llamaremos a la función dentro de nuestra configuración:
generate: {
routes: dynamicRoutes
},
Si está recopilando sus datos de una API con axios (que es más común), se vería más así:
import axios from 'axios'
let dynamicRoutes = () => {
return axios.get('https://your-api-here/products').then(res => {
return res.data.map(product => `/product/${product.id}`)
})
}
Y con eso, ¡hemos terminado por completo con el enrutamiento dinámico! Si apaga y reinicia el servidor, verá las rutas dinámicas por producto en acción.
Para la última parte de esta publicación, continuaremos, mostrando cómo se hizo el resto de la página y cómo estamos agregando artículos a nuestro carrito, ya que eso podría ser algo que usted también quiera aprender.
Rellenar la página
Ahora podemos llenar la página con la información que queramos mostrar, con el formato que queramos, ya que tenemos acceso a todo con la propiedad calculada del producto:
<main>
<section class="img">
<img :src="https://css-tricks.com/creating-dynamic-routes-in-a-nuxt-application/`/products/${product.img}`" />
</section>
<section class="product-info">
<h1>{{ product.name }}</h1>
<h4 class="price">{{ product.price | dollar }}</h4>
<p>{{ product.description }}</p>
</section>
...
</main>
En nuestro caso, también querremos agregar artículos al carrito que está en la tienda. Agregaremos la capacidad de agregar y eliminar elementos (sin permitir que el recuento de disminución caiga por debajo de cero)
<p class="quantity">
<button class="update-num" @click="quantity > 0 ? quantity-- : quantity = 0">-</button>
<input type="number" v-model="quantity" />
<button class="update-num" @click="quantity++">+</button>
</p>
...
<button class="button purchase" @click="cartAdd">Add to Cart</button>
En nuestro methods
en ese componente, agregaremos el elemento más un nuevo campo, la cantidad, a una matriz que pasaremos como carga útil a un mutation
en la tienda.
methods: {
cartAdd() {
let item = this.product;
item.quantity = this.quantity;
this.tempcart.push(item);
this.$store.commit("addToCart", item);
}
}
En la tienda de Vuex, comprobaremos si el artículo ya existe. Si es así, simplemente aumentaremos la cantidad. De lo contrario, agregaremos el artículo completo con la cantidad a la matriz del carrito.
addToCart: (state, payload) => {
let itemfound = false
state.cart.forEach(el => {
if (el.id === payload.id) {
el.quantity += payload.quantity
itemfound = true
}
})
if (!itemfound) state.cart.push(payload)
}
Ahora podemos usar un captador en la tienda para calcular el total, que es lo que eventualmente pasaremos a nuestra función sin servidor de Stripe (¡otra publicación sobre esto próximamente!). Usaremos un reduce para esto, ya que reduce es muy bueno para recuperar un valor de muchos. (Escribí más detalles sobre cómo funciona reducir aquí).
cartTotal: state => {
if (!state.cart.length) return 0
return state.cart.reduce((ac, next) => ac + next.quantity * next.price, 0)
}
Sitio de demostración
Repositorio de GitHub
¡Y ahí lo tienes! Hemos configurado páginas de productos individuales y Nuxt genera todas nuestras rutas individuales para nosotros en el momento de la construcción. Serías Nuxt si no lo intentaras tú mismo. 😬