To answer my own question...
I created a custom form field based on the 'SQL' type joomla standard form field.
In the custom form field I added the jQuery/AJAX that once this parent select field is changed, takes the selected value, add's it to an ajax $_GET request and calls a task in the sub-controller. The subcontroller then gets the selected value from jinput and passes it to a method in the model that uses it to return JSON data for the child field.
I didn't know it at the time of asking but Joomla uses the jQuery Chosen plugin to hide the bog standard html select lists and create nice live searchable drop down lists in their place. Also I've just noticed joomla 3.1 now includes the Ajax Chosen plugin but I've got a feeling it's implementation is only for tags? Maybe someone can shed light on this?
The custom field's getOptions function looks a little like this:
protected function getOptions()
{
// Build the script.
$script = array();
$script[] = 'jQuery(document).ready(function() { ';
$script[] = ' jQuery("#jform_child").trigger("liszt:updated"); ';
$script[] = ' jQuery("#jform_parent").chosen().change( function() { ';
$script[] = ' var selectedValue = jQuery("#jform_parent").val(); ';
$script[] = ' jQuery.ajax({ ';
$script[] = ' type: "GET", ';
$script[] = ' dataType: "json", ';
$script[] = ' url: "index.php?option=com_mycomponent&task=myview.controllerTask", ';
$script[] = ' data: { ';
$script[] = ' "selectedValue": selectedValue ';
$script[] = ' }, ';
$script[] = ' success:function(data) { ';
$script[] = ' jQuery("select#jform_child option").remove(); ';
$script[] = ' jQuery.each(data, function(i, item) { ';
$script[] = ' jQuery("select#jform_child").append( "<option value="+ i +">" + item + "</option>" ); ';
$script[] = ' }); ';
$script[] = ' jQuery("select").trigger("liszt:updated"); ';
$script[] = ' } ';
$script[] = ' }); ';
$script[] = ' }); ';
$script[] = '}); ';
// Add the script to the document head.
JFactory::getDocument()->addScriptDeclaration(implode("\n", $script));
$options = array();
// Initialize some field attributes.
$key = $this->element['key_field'] ? (string) $this->element['key_field'] : 'value';
$value = $this->element['value_field'] ? (string) $this->element['value_field'] : (string) $this->element['name'];
$translate = $this->element['translate'] ? (string) $this->element['translate'] : false;
$query = (string) $this->element['query'];
// Get the database object.
$db = JFactory::getDbo();
// Set the query and get the result list.
$db->setQuery($query);
$items = $db->loadObjectlist();
// Build the field options.
if (!empty($items))
{
foreach ($items as $item)
{
if ($translate == true)
{
$options[] = JHtml::_('select.option', $item->$key, JText::_($item->$value));
}
else
{
$options[] = JHtml::_('select.option', $item->$key, $item->$value);
}
}
}
// Merge any additional options in the XML definition.
$options = array_merge(parent::getOptions(), $options);
return $options;
}
As you can see the jQuery removes the current select options from the child field and populates it with new options grabbed from the sub-controller. The parent select field's selected value is passed to the sub-controller as the variable "selectedValue" data: { }. Once the new options are populated we call .trigger("liszt:updated")
on the child field, this will make the Chosen plugin rebuild the select menu with the new options.
Anyway - here's the sub-controller's task:
public function controllerTask()
{
JFactory::getDocument()->setMimeEncoding( 'application/json' );
JResponse::setHeader('Content-Disposition','attachment;filename="progress-report-results.json"');
$jinput = JFactory::getApplication()->input;
$selectedValue = $jinput->get('selectedValue');
$model = $this->getModel();
echo json_encode($model->modelMethod($selectedValue));
JFactory::getApplication()->close();
}
It takes the "selectedValue" variable we passed with Ajax and uses it with modelMethod. In our case modelMethod takes the "selectedValue" variable and returns the list of child data to be displayed in the child field.
Here's that model method:
public function modelMethod($selectedValue) {
$db = JFactory::getDbo();
$db->setQuery("SELECT 0 AS `id`, '- Please select a child -' AS `child` UNION SELECT id, child FROM #__mycomponent_children WHERE #__mycomponent_children.parent = " . $selectedValue);
$list = $db->loadObjectList();
foreach ($list as $option) {
$options[$option->id] = $option->child;
}
return $options;
}
I hope this helps someone!