zend form custom attribute in select option?
Asked Answered
S

7

14

I want know how to add custom attribute for option in a select field of Zend form.

PHP:

$option_values = array("multiOptions" => array(
    "US" => "United States",
    "CA" => "Canada",
));

$type=array('big','small');
$option= new Zend_Form_Element_Select('option',  $option_values);

HTML:

<select>
    <option value='US' type='big'>United States</option>
    <option value='CA' type='small'>Canada</option>
</select>

How to add this type attribute in the option?

Stannwood answered 23/3, 2011 at 6:1 Comment(4)
type is not standard attributes that option can have, see w3schools.com/tags/tag_option.aspBereft
@Haim It could be data-type which is perfectly valid in HTML5. The actual attribute name is arbitrary in relation to this questionSuperstitious
possible duplicate of How to add CSS classes to Zend_Form_Element_Select optionCrapulent
framework.zend.com/issues/browse/ZF-8684Crapulent
K
10

It is not possible using ZF's implementation of Zend_Form_Element_Select. You need to create your own element. I have done something similar, here's the relevant code:

<?php
require_once 'Zend/Form/Element/Select.php';

/**
 * Select, but with the possibility to add attributes to <option>s
 * @author Dominik Marczuk
 */
class Zend_Form_Element_SelectAttribs extends Zend_Form_Element {

    public $options = array();

    public $helper = 'selectAttribs';

    /**
     * Adds a new <option>
     * @param string $value value (key) used internally
     * @param string $label label that is shown to the user
     * @param array $attribs additional attributes
     */
    public function addOption ($value,$label = '',$attribs = array()) {
        $value = (string) $value;
        if (!empty($label)) $label = (string) $label;
        else $label = $value;
        $this->options[$value] = array(
            'value' => $value,
            'label' => $label
        ) + $attribs;
        return $this;
    }
}

Put this into /library/Zend/Form/Element/SelectAttribs.php. You also need a helper to render the element. Put it into your view helpers directory, name it SelectAttribs.php as well. Here's the contents of my file:

<?php
require_once 'Zend/View/Helper/FormElement.php';

class Zend_View_Helper_SelectAttribs extends Zend_View_Helper_FormElement {
    public function selectAttribs($name, $value = null, $attribs = null, $options = null, $listsep = "<br />\n") {
        $info = $this->_getInfo($name, $value, $attribs, $options, $listsep);
        extract($info); // name, id, value, attribs, options, listsep, disable

        // force $value to array so we can compare multiple values to multiple
        // options; also ensure it's a string for comparison purposes.
        $value = array_map('strval', (array) $value);

        // now start building the XHTML.
        $disabled = '';
        if (true === $disable) {
            $disabled = ' disabled="disabled"';
        }

        // Build the surrounding select element first.
        $xhtml = '<select'
                . ' name="' . $this->view->escape($name) . '"'
                . ' id="' . $this->view->escape($id) . '"'
                . $disabled
                . $this->_htmlAttribs($attribs)
                . ">\n  ";

        // build the list of options
        $list = array();
        $translator = $this->getTranslator();
        foreach ($options as $opt_value => $option) {
            $opt_disable = '';
            if (is_array($disable) && in_array($opt_value, $disable)) {
                $opt_disable = ' disabled="disabled"';
            }
            $list[] = $this->_build($option, $disabled);
        }

        // add the options to the xhtml and close the select
        $xhtml .= implode("\n   ", $list) . "\n</select>";

        return $xhtml;
    }

    protected function _build($option, $disabled) {
        $html = '<option';
        foreach ($option as $attrib => $value) {
            $html .= " $attrib=\"$value\"";
        }
        return $html.$disabled.">".$option['label']."</option>";
    }
}

With this, you should be ready to go:

$elt = new Zend_Form_Element_SelectAttribs('whatever');
$elt->addOption($value,$label,array('attribname' => 'attribvalue'));
Khz answered 23/3, 2011 at 11:31 Comment(7)
Regarding what? You need to extend Zend's form element to create a new type of select, with the new addOption function. The view helper is what renders the form element (each form element uses a view helper for rendering). Does this clarify the problem?Khz
You shouldn't name your own classes / helpers starting with 'Zend' and not put it in the Zend library directory. When you deploy a new version your own classes get overwritten. A better name would something like My_Form_Element_SelectAttributes of Mingos_... this is not conform zend coding conventions.Dekko
This is a great example, but it cant populate values, so setValue doesnt change active option. So I fixed some lines: $list[] = $this->_build($value, $option, $disabled); protected function _build($setvalue, $option, $disabled) { and added after $html = '<option'; if(is_array($setvalue) and in_array($option['value'], $setvalue)) $html .= " selected=selected";Crinoline
This helped me a lot, thanks. Remember to register your class as a view helper in your config or bootstrap: $view->addHelperPath('MyNamespace/View/Helper', 'MyNamespace_View_Helper');Drinkable
When use translation and attrib (id for example) value is in translation, that will be translated. Probably decorator issue?Atlante
For anyone landing here like I did, just a heads up that this is easily accomplished if you're using ZF2: #26057332Sholley
Sure. The question was about ZF1 though.Khz
P
4

Using addMultiOption($value,$label) I just set the value parameter to something like:

$value = $id . '" ref="' . $ref;

and when it renders you get:

<option value="<idValue>" ref="<refValue"><labelValue></option>

Hope this helps....

Okay, value gets escaped but optionClasses does not so inside the loop that adds the addMultiOptions(val,lable) I do something like this:

$optionClasses[<val>] = 'ref_' . <val> . '" ref="' . <ref>;

and then after the loop just do a setAttrib('optionClasses',$optionClasses)

And that actually works...

I answered this for another question but could not find a way to add a comment here to reference it; It was Zend Framework addMultiOption adding custom parameters like "rel" for options

Persecute answered 15/5, 2013 at 15:8 Comment(0)
D
2

I didn't find @mingos's answer complete and had some issues with implementing setting the value. His answer helped a lot with what needed to be extended and changed however. Once I had that start the rest was pretty easy. I just extended ZF1's Select and overrode where I needed:

/**
 * Select, now with abbility to specify attributes on <Option>, addMultiOption has new syntax
 * @author Seth Miller
 */
class MyNamespace_Form_Element_SelectAttribs extends Zend_Form_Element_Select {

    public $options = array();
    public $helper = 'selectAttribs';

    /**
     * Add an option
     *
     * @param  string $option
     * @param  string $value
     * @return Zend_Form_Element_Multi
     */
    public function addMultiOption($value, $label = '', $attribs = array()) {
        $value = (string) $value;
        if (!empty($label)) {
            $label = (string) $label;
        }
        else {
            $label = $value;
        }

        $this->_getMultiOptions();
        if (!$this->_translateOption($value, $label)) {
            $this->options[$value] = array(
                'value'  => $value,
                'label'  => $label
                    ) + $attribs;
        }
        return $this;
    }

    /**
     * Add many options at once
     *
     * @param  array $options
     * @return Zend_Form_Element_Multi
     */
    public function addMultiOptions(array $options) {
        foreach ($options as $optionKey => $optionProperties) {
            if (is_array($optionProperties)
                    && array_key_exists('key', $optionProperties)
                    && array_key_exists('value', $optionProperties)
            ) {
                if(array_key_exists('key', $optionProperties)) $optionKey = $optionProperties['key'];
                $this->addMultiOption($optionKey, $optionProperties['value'], $optionProperties['attribs']);
            }
            else {
                $this->addMultiOption($optionKey, $optionProperties);
            }
        }
        return $this;
    }

    public function isValid($value, $context = null)
    {
        if ($this->registerInArrayValidator()) {
            if (!$this->getValidator('InArray')) {
                $multiOptions = $this->getMultiOptions();
                $options      = array();

                foreach ($multiOptions as $optionKey => $optionData) {
                    // optgroup instead of option label
                    if (is_array($optionData['options'])) {
                        $options = array_merge($options, array_keys($optionData['options']));
                    }
                    else {
                        $options[] = $optionKey;
                    }
                }

                $this->addValidator(
                    'InArray',
                    true,
                    array($options)
                );
            }
        }
        return parent::isValid($value, $context);
    }
}

And the view helper:

class MyNamespace_View_Helper_SelectAttribs extends Zend_View_Helper_FormElement {

public function selectAttribs($name, $value = null, $attribs = null, $options = null, $listsep = "<br />\n") {
    $info = $this->_getInfo($name, $value, $attribs, $options, $listsep);
    extract($info); // name, id, value, attribs, options, listsep, disable
    // force $value to array so we can compare multiple values to multiple
    // options; also ensure it's a string for comparison purposes.
    $value = array_map('strval', (array) $value);

    // check if element may have multiple values
    $multiple = '';

    if (substr($name, -2) == '[]') {
        // multiple implied by the name
        $multiple = ' multiple="multiple"';
    }

    if (isset($attribs['multiple'])) {
        // Attribute set
        if ($attribs['multiple']) {
            // True attribute; set multiple attribute
            $multiple = ' multiple="multiple"';

            // Make sure name indicates multiple values are allowed
            if (!empty($multiple) && (substr($name, -2) != '[]')) {
                $name .= '[]';
            }
        }
        else {
            // False attribute; ensure attribute not set
            $multiple = '';
        }
        unset($attribs['multiple']);
    }

    // now start building the XHTML.
    $disabled = '';
    if (true === $disable) {
        $disabled = ' disabled="disabled"';
    }

    // Build the surrounding select element first.
    $xhtml = '<select'
            .' name="'.$this->view->escape($name).'"'
            .' id="'.$this->view->escape($id).'"'
            .$multiple
            .$disabled
            .$this->_htmlAttribs($attribs)
            .">\n    ";

    // build the list of options
    $list = array();
    $translator = $this->getTranslator();
    foreach ((array) $options as $optionKey => $optionData) {
        if (isset($optionData['options'])) {
            $optDisable = '';
            if (is_array($disable) && in_array($optionData['value'], $disable)) {
                $optDisable = ' disabled="disabled"';
            }
            if (null !== $translator) {
                $optValue    = $translator->translate($optionData['value']);
            }
            $optId       = ' id="'.$this->view->escape($id).'-optgroup-'
                    .$this->view->escape($optionData['value']).'"';
            $list[]      = '<optgroup'
                    .$optDisable
                    .$optId
                    .' label="'.$this->view->escape($optionData['value']).'">';
            foreach ($optionData['options'] as $optionKey2 => $optionData2) {
                $list[]  = $this->_build($optionKey2, $optionData2, $value, $disable);
            }
            $list[]  = '</optgroup>';
        }
        else {
            $list[] = $this->_build($optionKey, $optionData, $value, $disable);
        }
    }

    // add the options to the xhtml and close the select
    $xhtml .= implode("\n    ", $list)."\n</select>";

    return $xhtml;
}

/**
 * Builds the actual <option> tag
 *
 * @param string $value Options Value
 * @param string $label Options Label
 * @param array  $selected The option value(s) to mark as 'selected'
 * @param array|bool $disable Whether the select is disabled, or individual options are
 * @return string Option Tag XHTML
 */
protected function _build($optionKey, $optionData, $selected, $disable)
{
    if (is_bool($disable)) {
        $disable = array();
    }

    $opt = '<option';

    foreach ($optionData as $attrib => $attribValue) {
        $opt .= ' '.$this->view->escape($attrib).'="'.$this->view->escape($attribValue).'"';
    }

    // selected?
    if (in_array((string) $optionData['value'], $selected)) {
        $opt .= ' selected="selected"';
    }

    // disabled?
    if (in_array($optionData['value'], $disable)) {
        $opt .= ' disabled="disabled"';
    }

    $opt .= '>' . $this->view->escape($optionData['label']) . "</option>";

    return $opt;
}

}

And implementation in a form would be something like:

$selectElement = new MyNamespace_Form_Element_SelectAttribs('selectElementName');
$selectElement->addMultiOption($value, $label, array('data-custom' => 'custom data embedded in option tag.');

I hope that helps someone. Thanks.

Drinkable answered 19/9, 2013 at 18:13 Comment(0)
S
1

mingos, you forgot about setvalue method of multiselect.

you shoul add something like:

...
foreach ($options as $opt_value => $option) {
            $opt_disable = '';
            $opt_selected = '';
            if (is_array($disable) && in_array($opt_value, $disable)) {
                $opt_disable = ' disabled="disabled"';
            }
            if (in_array($opt_value,$value)) {
                $opt_selected = ' selected="selected"';
            }
            $list[] = $this->_build($option, $disabled, $opt_selected);
        }
...
and

protected function _build($option, $disabled, $opt_selected) {
        $html = '<option';
        foreach ($option as $attrib => $value) {
            $html .= " $attrib=\"$value\"";
        }
        return $html . $disabled . $opt_selected . " foo>" . $option['label'] . "</option>";
    }
Swiger answered 17/6, 2011 at 15:35 Comment(1)
I only presented the implementation that I was using in a particular project of mine. I apparently don't need setValue() there, thus it's not present in my code. Thanks for making the solution more complete.Khz
S
0

You could extend / overwrite the Zend_View_Helper_FormSelect helper but the real problem is going to be getting the extra data into each option.

By default, Zend_Form_Element_Select (via Zend_Form_Element_Multi) expects two strings for each option, one for the value attribute and an optional one for the text content. You may need to create your own element to handle the extra data.

Superstitious answered 23/3, 2011 at 6:24 Comment(0)
M
0

No need for custom form element at all, what u can do is: $element->setAttrib('disable', array(1, 2, 5));

As explained at http://pietervogelaar.nl/set-attribute-on-select-option-with-zend_form/

Mirna answered 1/3, 2012 at 9:53 Comment(2)
There's a catch though that you should have mentioned: you can only add attribute names this way, but not attribute values. You cannot set data-foo="bar" this way.Khz
I stand corrected, too much work and not enough caffeine... :)Mirna
C
0
use Zend\Form\Element;
use Zend\Form\Form;

$select = new Element\Select('language');
$select->setLabel('Which is your mother tongue?');
$select->setValueOptions([
    [
        'value' => '0',
        'label' => 'French',
        'attributes' => [
            'data-locale' => 'fr'
        ],
    ],
    [
        'value' => '1',
        'label' => 'Italian',
        'disabled' => true,
    ],
]);

$form = new Form('language');
$form->add($select);

I found this way much easier than any of the above suggestions

Curling answered 26/1, 2022 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.