
UPDATE: I’ve updated this post with a more comprehensive walkthrough, more code examples, and a downloadable archive that contains all the relevant files. It’s located here: More AJAX Dropdowns with CodeIgniter
CodeIgniter is my first choice for PHP rapid development frameworks. It’s lightweight, powerful, and easy to use. However there are a few common tasks that are not documented as well as they could be. For instance, populating the options in a dropdown select field using AJAX is certainly possible, but you’ll search long and hard before finding a good tutorial or a recipe for actually doing it.
While creating a dynamic form for a client’s project database, I had to figure out how to query the database using AJAX and populate several form fields based on the results of that query. So, for posterity’s sake, I’ve decided to document exactly how I accomplished it. First of all, this method uses CodeIgniter version 1.7.3. For the AJAX functionality, I use jQuery version 1.4.2. The database is MySQL with InnoDB tables.
Now, the application that I’m discussing here is basically a project management app with CRM functions. For this specific form, the user has to assign a “Client” to a project. Based on which Client is assigned, the user can choose a “Contact” for the project. So, the initial form is loaded with all “Clients” who already exist in the database (A future post will detail how I handle dynamically adding a new Client from the form). When the user chooses a Client from the “Client” dropdown, an AJAX request is made to the CodeIgniter controller to retrieve all of the contacts associated with that Client. The AJAX response is then used to populate the “Contacts” dropdown.
The form is created using the CodeIgniter Form Helper. The fields are set up for the form helper as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //attribute arrays for CodeIgniter form helper $clientListOptions = array('UNASSIGNED'); $clientList = $this->firm_model->getClients(); if($clientList != "Database Error"){ foreach($clientList as $key){ $clientListOptions[$key['idFirm']] = $key['FirmName']; } }else $clientListOptions['NA'] = "No Options"; // contactListOptions is set up with all client contacts. When the client dropdown changes, the contact // dropdown changes to hold only the contacts associated with that client. $contactListOptions = array('UNASSIGNED'); $contactList = $this->user_model->getClients(); if($contactList != "Database Error"){ foreach($contactList as $key){ $contactListOptions[$key['idContacts']] = $key['Contacts_First_Name'] . " " . $key['Contacts_Last_Name']; } }else $contactListOptions['NA'] = "No Options"; |
Once the field attributes are set, the fields themselves are created like so:
1 2 3 4 5 6 7 8 | echo "<div class='littlebox'>"; echo form_label('Client', 'FK_Client'); echo form_dropdown('FK_Client', $clientListOptions, isset($jobData->FK_Client) ? $jobData->FK_Client : $this->input->post('FK_Client'), $readonly == TRUE ? 'disabled class= med id=clientDrop' : 'class= med id=clientDrop'); echo "</div>"; echo "<div class='littlebox'>"; echo form_label('Client Contact', 'FK_Client_Contact'); echo form_dropdown('FK_Client_Contact', $contactListOptions, isset($jobData->FK_Client_Contact) ? $jobData->FK_Client_Contact : $this->input->post('FK_Client_Contact'), $readonly == TRUE ? 'disabled class= med id=contactDrop' : 'class= med id=contactDrop'); echo "</div>"; |
Each field is contained in a div for styling purposes, but it certainly isn’t required for this technique.
Now in the controller:
1 2 3 4 5 6 | function getClientContacts(){ $this->load->model('firm_model'); $clientId = $this->input->post('client'); $contacts = $this->firm_model->getContactsByFirm($clientId); echo json_encode($contacts); } |
You’ll notice that the controller function simply calls a method from the model, and JSONencodes the models response. The method in the model that actually does the heavy lifting is this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function getContactsByFirm($clientId) { if($clientId){ $this->db->select('idContacts, Contacts_First_Name, Contacts_Last_Name'); $this->db->from('Contacts'); $this->db->where("Contacts.FK_Contacts_Firm = " . $clientId); $query = $this->db->get(); if ($query->num_rows() > 0) { return $query->result(); }else{ return 'Database Error'; } } else return 'No User Found'; } |
So essentially at this point we have a form with 2 dropdown select’s. We have a method in the controller that receives the “id” of the client and passes that to the model which has a method to retrieve the contact list based on the id of the client. All that’s lacking at this point is the javascript to actually populate the 2nd dropdown with the contact data. Here it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $(function() { $('#clientDrop').change(function() { $.ajax({ type: "POST", url: "<?= base_url()?>index.php/assignment/getClientContacts", data: "client="+$('#clientDrop').val(), dataType: 'json', success: function(data){ $('#contactDrop').empty(); var unassigned = $('#contactDrop').attr('options'); unassigned[0] = new Option('UNASSIGNED', 0, true, true); for (var i = 0; i < data.length; i++) { var options = $('#contactDrop').attr('options'); options[options.length] = new Option(data[i].Contacts_First_Name + " " + data[i].Contacts_Last_Name, data[i].idContacts, true, true); } } }); }); }); |
What happens in the jQuery here is that we create an ajax call to the controller method, /assignment/getClientContacts. That method calls the model method firm_model->getContactsByFirm($clientId), and echo’s the JSON encoded data back to the jQuery success function. The success function first empties the option list for the contacts dropdown, then it loops through the JSON object and adds an option for each member. That’s essentially it. You now have an AJAX powered, CodeIgniter backed process to dynamically populate a form dropdown based on a database query.
One final gotcha. CodeIgniter has a debugging method that outputs the result of all the queries on a page. It’s enabled like this:
1 | $this->output->enable_profiler(TRUE); |
When this is enabled, for some reason, the jQuery success function tries to read the PHP output from the profiler, and that breaks the whole mess. I haven’t really looked into what’s actually happening there, but it seems that the profiler simply echo’s the PHP output along with whatever else it’s outputting. The jQuery success function attempts to load the PHP profiler output right along with the JSON encoded result, and the result is madness.