Laravel

Select dinámicos en Laravel solo con JS

6 abril, 2025
19,232 lecturas
¿Cómo crear select dinámicos en Laravel de tres niveles con JS?

Etiquetas:

#Selects Dependientes

¿Cómo implementar select dependientes en Laravel?

En este caso programaremos un ejemplo de Selects dependientes de tres niveles con la API Fetch de JS y Laravel. Para ello, antes se usaba comunmente Jquery, hoy en dia contamos con una solución nativa que requiere de menos líneas de código y la mayoría de navegadores modernos ya lo soportan. Entonces:

  • Solo usaremos Javascript donde vamos a consumir la API Fetch para inflar los select dinámicamente.
  • El framework Laravel  - PHP para el desarrollo de aplicaciones web
  • HTML y CSS para maquetar es decir aplicar diseño a la vista.

 

Estructura de la Base de Datos

Asumiremos que tienes las tablas:

  • categories (id, name)
  • subcategories (id, category_id, name)
  • products (id, subcategory_id, name)

Modelos y Relaciones

Define las relaciones en los modelos:

php

// Category.php
public function subcategories() {
return $this->hasMany(Subcategory::class);
}


// Subcategory.php
public function category() {
return $this->belongsTo(Category::class);
}
public function products() {
return $this->hasMany(Product::class);
}


// Product.php
public function subcategory() {
return $this->belongsTo(Subcategory::class);
}

Rutas (routes/web.php)

Crea rutas: web y para las peticiones AJAX:

php

Route::get('categories', [FrontController::class,'getCategories']);

Route::get('/subcategories/{category}', [FrontController::class, 'getSubcategories']);

Route::get('/products/{subcategory}', [FrontController::class, 'getProducts']);

Controladores

Crea los métodos para devolver datos en formato JSON:

php

public function getCategories(){
        $categories = Category::all();
        return view('categoria', compact("categories"));
    }

    public function getSubcategories(Category $category){
        return response()->json($category->subcategories);
    }

    public function getProducts(Subcategory $subcategory) {
        return response()->json($subcategory->products);
    }

Vista Blade (HTML + JavaScript)

Crea el formulario con selects dependientes:

blade

<h1 class="text-3xl underline p-5 text-center">SELECT DEPENDIENTES</h1>

<div class="flex justify-center">
    <div class="grid grid-cols-3 w-3/4 gap-10">
        <select id="category" class="border rounded-lg p-2">
            <option value="">Selecciona una categoría</option>
            @foreach ($categories as $category)
            <option value="{{ $category->id }}">{{ $category->name }}</option>
            @endforeach
        </select>
    
    
        <select id="subcategory" class="border rounded-lg p-2" disabled>
            <option value="">Selecciona una subcategoría</option>
        </select>
    
        <select id="product" class="border rounded-lg p-2" disabled>
            <option value="">Selecciona un producto</option>
        </select>
    </div>
</div>

JavaScript (Vanilla JS)

Agrega el script para manejar las dependencias:

javascript

document.getElementById('category').addEventListener('change', function() {
    const categoryId = this.value;
    const subcategorySelect = document.getElementById('subcategory');
    const productSelect = document.getElementById('product');
    
    // Resetear selects
    subcategorySelect.innerHTML = '<option value="">Selecciona una subcategoría</option>';
    subcategorySelect.disabled = true;
    productSelect.innerHTML = '<option value="">Selecciona un producto</option>';
    productSelect.disabled = true;

    if (categoryId) {
        // Cargar subcategorías
        fetch(`/subcategories/${categoryId}`)
            .then(response => response.json())
            .then(data => {
                subcategorySelect.innerHTML = data.map(item =>
                `<option value="${item.id}">${item.name}</option>`
            ).join('');
            subcategorySelect.disabled = false;
        });
    }    

})

document.getElementById('subcategory').addEventListener('change', function() {
    const subcategoryId = this.value;
    const productSelect = document.getElementById('product');

    // Resetear productos
    productSelect.innerHTML = '<option value="">Selecciona un producto</option>';
    productSelect.disabled = true;
    if (subcategoryId) {
        // Cargar productos
        fetch(`/products/${subcategoryId}`)
        .then(response => response.json())
        .then(data => {
            productSelect.innerHTML = data.map(item =>
            `<option value="${item.id}">${item.name}</option>`
            ).join('');
            productSelect.disabled = false;
        });
    }
});document.getElementById('category').addEventListener('change', function() {
    const categoryId = this.value;
    const subcategorySelect = document.getElementById('subcategory');
    const productSelect = document.getElementById('product');
    
    // Resetear selects
    subcategorySelect.innerHTML = '<option value="">Selecciona una subcategoría</option>';
    subcategorySelect.disabled = true;
    productSelect.innerHTML = '<option value="">Selecciona un producto</option>';
    productSelect.disabled = true;

    if (categoryId) {
        // Cargar subcategorías
        fetch(`/subcategories/${categoryId}`)
            .then(response => response.json())
            .then(data => {
                subcategorySelect.innerHTML = data.map(item =>
                `<option value="${item.id}">${item.name}</option>`
            ).join('');
            subcategorySelect.disabled = false;
        });
    }    

})

document.getElementById('subcategory').addEventListener('change', function() {
    const subcategoryId = this.value;
    const productSelect = document.getElementById('product');

    // Resetear productos
    productSelect.innerHTML = '<option value="">Selecciona un producto</option>';
    productSelect.disabled = true;
    if (subcategoryId) {
        // Cargar productos
        fetch(`/products/${subcategoryId}`)
        .then(response => response.json())
        .then(data => {
            productSelect.innerHTML = data.map(item =>
            `<option value="${item.id}">${item.name}</option>`
            ).join('');
            productSelect.disabled = false;
        });
    }
});document.getElementById('category').addEventListener('change', function() {
    const categoryId = this.value;
    const subcategorySelect = document.getElementById('subcategory');
    const productSelect = document.getElementById('product');
    
    // Resetear selects
    subcategorySelect.innerHTML = '<option value="">Selecciona una subcategoría</option>';
    subcategorySelect.disabled = true;
    productSelect.innerHTML = '<option value="">Selecciona un producto</option>';
    productSelect.disabled = true;

    if (categoryId) {
        // Cargar subcategorías
        fetch(`/subcategories/${categoryId}`)
            .then(response => response.json())
            .then(data => {
                subcategorySelect.innerHTML = data.map(item =>
                `<option value="${item.id}">${item.name}</option>`
            ).join('');
            subcategorySelect.disabled = false;
        });
    }    

})

document.getElementById('subcategory').addEventListener('change', function() {
    const subcategoryId = this.value;
    const productSelect = document.getElementById('product');

    // Resetear productos
    productSelect.innerHTML = '<option value="">Selecciona un producto</option>';
    productSelect.disabled = true;
    if (subcategoryId) {
        // Cargar productos
        fetch(`/products/${subcategoryId}`)
        .then(response => response.json())
        .then(data => {
            productSelect.innerHTML = data.map(item =>
            `<option value="${item.id}">${item.name}</option>`
            ).join('');
            productSelect.disabled = false;
        });
    }
});

 

¿Cómo funciona?

  1. Al seleccionar una categoría , se hace una petición GET a /subcategories/{category} para obtener las subcategorías asociadas.
  2. Al seleccionar una subcategoría , se hace una petición GET a /products/{subcategory} para obtener los productos.
  3. Los selects se habilitan/deshabilitan según la selección anterior.

Tutoriales Recomendados

Sigue explorando publicaciones de la misma categoría

Ver todos