If you’re a fastidious Magento module developer, you will try to make sure that your module options only reveal the necessary and configured fields to the admin user and hide all the irrelevant stuff. Specifically, I’m referring to Yes/No options that allow the administrator to enable and configure a subsequent option. For example, you have an extension that can show additional field content if an option is enabled and allows for entering the field contents:

Standard options configuration
As we can see, the initial state is ‘No’ for the ‘Enable Additional Text’ option, and the textarea is blank. It would be really nice if the whole ‘Additional Text’ row was hidden in this scenario.
We can achieve that by utilizing a front end model for our Yes/No option. Magento provides this mechanism for creating options that depend on certain settings or other dependencies. Some of the built in options are the ‘Allowed Countries’, Enable Flat Catalog Products/Categories and the Tax Destination Calculation ‘State’ option.
We are going to use the front end model to add some logic to enable/disable the textarea and some JavaScript to enable the toggle functionality when the Yes/No drop-down is changed. Note that this is just one, albeit trivial, example of using this technique. Especially since Magento provides the <depends> node for this specific purpose (see the update below).
Using the same tutorial module from our Explain Your Module Configuration Options With Tooltips quick tip, we’ll change our system.xml to read:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<tabs>
<magebase translate="label" module="custom">
<label>Magebase</label>
<sort_order>700</sort_order>
</magebase>
</tabs>
<sections>
<mbcustom translate="label" module="custom">
<tab>magebase</tab>
<label>Magebase Tutorial Options</label>
<frontend_type>text</frontend_type>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<groups>
<default translate="label">
<label>Conditional Toggle Options</label>
<frontend_type>text</frontend_type>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<fields>
<mbcustomopt1 translate="label comment">
<label>Enable Additional Text</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<frontend_model>custom/adminhtml_system_config_form_field_customopt1toggle</frontend_model>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<comment>Select whether to show a text block in the front end.</comment>
</mbcustomopt1>
<mbcustomopt2 translate="label comment">
<label>Additional Text</label>
<frontend_type>textarea</frontend_type>
<sort_order>20</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<comment>Enter the content for the text block.</comment>
<tooltip>Note that the content will be erased if you disable it above, and save.</tooltip>
</mbcustomopt2>
</fields>
</default>
</groups>
</mbcustom>
</sections>
</config>
The key to our quick tip is in:
<frontend_model>custom/adminhtml_system_config_form_field_customopt1toggle</frontend_model>
So, now, we’re going to create the front end model class. Incidentally, even though it’s named a ‘model’ class we are going to create it as a Magento Block class since this is how the other built in front end model classes are implemented. Effectively, it is used for template output so it’s appropriate to create a block class for this.
Create Customopt1toggle.php under .../Magebase/Custom/Block/Adminhtml/System/Config/Form/Field/ with the following content:
class Magebase_Custom_Block_Adminhtml_System_Config_Form_Field_Customopt1toggle extends Mage_Adminhtml_Block_System_Config_Form_Field
{
/**
* Get element ID of the dependent field to toggle
*
* @param object $element
* @return String
*/
protected function _getToggleElementId($element)
{
return substr($element->getId(), 0, strrpos($element->getId(), 'mbcustomopt1')) . 'mbcustomopt2';
}
/**
* Get element ID of the dependent field's parent row
*
* @param object $element
* @return String
*/
protected function _getToggleRowElementId($element)
{
return 'row_'.$this->_getToggleElementId($element);
}
/**
* Override method to output our custom HTML with JavaScript
*
* @param Varien_Data_Form_Element_Abstract $element
* @return String
*/
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
{
// If our Yes/No toggle is 'No' then disable the text value field
if(!$element->getValue() || $element->getValue()!=1) {
$_frm = $element->getForm();
$_elm = $_frm->getElement($this->_getToggleElementId($element));
$_elm->setDisabled('disabled')->setValue('');
}
// Get the default HTML for this option
$html = parent::_getElementHtml($element);
// Set up additional JavaScript for our toggle action. Note we are using the two helper methods above
// to get the correct field ID's. They are hard-coded and depend on your option names in system.xml
$javaScript = "
<script type=\"text/javascript\">
Event.observe(window, 'load', function() {
enabled=$('{$element->getHtmlId()}').value;
if (!enabled || enabled!=1) {
$('{$this->_getToggleRowElementId($element)}').hide();
} else {
$('{$this->_getToggleRowElementId($element)}').show();
}
});
Event.observe('{$element->getHtmlId()}', 'change', function(){
enabled=$('{$element->getHtmlId()}').value;
$('{$this->_getToggleElementId($element)}').disabled = (!enabled || enabled!=1);
if (!enabled || enabled!=1) {
$('{$this->_getToggleRowElementId($element)}').hide();
} else {
$('{$this->_getToggleRowElementId($element)}').show();
}
});
</script>";
$html .= $javaScript;
return $html;
}
}
The Varien_Data_Form_Element_Abstract class implements the _getElementHtml() method responsible for generating the HTML for each option element. This is where we can add out override and ‘inject’ some custom code to control the other element as well as the JavaScript to implement the dynamic toggle functionality. The code is self-explanatory.
Basically, we have a couple of helper methods to return our HTML field and row ID’s depending on the names we gave our options in config.xml. The _getElementHtml() method just sets the disabled state of the textarea field if the option is ‘No’ and adds the Prototype JavaScript to add our toggle functionality. The JavaScript code will execute on page load and completely hide the text field if the option is ‘No’ and install an event observer on the Yes/No drop-down to show/hide the text option.
The result of our work would look like this initially:

Toggle options - Text Hidden
Selecting ‘Yes’, wil toggle the next field into its visible state:
Conclusion
We’ve seen how to create a nice little usability enhancement to busy module option pages. You can take this paradigm and enhance it for your own specific purposes. You can study the other examples of the built in Magento options using a frontend_model class for more inspiration and ideas.
One note, the above solution will erase a text option once its saved when the toggle is set to ‘No’. If you’d like to preserve the option, just remove the PHP code setting the text field as disabled as well as the $('{$this->_getToggleElementId($element)}').disabled = (!enabled || enabled!=1); JavaScript line. That should allow the hidden option to be saved.
Update:
The same result as above can be achieved by utilizing the <depends> node in the system.xml as Damián pointed out:
<fields>
<mbcustomopt1 translate="label comment">
<label>Enable Additional Text</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<comment>Select whether to show a text block in the front end.</comment>
</mbcustomopt1>
<mbcustomopt2 translate="label comment">
<label>Additional Text</label>
<frontend_type>textarea</frontend_type>
<sort_order>20</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<depends><mbcustomopt1>1</mbcustomopt1></depends>
<comment>Enter the content for the text block.</comment>
</mbcustomopt2>
</fields>
However, this article illustrates how you can potentially add custom option processing using the front end model option. This is more relevant for example if you have an option where you need to check more complex conditions or link several options together.
Originally published on magebase.com. Copyright © 2011 Magebase - All Rights Reserved.





Proud members of the
Why not to use instead?
Last chance and no more coffee.
You can use the depends node into the system.xml.
@Damián Thanks for your feedback. The purpose of this example really is to show how you can add custom option processing into your configuration using an option with a front end model. This can be applied for non-trivial applications.
Just out of curiosity, are the instructions below the comment box on how to insert code into comments invisible or otherwise unclear?
LOL
The problem with the instructions is between the keyboard and the chair.
<depends>node.Yes
allows you specify that your configuration field should only be displayed when another confgiruation field in the same group has a specific value.
But note that this node is available on Magento version >= 1.4.x
Simple dependencies can be done anywhere in forms by hijacking the same block that the system.xml config uses. Just add the Mage_Adminhtml_Widget_Form_Element_Dependence block to your layout and use its addFieldMap() to map element IDs to a field and addFieldDependence() to set up the dependencies. For example, on a form that shows FTP login information fields only if FTP is selected as a source, I do:
$this->getLayout()->createBlock('adminhtml/widget_form_element_dependence')) ->addFieldMap('source_type', 'source_type') ->addFieldMap('ftp_server', 'ftp_server') ->addFieldMap('ftp_user', 'ftp_user') ->addFieldMap('ftp_pass', 'ftp_pass') ->addFieldDependence('ftp_server', 'source_type', 'ftp') ->addFieldDependence('ftp_user', 'source_type', 'ftp') ->addFieldDependence('ftp_pass', 'source_type', 'ftp');