Using one select to control another select using SilverStripe and jQuery

Posted by Gav on 27 August 2010 | 1 Comments

Tags: ,

In this blog post, I'll show you how to use SilverStripe and jQuery to use one select to control another select. If you want to check out the demo, have a look at this page.

Step 1: First create some data objects

I am just using a CarMake DataObject and a CarModel DataObject to demonstrate this. First create the mysite/code/CarMake.php file with the following code.

<?php
class CarMake extends DataObject {
	static $db = array(
		'Title' => 'Varchar(50)'
	);
	static $has_many = array(
		'CarModels' => 'CarModel'
	);
}

Next create the mysite/code/CarModel.php file with the following code.

<?php
class CarModel extends DataObject {
	static $db = array(
		'Title' => 'Varchar(50)'
	);
	static $has_one = array(
		'CarMake' => 'CarMake'
	);
}

The relationship between CarMake and CarModel is a one 2 many relationship. This is easily achieved using SilverStripe.

To add a simple administration interface, create the mysite/code/CarModelAdmin.php file with the following code.

<?php
class CarModelAdmin extends ModelAdmin {
	static $managed_models = array(
		'CarMake',
		'CarModel'
	);

	static $url_segment = 'cars';
	static $menu_title = 'Cars';
}

Now lets build the database by visiting http://localhost/dev/build/?flush=1

Instead of entering a whole heap of data, i have upload a file which has the mysql dump of the CarMake and CarModel tables below:
MySQL dump for the CarMake and CarModel tables

Step 2: Create a page type which has the form

Now we are to create the mysite/code/CarPage.php file. There is quite a lot going on on this page. We need to create the following:

  • 1. We need to include the jQuery library from the sapphire thirdparty directory.
  • 2. We need to write some jQuery code which will fire on the onchange event of the CarModel select element.
  • 3. We need to create the form.
  • 4. And we also need to modify the index function on the CarPage_Controller so that if a request is to update the values in the CarModels we only want to render the correct content instead of the entire page. We are going to use a new Layout file for this.

This is my attempt to try and explain the code

1: We need to include the jQuery library from the sapphire thirdparty directory

This is done on line 21 using the Requirements::javascript call. There is a javascript file called jquery.js within the sapphire/thirdparty/jquery/ directory.

Requirements::javascript('sapphire/thirdparty/jquery/jquery.js');

2. We need to write some jQuery code which will fire on the onchange event of the CarModel select element.

The next step is to write the jQuery code which will populate the CarMake select element. This code is from lines 23-49. We are going to use JSON to get the data. We also use Requirements::customScript to add this code to the document. We also need to wrap the code around an anonymous javascript function by using (function($) { and })(jQuery);

Requirements::customScript("
	(function($) {
		$(document).ready(function() {
			$('select#Form_CarForm_CarMakeID').change(function() {

				var location = window.location.href;

				var select = $('select#Form_CarForm_CarModelID');
				var options = select.attr('options');
				$('option', select).remove();
				options[options.length] = new Option('Loading....', '');
				select.attr('disabled', 'disabled');

				$.getJSON(location, {updatecarmodelddl: 1, CarMakeID: $(this).val()}, function(data){

					$('option', select).remove();
					$.each(data, function(index, array) {
						options[options.length] = new Option(array['optionDisplay'], array['optionValue']);
					});
					select.removeAttr('disabled');

				});
			});
		});
	})(jQuery);

");

3. We need to create the form.

This is done from lines 51 - 65. This is a pretty stardard fieldset for the fields and actions. We are creating 2 dropdownfields. The first one will source the values from the CarMake DataObject and the 2 one will just contain a message notifying the users to select a make first.

$fields = new FieldSet(
	new DropdownField('CarMakeID', 'Car make', DataObject::get('CarMake', '', 'Title ASC')->toDropdownMap('ID', 'Title'), '', '', 'Please select a car make'),
	new DropdownField('CarModelID', 'Car model', array('' => 'Please select a make first'))
);

$actions = new FieldSet(
	new FormAction('CarFormAction', 'Submit')
);

return new Form($this, 'CarForm', $fields, $actions);

4. And we also need to modify the index function on the CarPage_Controller so that if a request is to update the values in the CarModels we only want to render the correct content instead of the entire page. We are going to use a new Layout file for this.

This is from lines 10-16. What we are doing here is changing the content which is sent back to the browser. When we use the renderWith command, we are going to use that template to render the content with. If we use Array() we are using the default template which will be CarPage.ss in this case.

function index() {
	if (isset($_REQUEST['updatecarmodelddl'])) {
		return $this->renderWith('CarModelDDL');
	} else {
		return Array();
	}
}

The full code for mysite/code/CarPage.php is as follows

<?php
class CarPage extends Page {
	static $db = array();
	static $has_one = array();
}

class CarPage_Controller extends Page_Controller {


	function index() {
		if (isset($_REQUEST['updatecarmodelddl'])) {
			return $this->renderWith('CarModelDDL');
		} else {
			return Array();
		}
	}

	function CarForm() {

		//include the jQuery lib.
		Requirements::javascript('sapphire/thirdparty/jquery/jquery.js');

		Requirements::customScript("
			(function($) {
				$(document).ready(function() {
					$('select#Form_CarForm_CarMakeID').change(function() {

						var location = window.location.href;

						var select = $('select#Form_CarForm_CarModelID');
						var options = select.attr('options');
						$('option', select).remove();
						options[options.length] = new Option('Loading....', '');
						select.attr('disabled', 'disabled');

						$.getJSON(location, {updatecarmodelddl: 1, CarMakeID: $(this).val()}, function(data){

							$('option', select).remove();
							$.each(data, function(index, array) {
								options[options.length] = new Option(array['optionDisplay'], array['optionValue']);
							});
							select.removeAttr('disabled');

						});
					});
				});
			})(jQuery);

		");

		$fields = new FieldSet(
			new DropdownField('CarMakeID', 'Car make', DataObject::get('CarMake', '', 'Title ASC')->toDropdownMap('ID', 'Title'), '', '', 'Please select a car make'),
			new DropdownField('CarModelID', 'Car model', array('' => 'Please select a make first'))
		);

		$actions = new FieldSet(
			new FormAction('CarFormAction', 'Submit')
		);

		return new Form($this, 'CarForm', $fields, $actions);
	}

	function CarFormAction($data, $form) {

	}

	function CarModelsJSON() {
		if (isset($_REQUEST['updatecarmodelddl']) && isset($_REQUEST['CarMakeID'])) {
			return DataObject::get('CarModel', 'CarMakeID=' . $_REQUEST['CarMakeID']);
		}
	}

}

We also need to create the themes/<theme name>/Includes/CarModelDDL.ss. This is the content which will be loaded into the select via javascript.

[{"optionValue":"", "optionDisplay": "Please select"}

<% if CarModelsJSON %>
	<% control CarModelsJSON %>,{"optionValue":$ID, "optionDisplay": "$Title.JS"}<% end_control %>
<% end_if %>

]

Post your comment

Comments

sergieboy says:

Forget my previous posted comment. It works. I love you code/example, since I was looking days for it ! Ever thought about integrating JTable JQUERY plugin into Silverstripe ? I'm working on it but since I'm fairly new to SS, it's hard for me.

Posted by sergieboy, 30 December 2011 (3 years ago)

RSS feed for comments on this page | RSS feed for all comments