Populatate a second select based on selection a 1st select

I have two selects: one for type and one for models. I would like to populate the models select depending on the type selection.

What is the easiest way to do it? And I am kind newbie concerning ajax.

I was thinking of perhaps having only one select with groups but can I afterwards do a search on the category?

Also one of my problems is that I am doing this on the multistep form so I don’t want to reload the page. My data currently is on text files and converted it to php arrays which I used to populate the selects (check div step2a with the pc_model example).

Here is a sample of my code.

sources for types and models

$comp_model = $dir . '\Computer_model.txt'; //Liste complète
$hard_type = $dir . '\Hardware_type.txt';
$hard_model = $dir . '\Hardware_model.txt';

index.php

<form id="add_to_stock" class="form-horizontal" name="add_to_stock" method="post" action="./result.php">
        <div id="step1" class="add_mat">
            <legend>Step 1 of 4</legend>
            <div class="form-group col-sm-12">
                <label class="control-label">Type de Matériel</label> 
                <div class="radio">
                    <label><input type="radio" name="mat_type" value="computer" checked>Ordinateur</label>
                </div>
                <div class="radio">
                    <label><input type="radio" name="mat_type" value="hardware">Hardware</label>
                <div class="radio">
                    <label><input type="radio" name="mat_type" value="contract">Contrat</label>
                </div>
                <div class="form-group col-sm-12">
                    <div class="col-sm-5 pull-right">
                        <button class="btn btn-success pull-right go_to_step_2" type="button">
                            Suivant <span class="glyphicon glyphicon-chevron-right"></span>
                        </button>
                    </div>
                </div>
            </div>
        </div>
    <div id="step2a" class="add_mat">
        <legend>Step 2 of 4</legend>
        <div class="form-group col-sm-12 ">
            <h4 class="text-muted text-uppercase "><strong>Ajouter un ordinateur</strong></h4>

            <div class="alert alert-warning"><span class="glyphicon glyphicon-warning-sign"></span><strong> Attention :</strong> Il faut remplir tous les champs!</div>   

            <div class="form-group">
                <label class="col-sm-2 control-label" for="pc_model">Model :</label>
                <div class="col-sm-10">
                    <select id="pc_model" name="pc_model" class="select2 form-control" style="width: 100%">
                        <?php
                        $pc_models = file($comp_model, FILE_IGNORE_NEW_LINES);
                        foreach ($pc_models as $model) {
                          ?>
                          <option value="<?php echo $model; ?>"><?php echo $model; ?></option>;
                          <?php
                        }
                        ?>
                    </select>
                </div>
            </div>

            <div class="form-group no_margin">                                                
                <div class="col-sm-10">
                    <input id="pc_deploymentState" name="pc_deploymentState" class="form-control" type="hidden" value="Stock new">
                </div>
            </div>

            <div class="form-group no_margin">                                                
                <div class="col-sm-10">
                    <input id="pc_incidentState" name="pc_incidentState" class="form-control" type="hidden" value="Operational">
                </div>
            </div>

            <div class="form-group cont-quant">
                <label class="col-sm-2 control-label" for="pc_quantity">Quantité :</label>
                <div class="col-sm-10">
                    <input id="pc_quantity" name="pc_quantity" class="form-control quantity" type="number" min="1" max="10" value="">
                    <span class="glyphicon glyphicon-remove form-control-feedback AQ_close_icon hidden"></span>
                </div>                                            
            </div>
            <div class="col-sm-2"></div>
            <div class="col-sm-10 alert alert-danger alert-dismissible AQ hidden">
                <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
                La quantité choisi doit être entre 1 et 10.
            </div>
            <div class="form-group col-sm-12">
                <div class="col-sm-1"></div>
                <div class="col-sm-5">
                    <button class="btn btn-warning pull-left back_to_step_1" type="button">
                        <span class="glyphicon glyphicon-chevron-left"></span> Précédent 
                    </button>
                </div>
                <div class="col-sm-6">
                    <button  class="btn btn-success pull-right go_to_step_3" type="button">
                        Suivant <span class="glyphicon glyphicon-chevron-right"></span>
                    </button>
                </div>                                            
            </div>
        </div>
    </div>
    <div id="step2b" class="add_mat">

        <legend>Step 2 of 4</legend>
        <div class="form-group col-sm-12">
            <label class="control-label">Ajouter du hardware</label> 

            <div class="form-group">
                <label class="col-sm-2 control-label" for="hard_type">Type :</label>
                <div class="col-sm-10">
                    <select id="hard_type" name="hard_type" class="select2 form-control" style="width: 100%">
                        <?php
                        $hard_types = file($hard_type, FILE_IGNORE_NEW_LINES);
                        foreach ($hard_types as $hard_type) {
                          ?>
                          <option value="<?php echo $hard_type; ?>"><?php echo $hard_type; ?></option>;

                          <?php
                        }
                        ?>
                    </select>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label" for="hard_model">Model :</label>

                <div class="col-sm-10">
                    <select id="hard_model" name="hard_model" class="select2 form-control" style="width: 100%">

                        <?php
                        //if($current_type != "PCI/PCIe Card"){
                        //  $hard_model = $dir . $current_type .'.txt';
                        //}else{
                        //  $hard_mocel = $dir . 'PCI_PCIe Card.txt';
                        //}

                        $hard_models = file($hard_model, FILE_IGNORE_NEW_LINES);
                        foreach ($hard_models as $hard_model) {
                          ?>
                          <option value="<?php echo $hard_model; ?>"><?php echo $hard_model; ?></option>;
                          <?php
                        }
                        ?>
                    </select>
                </div>
            </div>

            <div class="form-group no_margin">                                                
                <div class="col-sm-10">
                    <input id="hard_deploymentState" name="hard_deploymentState" class="form-control" type="hidden" value="Stock new">
                </div>
            </div>

            <div class="form-group no_margin">                                                
                <div class="col-sm-10">
                    <input id="hard_incidentState" name="hard_incidentState" class="form-control" type="hidden" value="Operational">
                </div>
            </div>

            <div class="form-group cont-quant">
                <label class="col-sm-2 control-label" for="hard_quantity">Quantité :</label>
                <div class="col-sm-10">
                    <input id="hard_quantity" name="hard_quantity" class="form-control quantity" type="number" min="1" max="10" value="" value="">
                    <span class="glyphicon glyphicon-remove form-control-feedback AQ_close_icon hidden"></span>
                </div>

            </div>
            <div class="col-sm-2"></div>
            <div class="col-sm-10 alert alert-danger alert-dismissible hidden AQ">
                <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
                La quantité choisi doit être entre 1 et 10.
            </div>
            <div class="form-group col-sm-12">
                <div class="col-sm-1"></div>
                <div class="col-sm-5">
                    <button class="btn btn-warning pull-left back_to_step_1" type="button">
                        <span class="glyphicon glyphicon-chevron-left"></span> Précédent 
                    </button>
                </div>
                <div class="col-sm-6">
                    <button  class="btn btn-success pull-right go_to_step_3" type="button">
                        Suivant <span class="glyphicon glyphicon-chevron-right"></span>
                    </button>
                </div>                                            
            </div>
        </div>

    </div>
    <div id="step2c" class="add_mat">
        <legend>Step 2 of 4</legend>
        <div class="form-group col-sm-12">
            <label class="control-label">Contract</label> 

            <div class="form-group col-sm-12">
                <div class="col-sm-1"></div>
                <div class="col-sm-5">
                    <button class="btn btn-warning pull-left back_to_step_1" type="button">
                        <span class="glyphicon glyphicon-chevron-left"></span> Précédent 
                    </button>
                </div>
                <div class="col-sm-6">
                    <button  class="btn btn-success pull-right go_to_step_3" type="button">
                        Suivant <span class="glyphicon glyphicon-chevron-right"></span>
                    </button>
                </div>                                            
            </div>
        </div>
    </div>

    <div id="step3" class="add_mat">
        <fieldset id="add_serialNumber">
            <legend>Step 3 of 4</legend>
            <div class="form-group col-sm-12">
                <label class="control-label">Ajout des numéros de série</label>

                <div class="SN_wrapper form-group">
                    <label class="col-sm-2 control-label padding-right" for="serialNb_1">Numéro de série 1 :</label>
                    <div class="col-sm-10">
                        <input id="serialNb_1" name="serialNbs[]" class="form-control" type="text" value="">
                    </div>
                </div>

                <div class="form-group col-sm-12">
                    <div class="col-sm-1"></div>
                    <div class="col-sm-5">
                        <button class="btn btn-warning pull-left back_to_step_2" type="button">
                            <span class="glyphicon glyphicon-chevron-left"></span> Précédent 
                        </button>
                    </div>
                    <div class="col-sm-6">
                        <button  class="btn btn-success pull-right go_to_step_4" type="button">
                            Suivant <span class="glyphicon glyphicon-chevron-right"></span>
                        </button>
                    </div>
                </div>
            </div>
    </div>
    <div id="step4" class="add_mat">                                    
        <div id="recap" class="container">
            <h4 id="type_h4">Type : <span id="type"></span></h4>
            <h4 id="model_h4">Modèle : <span id="model"></span></h4>
            <h4 id="quant_h4">Quantité : <span id="quant"></span></h4>
            <div class="">
                <ul class="list-unstyled">


                </ul>
            </div>

        </div>

        <legend>Step 4 of 4</legend>

        <div class="form-group col-sm-12">                                        
            <label class="control-label">Confirmation</label>   
            <div class="form-group col-sm-12">
                <div class="col-sm-1"></div>
                <div class="col-lg-5">
                    <button class="btn btn-warning pull-left back_to_step_3" type="button">
                        <span class="glyphicon glyphicon-chevron-left"></span> Précédent 
                    </button>
                </div>
                <div class="col-lg-6">
                    <button id="add_new_mat" class="btn btn-primary pull-right" type="submit">
                        Ajouter
                    </button>
                </div>
            </div>
        </div>
    </div>          
</form>

stock.js

$(document).ready(function () {
  var id, current_div;
  $(".select2").select2();
   $(".select2").select2({
	selectOnClose: true        
    
    
});



  //évenements click des buttons "suivant"
  $(".go_to_step_2").click(function () {
      var option_val = $('input[name=mat_type]:checked').val();
      console.log(option_val);

      switch (option_val) {
          case 'computer':
              $('#step1').hide();
              $('#step2a').show();
              break;
          case 'hardware':
              $('#step1').hide();
              $('#step2b').show();
              break;
          case 'contract':
              $('#step1').hide();
              $('#step2c').show();
              break;
          default:
      }

      $(".quantity").blur(function () {
          $this = $(this).val();
          if ($this < 1 || $this > 10) {
              $(".cont-quant").addClass("has-error has-feedback");
              $(".AQ").removeClass("hidden");
              $(".AQ_close_icon").removeClass("hidden");
          } else {
              $(".cont-quant").removeClass("has-error has-feedback");
              $(".AQ").addClass("hidden");
              $(".AQ_close_icon").addClass("hidden");
          }
      });   

  });
  
  $("#hard_type").change(function(){
    var selected =  $("#hard_type").val();
    $.ajax({
      url:      'script.php',
      type:     'POST',
      data:     {'current_type': selected},
      success:  function(){
        
      }
    });
  });

  $(".go_to_step_3").click(function () {
      if ($('div').css('display') == 'block' && $('div').hasClass('add_mat')) {
          id = $(".add_mat:visible").attr('id');
      }
      console.log(id);
      current_div = "#" + id;
      console.log(current_div);

      $(current_div).hide();
      $("#step3").show();

      var option_val = $('input[name=mat_type]:checked').val();
      var quantity;
      switch (option_val) {
          case 'computer':
              quantity = $('input[name=pc_quantity]').val();
              console.log(quantity);
              break;
          case 'hardware':
              quantity = $('input[name=hard_quantity]').val();
              console.log(quantity);
              break;
          case 'contract':
              quantity = $('input[name=contr_quantity]').val();
              console.log(quantity);
              break;
          default:
      }

      console.log(quantity);
      var wrapper = $('.SN_wrapper');
      var labelField = '';
      var inputField = '';
      var initialInputID = $("input[name*='serialNbs']").attr('id');
      var x = parseInt(initialInputID.split("_")[1]);
      var y = x + 1;

      for (i = 0; i < quantity - 1; i++) {
          labelField = '<label for="serialNb_' + y + ' " class="col-sm-2 control-label padding-right">' + 'Numéro de série ' + y + ' :</label>';
          inputField = '<div class="col-sm-10"><input id="serialNb_' + y + '" name="serialNbs[]" class="form-control" type="text" value="" /></div>';

          y++;

          $(wrapper).append(labelField, inputField);

      }

  });

  $(".go_to_step_4").click(function () {

      $("#step3").hide();
      $("#step4").show();

      var option_val = $('input[name=mat_type]:checked').val();
      var quantity;
      var model;
      var type;
      switch (option_val) {
          case 'computer':
              $("#type_h4").hide();
              model = $('#pc_model option:selected').text();
              $("#model").append(model);
              quantity = $('input[name=pc_quantity]').val();
              $("#quant").append(quantity);
              console.log(model + ";" + quantity);
              break;
          case 'hardware':
              type = $('#hard_type option:selected').text();
              $("#type").append(type);
              model = $('#hard_model option:selected').text();
              $("#model").append(model);
              quantity = $('input[name=hard_quantity]').val();
              $("#quant").append(quantity);
              console.log(type + ";" + model + ";" + quantity);
              break;
          case 'contract':
              quantity = $('input[name=contr_quantity]').val();
              console.log(quantity);
              break;
          default:
      }

      console.log(type + ";" + model + ";" + quantity);
  });

  //évenements click des buttons "précédent"

  $(".back_to_step_1").click(function () {
      if ($('div').css('display') == 'block' && $('div').hasClass('add_mat')) {
          id = $(".add_mat:visible").attr('id');
      }

      console.log(id);
      current_div = "#" + id;
      console.log(current_div);

      $(current_div).hide();
      $("#step1").show();

  });

  $(".back_to_step_2").click(function () {
      var option_val = $('input[name=mat_type]:checked').val();
      console.log(option_val);

      switch (option_val) {
          case 'computer':
              $('#step3').hide();
              $('#step2a').show();
              break;
          case 'hardware':
              $('#step3').hide();
              $('#step2b').show();
              break;
          case 'contract':
              $('#step3').hide();
              $('#step2c').show();
              break;
          default:
      }
  });

  $(".back_to_step_3").click(function () {
      $("#step4").hide();
      $("#step3").show();
  });

});

If you want to populate the select#hard_model based on the user’s selection in the select#hard_type, you cannot pre-populate the select#hard_model using PHP. Instead, you should populate the select#hard_model using JavaScript, after the user has made a selection in the select#hard_type.

I think your choice of text files, which you then convert into PHP arrays, is not the best design in this situation. Instead, I think you should set up your data files in JSON format. In particular, the data file for the hardware models should be a nested JSON structure that contains all of the hardware types and models data—something like this:

{ "types": [
    {"id": "type1", "text": "Hardware type 1", "models": [
        {"id": "model1a", "text": "Hardware model 1a"},
        {"id": "model1b", "text": "Hardware model 1b"},
        ...
    ]},
    {"id": "type2", "text": "Hardware type 2", "models": [
        {"id": "model2a", "text": "Hardware model 2a"},
        {"id": "model2b", "text": "Hardware model 1b"},
        ...
    ]},
    ...
]}

You can load this file in JavaScript (using $.ajax(), and set the “dataType” setting to “json”, to automatically parse your JSON data into a JavaScript object. That object’s “types” property is in the array format that Select2 expects, so you could use this property to initialize the select#hard_type Select2 (by passing it as the value of the “data” initialization option). (Note that you will need to initialize these two selectss individually, because you need to pass data to the initializer for the “hard_type” Select2, but you don’t want to pass any data for the “hard_model” Select2.)

Next, you need to load the associated model data into the select#hard_model Select2 when the user selects a hardware type. The “best” way to do that is to attach an event handler for the “select2:select” event to the select#hard_type:
$('#hard_type').on('select2:select', function(event) { ... });.
That event handler will receive the data value for the selected item, and that value will include the model data that is associated to the selected hardware type. Your event handler can then update the select#hard_model Select2 by:

  1. removing any existing option elements,
  2. inserting new option elements for all the associated models, and
  3. triggering the “change” or “change.select2” event to notify the select#hard_model Select2 that its data has changed.
1 Like

Hi John, thank you for your reply, I will try to do as you suggested.

I know the text files are not the best option, but I tried to find the easiest way for anyone to be able to add a new type or a new model when needed (it doesn’t happen often) since I don’t have access to the database. If I use the tagging feature, for instance, how can I update the JSON file then?

I was trying to do what you told me but I am having difficulty in understanding this step (I am really new to Ajax). So what I have now is:

       $("#hard_type").on("change", function (){
    var selected =  $("#hard_type").val();        
    console.log(selected);
   
    $.ajax({
      url:      '/Stock/json/stock.json',
      type:     'get',
      dataType: 'json',
      processResults: function (data) {
          return {results: data.types};
      }
    });
  }).on("select2:select", function(){
    $("#hard_model").select2({
      ajax: {
        url:      "/Stock/json/stock.json",
        type:     "get",
        dataType: "json'",
        processResults: function (data) {
          return {results: data.models};
        }
      }        
  });
});

Your original post did not mention that you needed to update the data as well. In any case, you should be able to write PHP code that would update the JSON data file, since PHP can work with JSON data. Your server-side PHP script would:

  1. Read in the existing data file.
  2. Parse it into a PHP object (json_decode)
  3. Use the selected value of select#hard_type and select#hard_model to insert values into the object.
  4. Create a string representation of the updated object (json_encode).
  5. Write the updated string back to the data file.

However, if that is too complicated for you, then I would recommend that you keep a separate model data file for each type (for example, “./hardware_models_type1.txt”, “./hardware_models_type2.txt”, etc.). That way you can still dynamically load the select#hard_model based on the user’s selection in select#hard_type.

I do not recommend using the built-in AJAX feature of the Select2 widget. Select2’s AJAX capability expects the data source you specify to filter the data according to what the user enters in the search field. Also, the user will expect that the items shown in the Select2 widget match what they have typed, but since your data source isn’t doing any filtering, the results list will show all the items, which will confuse your users.

Instead, I recommend that you load the data for each Select2 using either jQuery’s $.ajax() method or JavaScript’s fetch() method, which doesn’t expect your back end to perform any logic, and use this data to initialize your Select2. That will allow the Select2 to filter all of the (in memory) data when the user types in the search field. Something like this:

// Initialize the hardware types Select2:
$.ajax('./hardware_types.txt', {
    dataType: 'text',
    success: function(text) {
        // Note: this assumes the data in hardware_types.txt is in the following format:
        // typeId1:typeName1
        // typeId2:typeName2
        // etc.
        // If your data is in a different format, then adjust the logic below to match it.
        // The goal is to parse your data into a JavaScript array containing objects
        // of the format { "id": "id_value", "text": "display_value"}.
        var data = text.split("\n").map(function(item) {
            var typeIdName= item.split(':');
            return {'id': typeIdName[0], 'text': typeIdName[1]};
        });
        $('#hard_type').select2({
            'data': data,
            'tags': true,
            // Any other configuration options you want.
        });
    }
});

You would use similar code to initialize the select#hard_model Select2.

1 Like

Thanks a lot for your help. I used the option with a file of models per type. I just still have one question… how can I clear the options of the second select before adding the new ones?

    $.ajax('./sources/hardware_types.txt', {
    dataType: 'text',
    success: function(text) {
       
        var data = text.split("\n").map(function(item) {
            var typeIdName= item.split(':');
            return {'id': typeIdName[0], 'text': typeIdName[1]};
        });
        console.log(data);
        $('#hard_type').select2({
            'data': data,
            'tags': true,
            placeholder: "Seleccionez un type",
            allowClear: true
        });
    }
  });
  
  $("#hard_type").on("change", function (){
     var selectedType =  $("#hard_type").val();        
     console.log(selectedType);
     
     $.ajax('./sources/hardware_models_'+ selectedType +'.txt', {
      dataType: 'text',
      success: function(text) {
       
        var data = text.split("\n").map(function(item) {
            var typeIdName= item.split(':');
            return {'id': typeIdName[0], 'text': typeIdName[1]};
        });
        console.log(data);
        $('#hard_model').select2({
            'data': data,
            'tags': true,
            placeholder: "Seleccionez un modèle",
            allowClear: true
        });
    }
  });

I found a way, I am not sure if it is the proper one though. I added before the $.ajax the line :
$('#hard_model').html('').select2({data: [{id: '', text: ''}]});

That’s a reasonable solution, although it does re-initialize the Select2, and it’s possible you’ll see the “plain” HTML select flash on the screen when the re-initialization happens.

Another approach would be to clear the HTML as you’re already doing, but then trigger the change event on the select. That will notify the Select2 that its data has changed. You can then reload the new data into the select and trigger the change event again.

But if your solution is working well for you, I think you should use it.

1 Like