Dependent Dropdown List with jquery in Laravel 5.1

December 6, 2015 by Bill Keck

Dependent Dropdown List with jquery in Laravel 5.1*

In this tutorial, we are going to do a simple, straightforward dependent dropdown list using jQuery and Laravel 5.1. This tutorial is oriented toward beginners, so there will be a lot of granular detail, gists are provided for views. Intermediate programmers can skip ahead.

Also note I heavily referenced this video for the jquery part. Even though the video is written for Laravel 4.2, I was able to use the jquery part almost exactly.

To make our example as useful as possible, we are going to use category and subcategory as our parent/child relationship. We are going to use both a Category model and a Subcategory model, so if you wish to follow along, create those models and migrations now.

You can customize your data structure however you wish to suit your needs, but to follow my example, you have to have an autoincrement id and a category_name column for the categories table, and for the subcategories table, you will need an autoincrement id, a subcategory_name column, and a category_id column.

For your convenience, I will provide the up and down functions for use in a migration:



public function up()
{
   Schema::create('categories', function (Blueprint $table) {
       $table->increments('id');
       $table->string('category_name')->unique();
       $table->timestamps();
   });

   Schema::create('subcategories', function (Blueprint $table) {
       $table->increments('id');
       $table->string('subcategory_name')->unique();
       $table->integer('category_id')->unsigned();
       $table->timestamps();
   });
}

public function down()
{

   Schema::drop('categories');
   Schema::drop('subcategories');
}

If you need a refresher on that or are unfamiliar with migrations, see the Laravel docs.

A lot of developers do not use names like category_name and will simply create a name column, but I prefer to make column names as unambiguous as possible. You can see with just two tables, if they both have a name column, we can get confused quickly about which name column we are referring to. On the other hand, category_name is crystal clear.

Ok, let’s assume you have your category model and your subcategory model setup and that you have populated the tables with a few records in each. Remember to make sure the records in the subcategories table have values in their category_id columns.

Typically you will have some other model that is referencing Category and Subcategory, so for example, let’s use our Widget model from the earlier tutorials.

We could have a create view that has a form for creating a widget. If you have been following my other tutorials, the widget create view looks something like this:

Widget Create view

I’m providing a gist for the view code because wordpress isn’t playing nice with my code and doesn’t escape the html properly.

Following along in the gist, you may or may not have a master page, but I will assume that you do. Rename it appropriately if you are using a different name.

You can see I’m extracting my errors out to a partial, so don’t include that if you are not using it. The breadcrumbs helper is from the Bootstrapper package.

Take that out if you are not using it. Also note that i’m using the Collective HTML form helper class, which is a package that you need to pull in if you want to use it as I have in our example. Otherwise go the straight html/bootstrap route.

I’m showing all this just for reference and context, you actually don’t need any of that if you don’t want it and just want the drop down dependency part.

You can see that create widget view is super simple, just one text input for widget_name. It’s very easy to imagine that we would want to add our category and subcategory to our form.

If you are following along and you want to actually build the widget example, we’re assuming you already have the widget model. Then you need to run a migration that uses the up and down methods below to add the following to your widget model:



public function up()
{
   Schema::table('widgets', function(Blueprint $table)
   {

       $table->integer('category_id')->unsigned()->after('widget_name');
       $table->integer(subcategory_id')->unsigned()->after('category_id');


   });
}

public function down()
{
   Schema::table('products', function(Blueprint $table)
   {

       $table->dropColumn('category_id');
       $table->dropColumn('subcategory_id');
      

   });
}

Don’ forget to add to your $fillable array on your widget model, so you can mass assign:



protected $fillable = ['widget_name',
                        'category_id',
                        'subcategory_id'];

So obviously now, if we want to be able to pull in categories into our widget create view, we will have to modify the widget controller to send the list of categories to the view. Let’s do it like so:



public function create()
{

   $categories = Category::orderBy('category_name', 'asc')->get();


   return view('widget.create', compact('categories'));
}

Obviously, you will need pull in the Category model:



use App\Category:

So the eloquent call will give us the list of categories in alphabetical order. We don’t need to send subcategories to the view because we are going to get them via an ajax call.

Now we’re ready to add the following to our widget create form, which I will provide via gist:

Categories

You can see we are using a simple foreach loop on $categories to give us our option values and names for our select:





  @foreach($categories as $category)

<option value="{{ $category->id }}"> {{$category->category_name}}</option>

           @endforeach

So you can see this will give us a select option for each record we have in the database. I added the first select option for Select Category that comes before the foreach loop.



<option value="">Select Category</option>

Also note that we gave the select element a name of category_id and an id of category_id. Obviously I’m doing that because that is the column name that we have on the widget model, where we will be saving it.

The next block we have the select with a name of subcategory_id and and id of subcategory_id. In the option that is listed, it says, “First Select Category,” which is the behavior we want, since this list is empty until we select the category. Here is the gist.

As I mentioned, we are going to be using ajax to retrieve the subcategory data, so let’s set up the route:



Route::get('api/category-dropdown', 'ApiController@categoryDropDownData');

In this case, you can see I have created an ApiController and a method on that controller named categoryDropDownData. I’ve seen a lot of examples from Laravel 4.2 with routes that have eloquent calls in them and I decided not to go in that direction for a few of reasons.

First, I want to keep my route files as simple as possible.

Second, since the routes file is a file and not a class, I’m not sure how to pull in the things I would need.

Third, I have more than one ajax call in my application and have found that having an ApiController makes it easy to keep them organized. For each ajax call I need, I just add another method to the controller. If I want to put middleware on the ajax calls to authorize them, I can easily do it through the constructor.

So go ahead and run artisan to make the controller:



php artisan make:controller ApiController

On our ApiController, we will need the following use statements:



namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Input;
use App\Subcategory;

Here is what my categoryDropDownData method looks like:



public function categoryDropDownData()
{

   $cat_id = Input::get('cat_id');


   $subcategories = Subcategory::where('category_id', '=', $cat_id)
                  ->orderBy('subcategory_name', 'asc')
                  ->get();

   return Response::json($subcategories);


}

So we use the cat_id get variable, which we will hand in via ajax to find the associated subcategories.

It should be fairly apparent from this method that when you create subcategory records, which we assume you have already done at this point, that you associated them to the appropriate category record by filling in the category_id column on the subcategory records.

We will also order our results by subcategory_name, so we get a nice alphabetical order to our subcategories.

Also note that we used Response::json to get exactly the format we need.

So now all we need to make this work is a bit of jquery.

Normally, what I do is create an @yield(‘scripts’) line on my master page, so that I can put an @section(‘scripts’) at the bottom of my view. The reason I do this is that I put the @yield(‘scripts’) after my call to jquery, so whenever I use jquery, I already have jquery loaded. Hopefully this makes sense.

If you are unfamiliar with master pages, check out my tutorial on master page before going on. In any case, make sure that you have jquery pulled in before calling the following script or it won’t work.

One more thing to add to the master page. We won’t be able to do the ajax call without a token, so we need to add the following in the meta tag section of the section:



<meta name="csrf-token" content="{{ csrf_token() }}">

Ok, we are finally ready for the jquery script that will make this work. I provided the gist in that link because wordrpess is chopping out my script tags, but otherwise it is correct:




@section('scripts')



   $('#category_id').on('change', function(e){

    var cat_id = e.target.value;

       //ajax

       $.get('/api/category-dropdown?cat+id=' + cat_id, function(data){

           //success data
           $('#subcategory_id').empty();

           $('#subcategory_id').append(' Please choose one');

           $.each(data, function(index, subcatObj){

               $('#subcategory_id').append(''
               + subcatObj.subcategory_name + '</option');


           });



       });


   });




@endsection

Ok, so in the first part, we grab the element category_id and use an on change event. We set our variable cat_id to the event target value:



$('#category_id').on('change', function(e){

    var cat_id = e.target.value;

So when the category is selected, it is held in the cat_id variable.

Next we have our ajax call:



 $.get('/api/category-dropdown?cat+id=' + cat_id, function(data){

You can see we are using our route and appending a get variable, in this case, cat_id, which is holding the id of the associated category.

The ajax call returns the data via (data) that you see in the function. So then we tell it to do 3 things:

First we clear the options:



$('#subcategory_id').empty();

This gets rid of the original instruction to first select category. Then we append in the new instructions:



$('#subcategory_id').append(' Please choose one');

Finally, use jquery’s each method to iterate over the data and append in the select options to the selected element, subcategory_id:



$.each(data, function(index, subcatObj){

               $('#subcategory_id').append(''
               + subcatObj.subcategory_name + '</option');


           });

I was curious about how the each method in jquery works, so I looked it up here.

The method uses the indexes and object that you return from the data to iterate over. In this case we are calling the object holding the results from the data subcatObj. So then we have access to subcatObj.id and subcatObj.subcategory_name.

And that is pretty much it. There are probably a lot of ways to accomplish a dependent dropdown, but I felt like this approach, especially on the jquery side, was very clear and easy to follow.

I hope you have enjoyed this tutorial and found it useful. Click on the sprocket icon at the top of the page to see all tutorials. Please comment, share, and like if you can, thanks.

I don’t have a donate button, but If you would like to support my work and learn more about Laravel, you can do so by buying one of my books, Laraboot: laravel 5* For Beginners, I really appreciate it.

17 thoughts on “Dependent Dropdown List with jquery in Laravel 5.1

  1. khande1n says:

    Hello Bill,

    I have followed as mentioned in the tutorial, but dependent dropdown list is not working as intented.
    Basically in my project, its kind of working till $(‘#subject_id’).empty(); only.

    The query run in the background while selecting the first dropdown is
    select * from `subjects` where `grade_id` = ’29’ order by `subject` asc

    Please guide.

    Like

  2. khande1n says:

    $.get(‘/api/category-dropdown?gra+id=’ + gra_id, function(data){

    //success data
    $(‘#subject_id’).empty();
    console.log(‘EMPTY DONE’);

    $(‘#subject_id’).append(“Please choose one”);
    console.log(‘Please choose DONE’);

    $.each(data, function(index, subjectObj){
    $(‘#subject_id’).append(”
    + subjectObj.subject + ‘</option');
    });
    console.log('DONE');

    });

    Now, what is the message in the console is:
    EMPTY DONE
    Please chose DONE
    ———————–

    Therefore the issue is in the last part, where the subject list append has to take place.

    Also, after emptying the subject list in first part, "Please choose one" does not appear in the dropdown.

    Thanks in advance.

    Like

  3. khande1n says:

    Hey, found the solution.

    In the last each function I had to Json.parse my data as jquery in function ony works on objects, not strings.

    $.each(JSON.parse(data), function(index, subjectObj)

    And its Done!!! 🙂

    Like

  4. Lee says:

    Great tutorial. Thanks!

    I was wondering if there is any particular reason that you didn’t use a relationship between category and subcategory?

    I updated your example slightly and added a hasMany relationship on the Category model for Subcategory. I also added a belongsTo relationship to Subcategory on Category.

    Then in the ApiController i updated the categoryDropDownData to be:

    $category_id = Input::get(‘category_id’);
    $subcategories = Category::find($category_id)->subcategory()->get();
    return Response::json($subcategories);

    I am still learning Laravel so wondered if there was any benefits for or against using that method.

    Like

  5. Alex says:

    Hi. Thank you for this nice tutorial. My forms are working fine. Just that I don’t know how to keep the selected values after a validation fail. Tried to setup the validation in laravel controller, but after googling, seems that the validation has to be done inside the script.
    Any help will be very much appreciated.

    Like

Leave a comment