# Ruta dinámica
# views > Product.vue
# Importaciones
- Computed de vue
- Rutas / router
- Stores card + products
- Tipos typescript / interface
- Utilidad de moneda
- Componente esqueleto
# Funciones
- Buscamos en items por producto.Id
- stores > product
- Añadimos la función add
- stores > card
<script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useCartStore } from '../stores/cart';
import { useProductStore } from '../stores/products';
import type { Product } from '../stores/products';
import { toCurrency } from '../shared/utils';
import CartCardSkeleton from '../components/CartCardSkeleton.vue';
const cartStore = useCartStore();
const productStore = useProductStore();
const route = useRoute();
const product = computed<Product>(
() => productStore.items[route.params.productId as string]
);
</script>
# Componentes > Navbar
# Importaciones
- Computed de vue
- Stores cart / cesta
- Componente Search.vue
# Funciones
- Contador de productos
- router-link
- Componente Search.vue
<script setup lang="ts">
import { computed } from 'vue';
import { useCartStore } from '../stores/cart';
import Search from './Search.vue';
const cartStore = useCartStore();
const count = computed(() => cartStore.count);
</script>
<template>
<div class="navbar mb-2 shadow-lg bg-neutral text-neutral-content">
<div class="flex-none lg:hidden">
<label for="drawer-input" class="btn btn-square btn-ghost">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="inline-block w-6 h-6 stroke-current"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
></path>
</svg>
</label>
</div>
<div class="flex-none px-2 mx-2">
<span class="text-lg font-bold"> Arduinoes Tienda</span>
</div>
<div class="flex-1 px-2 mx-2">
<div class="items-stretch hidden lg:flex">
<router-link class="btn btn-ghost btn-sm rounded-btn" to="/">
Productos
</router-link>
<router-link class="btn btn-ghost btn-sm rounded-btn" to="/cart">
Cesta
<div class="badge ml-2 badge-outline" v-text="count"></div>
</router-link>
</div>
</div>
<div class="flex-1 lg:flex-none">
<Search />
</div>
</div>
</template>
# Components > Search.vue
# Importaciones
- Importamos modulos de vue,
- Router
- Store / pinia
# Funciones
- Búsqueda a través de filter
- Navegación por ruta dinámica
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useProductStore } from '../stores/products';
const productStore = useProductStore();
const router = useRouter();
const input = ref('');
const searchResults = computed(() => {
if (!input.value || input.value.length < 2) return [];
return productStore.list.filter((item) => { // Valor en minúsculas
return item.title.toLowerCase().includes(input.value.toLowerCase());
});
});
const navigate = (id: string) => {
input.value = '';
router.push(`/product/${id}`);
};
</script>
<template>
<div class="dropdown dropdown-end">
<div class="form-control">
<input
type="text"
placeholder="Buscar..."
class="input input-ghost"
:disabled="!productStore.loaded"
v-model="input"
/>
</div>
<ul
class="shadow menu dropdown-content bg-base-100 rounded-box w-64 text-base-content overflow-y-scroll"
style="max-height: 50vh"
>
<li v-for="product in searchResults" :key="product.id">
<a
href="#"
@click.prevent="navigate(product.id)"
v-text="product.title"
></a>
</li>
</ul>
</div>
</template>