Skip to content

Instantly share code, notes, and snippets.

@jlainezs
Last active April 21, 2022 01:37
Show Gist options
  • Save jlainezs/5ae009d230eb39fa4393034076059b19 to your computer and use it in GitHub Desktop.
Save jlainezs/5ae009d230eb39fa4393034076059b19 to your computer and use it in GitHub Desktop.
Joomla 3.X custom field which implements chained dropdown controls with ajax autocomplete capabilities.
<?php
/**
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Pep Lainez <jlainezs@cloudconceptes.com>
* @copyright Copyright (C) 2018 CloudConceptes
* @license GNU/GPL https://www.gnu.org/licenses/gpl-3.0.html
*/
defined('JPATH_PLATFORM') or die;
JFormHelper::loadFieldClass('list');
/**
* Base class.
* Strictly not needed, but it allows to focus important tasks in descendants.
*/
abstract class BasicFormFieldList extends JFormFieldList
{
protected $placeholder = false;
protected $style;
/**
* Gets the data
*
* @return mixed
*
* @since version
*/
protected function getDbOptions()
{
return [];
}
/**
* Gets the options
*
* @see JFormFieldList::getOptions()
*
* @return array
*/
protected function getOptions()
{
$options = parent::getOptions();
$dbOptions = $this->getDbOptions();
if (count($dbOptions))
{
foreach ($dbOptions as $dbOption)
{
$tmp = JHtml::_('select.option', $dbOption->id, $dbOption->title);
$options[] = $tmp;
}
}
return $options;
}
/**
* Method to attach a JForm object to the field.
*
* @param \SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object.
* @param mixed $value The form field value to validate.
* @param string $group The field name group control value. This acts as as an array container for the field.
* For example if the field has name="foo" and the group value is set to "bar" then the
* full field name would end up being "bar[foo]".
*
* @return boolean True on success.
*
* @since 11.1
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
$this->style = (isset($element['style']) ? (string) $element['style'] : '');
return parent::setup($element, $value, $group); // TODO: Change the autogenerated stub
}
/**
* Method to get the field input markup.
*
* @return string The field input markup.
*
* @since 11.1
*/
protected function getInput()
{
$attr = '';
// Initialize some field attributes.
$attr .= !empty($this->class) ? ' class="' . $this->class . '"' : '';
$attr .= $this->disabled ? ' disabled' : '';
$attr .= !empty($this->size) ? ' size="' . $this->size . '"' : '';
$attr .= $this->multiple ? ' multiple' : '';
$attr .= $this->required ? ' required aria-required="true"' : '';
$attr .= $this->autofocus ? ' autofocus' : '';
$attr .= !empty($this->style) ? ' style="' . $this->style . '"' : '';
if ($this->getAttribute('placeholder'))
{
$attr .= ' data-placeholder = "' . JText::_($this->getAttribute('placeholder')) . '"';
}
// Initialize JavaScript field attributes.
$attr .= $this->onchange ? ' onchange="' . $this->onchange . '"' : '';
// Get the field options.
$options = $this->getOptions();
return JHtml::_('select.genericlist', $options, $this->name, trim($attr), 'value', 'text', $this->value, $this->id);
}
}
<?php
/**
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Pep Lainez <jlainezs@cloudconceptes.com>
* @copyright Copyright (C) 2018 CloudConceptes
* @license GNU/GPL https://www.gnu.org/licenses/gpl-3.0.html
*/
defined('JPATH_PLATFORM') or die;
use \Joomla\CMS\Factory;
require_once __DIR__ . '/basicformfieldlist.php';
/**
* Form Field class for chaining drop downs
*
* @since 1.0.1
*/
class FormFieldChainedList extends BasicFormFieldList
{
protected $placeholder = false;
protected $watch = '';
protected $option = '';
protected $task = '';
protected $format;
/**
* Configures the control
*
* @param SimpleXMLElement $element
* @param mixed $value
* @param null $group
*
* @return bool
*
* @since 1.0.1
* @throws Exception
*/
public function setup(\SimpleXMLElement $element, $value, $group = null)
{
$input = Factory::getApplication()->input;
$this->watch = ((isset($element['watch']) && !empty($element['watch'])) ? (string) $element['watch'] : '');
$this->option = (isset($element['option']) ? (string) $element['option'] : $input->get('option'));
$this->task = (isset($element['task']) ? (string) $element['task'] : '');
$this->format = (isset($element['format']) ? (string) $element['format'] : '');
return parent::setup($element, $value, $group);
}
private static $commonScriptsLoaded = false;
protected function getInput()
{
$watch_at = '#' . $this->watch;
$me = '#' . $this->id;
if (!self::$commonScriptsLoaded)
{
self::$commonScriptsLoaded = true;
Factory::getDocument()->addScriptDeclaration('
function chainedlist_updateValues(who, data, fullUpdate)
{
jQuery(who).children("option:not(:selected)").remove();
jQuery(who).prop("selectedIndex", -1);
for (i=0; i < data.length; i++){
let option = jQuery("<option>").prop("value", data[i].id).html(data[i].title);
jQuery(who).append(option);
}
let ChosenInputValue = jQuery(who + "_chzn input").val();
jQuery(who).trigger("liszt:updated");
jQuery(who + "_chzn input").val(ChosenInputValue);
jQuery(who + "_chzn").removeClass("chzn-container-single-nosearch");
jQuery(who + "_chzn input").prop("readonly", false);
}
function chainedlist_getItems(me, selected_value, option, task, initial_value, like, format, token){
jQuery.ajax({
url: "index.php?" + token + "=1",
data:{
option : option,
task : task,
parent_value : selected_value,
initial_value : initial_value,
like : like,
format : format,
},
method: "get",
dataType: "json"
})
.done(function(data, textStatus, xhqr){
chainedlist_updateValues(me, data);
});
}
function prepareUpdateFor(who){
jQuery(who).on("change", function(){
jQuery(document).trigger("update_chained", who);
});
}
');
}
$script = '
jQuery(document).ready(function(){
let token = "' . JSession::getFormToken() . '";';
if (!empty($this->watch))
{
$script .= '
prepareUpdateFor("' . $watch_at . '");
jQuery(document).on("update_chained", function(e, sender){
let selected_value = jQuery("' . $watch_at . '").val();
if (sender !== "' . $watch_at . '") return;
chainedlist_getItems(
"' . $me . '",
selected_value,
"' . $this->option . '",
"' . $this->task . '",
' . (int) $this->value . ',
"",
"json",
token
);
});';
}
else
{
$script .= 'let selected_value = 0;';
}
$script .= '
let timeOut'.$this->id.' = null;
jQuery("' . $me . '_chzn input").on("keyup", function(e){
let like = jQuery(this).val();
let selected_value = jQuery("' . $watch_at . '").val();
clearTimeout(timeOut'.$this->id.');
timeOut'.$this->id.' = setTimeout(function(){
chainedlist_getItems(
"' . $me . '",
selected_value,
"'. $this->option .'",
"' . $this->task . '",
' . (int) $this->value . ',
like,
"json",
token
);
}, 350);
});
jQuery("' . $me . '").chosen({disable_search_threshold : 0}).trigger("liszt:updated");
jQuery("' . $me . '_chzn").removeClass("chzn-container-single-nosearch");
jQuery("' . $me . '_chzn input").prop("readonly", false);
});';
Factory::getDocument()->addScriptDeclaration($script);
return parent::getInput();
}
}
<?xml version="1.0" encoding="utf-8"?>
<form>
<fieldset name="COM_MM_DETAILS" addfieldpath="administrator/components/com_yourcomponent/models/fields">
<field name="country_id" type="country"
label="COM_MM_COUNTRY_LABEL"
description="COM_MM_PROVINCE_COUNTRY_TITLE_DESCR"
option="com_yourcomponent"
task="the_controller.ajaxsearch"
format="json"
class="inputbox input-medium" required="true">
</field>
<field name="province_id" type="province"
watch="jform_country_id"
label="COM_MM_PROVINCE_TITLE_LABEL"
description="COM_MM_CITY_PROVINCE_TITLE_DESCR"
option="com_yourcomponent"
task="anothercontroller.ajaxsearch"
format="json" />
<field name="city_id" type="city"
watch="jform_province_id"
label="COM_MM_CITY_TITLE_LABEL"
description="COM_MM_CITY_PROVINCE_TITLE_DESCR"
option="com_yourcomponent"
task="evenanothercomponent.ajaxsearch"
format="json" />
</fielfset>
</form>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment