【问题标题】:add required suffix to zf2 form element label为 zf2 表单元素标签添加所需的后缀
【发布时间】:2013-04-28 07:54:40
【问题描述】:

我使用以下代码将电子邮件元素添加到我的 Zend Framework 2 表单:

$form->add(array(
    'type' => 'Zend\Form\Element\Email',
    'name' => 'email',
    'options' => array(
        'label' => 'Email'
    ),
));

默认情况下,此元素的 getInputSpecification() 方法将 required 设置为 true。但是元素对象不包含任何必需的属性,因此标记也不包含。

如何向我的表单添加标记以便我的 CSS 能够添加所需的后缀?或者至少:自定义视图助手如何获取“必需”设置?

我意识到我可以只添加一个必需的属性,但这感觉不对,因为它可能与内部元素的“必需”设置不同步。

【问题讨论】:

    标签: forms zend-framework2 required-field


    【解决方案1】:

    虽然 Sam 和 Cellulosa 的反应是一个非常好的解决方案。

    我担心随着 ZF2 框架的发展,这些将无法扩展。尽管他们正在扩展 Zend\Form\View\Helper\FormLabel,但 __invoke 方法中有很多重复的代码。随着对框架和原始助手的更改,我们将不得不不断更新您的新表单视图助手以复制原始 __invoke 方法中的任何更改。

    一个更简单的解决方案是使用提供的参数调用 parent::__invoke() 到 ViewHelper 中以避免重复代码。

    所以,这里是解决方案:

    在 Application/src/Application/Form/View/Helper/RequiredMarkInFormLabel.php 中创建 ViewHelper

    <?php
    namespace Application\Form\View\Helper;
    
    use Zend\Form\View\Helper\FormLabel as OriginalFormLabel;
    use Zend\Form\ElementInterface;
    
    /**
     * Add mark (*) for all required elements inside a form.
     */
    class RequiredMarkInFormLabel extends OriginalFormLabel
    {
         /**
         * Invokable
         *
         * @return str
         */    
        public function __invoke(ElementInterface $element = null, $labelContent = null, $position = null)
        {
    
            // invoke parent and get form label
            $originalformLabel = parent::__invoke($element,$labelContent,$position);
    
            // check if element is required
            if ($element->hasAttribute('required')) {
                // add a start to required elements
                return '<span class="required-mark">*</span>' . $originalformLabel;
            }else{
                // not start to optional elements
                return  $originalformLabel;
            }
        }
    }
    

    记得在 Application/config/module.config.php 中注册 ViewHelper

    'view_helpers' => array(
        'invokables'=> array(
            'formlabel' => 'Application\Form\View\Helper\RequiredMarkInFormLabel'  
        )
    ),  
    

    【讨论】:

    • 这是一个重要的考虑因素,也是这个问题的干净解决方案。非常好!
    【解决方案2】:

    不过,您几乎是正确的。为此,您肯定需要自己的 ViewHelper。实现这一点的最简单方法是从Zend\Form\View\Helper\FormLabel 扩展并覆盖__invoke() 函数。这只是一个快速尝试,但我想这有可能正常工作;)

    public function __invoke(ElementInterface $element = null, $labelContent = null, $position = null)
    {
        // Implement all default lines of Zend\Form\View\Helper\FormLabel
    
        // Set $required to a default of true | existing elements required-value
        $required = ($element->hasAttribute('required') ? $element->getAttribute('required') : true);         
    
        if (true === $required) {
            $labelContent = sprintf(
                '<span class="im-required">(*)</span> %s',
                $labelContent
            );
        }
    
        return $openTag . $labelContent . $this->closeTag();
    }
    

    记得在您的Module#getViewHelperConfig() 中注册您自己的ViewHelper,如下所示:

    public function getViewHelperConfig()
    {
        return array(
            'factories' => array(
                'myFormLabel' => function($sm) {
                    return new Mynamespace\Form\View\Helper\MyFormLabel;
                },
            ),
        );
    }
    

    我真的有点惊讶这样的事情没有实现,尽管我确信这是有原因的:)

    【讨论】:

    • 查看我的问题的最后一行:通过添加一个必需的属性,您必须在地方注册“必需”:一个在表单中,一个在输入过滤器中。这似乎不是要走的路。
    • 尝试理解其背后的逻辑。表单元素的attribute[required] 代表HTML 属性&lt;input required="$required" .../&gt;InputFilter 负责您的业务逻辑。这是两个完全不同的问题,即使它们有接触点。
    • 验证和标记确实是两个独立的问题。但在这里我们谈论的是产生内容的验证。如果您将这些分开,您将手动执行某些操作(存在验证和内容不同步的风险),这可以自动完成。
    【解决方案3】:

    使用 Sam 提供的指南,我设法将其排序如下:

    我创建了一个Application/src/Application/Form/View/Helper/RequiredMarkInFormLabel.php 文件:

    <?php
    namespace Application\Form\View\Helper;
    
    use Zend\Form\View\Helper\FormLabel as OriginalFormLabel;
    use Zend\Form\ElementInterface;
    use Zend\Form\Exception;
    
    class RequiredMarkInFormLabel extends OriginalFormLabel
    {
        public function __invoke(ElementInterface $element = null, $labelContent = null, $position = null)
        {
    
            ...
    
            // Set $required to a default of true | existing elements required-value
            $required = ($element->hasAttribute('required') ? true : false);
    
            if (true === $required) {
                $labelContent = sprintf(
                    '%s<span class="required-mark">*</span>',
                    $labelContent
                );
            }
    
            return $openTag . $labelContent . $this->closeTag();
        }
    }
    

    我通过在Application/Module.php 中添加此代码启用:

    public function getViewHelperConfig()
    {
        return array(
            'invokables' => array(
                'formlabel' => 'Application\Form\View\Helper\RequiredMarkInFormLabel',
            ),
        );
    }
    

    希望这对某人有所帮助! ;)

    【讨论】:

      【解决方案4】:

      我所做的是根据 InputFilter 在 Elements 上动态添加所需的属性。

      在我的表单助手上:

      public function render(Traversable $fieldset)
          {
              ...
              elseif ($element instanceof ElementInterface) {
                      if ($fieldset instanceof \Zend\InputFilter\InputFilterProviderInterface){
                          $ifs = $fieldset->getInputFilterSpecification();
                          $start = strrpos($element->getName(), '[')+1;
                          $end = strpos($element->getName(),']');
                          $elementName = substr($element->getName(),$start,$end-$start);
                          if (isset($ifs[$elementName]['required'])){
                              $element->setAttribute('required', $ifs[$elementName]['required']);
                          }
      
                      }
                      $form .= $elementHelper->render($element);
              ...
      
              return $form;
          }
      

      在元素助手上

      public function render(ElementInterface $element){
      ...
      $required = ($element->hasAttribute('required') ? $element->getAttribute('required') : false);
      ...
      if (true === $required && ($label != '' || $label != null || $label != false)) {
           $label .= '*';
      }
      

      因此,您不必担心按照@Sam 的建议添加属性和 InputSpecification

      【讨论】:

        【解决方案5】:

        好的,所以我厌倦了进一步寻找......因此我只是解析了元素名称以查找括号之间最深的字符串......这是我的视图助手,用于使用“form-horizo​​ntal”渲染表单的所有元素' twitter bootstrap 的表单布局和必填字段的星号图标(需要在 inputfilter 中设置,而不是元素属性)

        FormControlGroup.php(ViewHelper 将表单元素呈现为)

        <?php
        
        namespace Application\View\Helper;
        
        use Zend\InputFilter\InputFilter;
        
        use Zend\Form\ElementInterface;
        use Zend\Form\Element;
        use Zend\Form\Fieldset;
        
        use Zend\Form\View\Helper\AbstractHelper;
        use Zend\Form\View\Helper\FormLabel;
        use Zend\Form\View\Helper\FormElement;
        use Zend\Form\View\Helper\FormElementErrors;
        
        use Application\View\Helper\FormControlGroupFieldset;
        
        use Zend\Debug\Debug;
        
        class FormControlGroup extends AbstractHelper
        {
        
        public function __invoke(ElementInterface $elem = null,InputFilter $inputFilter = null){
            if(!$elem){ return $this; }
        
            // back up for fieldsets
            if($elem instanceof Fieldset){ 
                if($this->getView()){
                    $fcgf = new FormControlGroupFieldset();
                    $fcgf->setView($this->getView());
                    return $fcgf->__invoke($elem,$inputFilter);
                } else {
                    throw new Exception\DomainException(sprintf('FormControlGroup ViewHelper expects either an Element or a Fieldset. No View Could be found in the provided Object.'));
                }
            }
        
            // add control-group container
            $out = '<div class="control-group">';
        
            // add control-label class to label if label exists
            $out .= $this->renderLabel($elem,$inputFilter);
        
            // add controls container
            $out .= '<div class="controls">';
        
            // ensure renderer for used viewhelpers
            if($this->getView()){
                // render element
                $el = new FormElement();
                $el->setView($this->getView());
                $out .= $el->__invoke($elem);
                unset($el);
        
                // render element errors
                $er = new FormElementErrors();
                $er->setView($this->getView());
                $out .= $er->__invoke($elem,array('class' => 'help-inline'));
                unset($er);
            } else {
                $out .= 'No renderer found';
            }
        
        
            // close containers
            $out .= '</div></div>';
            return $out;
        }
        
        /**
         * 
         * @param ElementInterface $elem
         * @return string rendered Label with control-label class
         */
        protected function renderLabel(ElementInterface &$elem,InputFilter &$inputFilter = null){
                if($elem->getLabel()){
                $lblAttr = $elem->getLabelAttributes();
                if($lblAttr && key_exists('class', $lblAttr)){
                    if(stripos($lblAttr['class'],'control-label')){
                        $lblAttr['class'] .= 'control-label';
                    }
                } else {
                    $lblAttr['class'] = 'control-label';
                }
                $elem->setLabelAttributes($lblAttr);
                // check whether this element is required to fill out
                if($inputFilter){
                    $inputs = $inputFilter->getInputs();
                    if(key_exists($this->getRealElementName($elem),$inputs)){
                        if($inputs[$this->getRealElementName($elem)]->isRequired()){
                            $elem->setLabel($elem->getLabel().'<i class="icon-asterisk"></i>');
                        }
                    }
                }
                $lbl = new FormLabel();
                return $lbl->__invoke($elem);
            } else {
                return '';
            }
        }
        
        public function getRealElementName($e){
            $start = strrpos($e->getName(), '[')+1;
            $end = strpos($e->getName(),']');
            $elName = substr($e->getName(),$start,$end-$start);
            return $elName;
        }
        }
        

        FormControlGroupFieldset.php(ViewHelper 将表单字段集呈现为 - 由 FormControlGroupFieldset 调用)

        <?php
        
        namespace Application\View\Helper;
        
        
        use Zend\InputFilter\InputFilter;
        
        use Zend\Form\Fieldset;
        use Zend\Form\View\Helper\AbstractHelper;
        
        use Application\View\Helper\FormControlGroup;
        
        use Zend\Debug\Debug;
        
        
        class FormControlGroupFieldset extends AbstractHelper
        {
        
        public function __invoke(Fieldset $fs=null,InputFilter $inputFilter = null){
            if(!$fs){ return $this; }
        
            $out = '<fieldset>';
        
            if($fs->getLabel()){
                $out .= '<legend>'.$fs->getLabel().'</legend>';
            }
        
            if($this->getView() && sizeof($fs->getElements()) > 0){
                $cg = new FormControlGroup();
                $cg->setView($this->getView());
                if($inputFilter){
                    $inputs = $inputFilter->getInputs();
                    $fsInput = $inputs[$fs->getName()];
                }
                foreach($fs->getElements() as $e){
                    $out .= $cg->__invoke($e,$fsInput);
                }
                unset($cg);
            } else {
                $out .= "No Fieldset renderer found.";
            }
        
            $out .= '</fieldset>';
        
            return $out;
        }
        
        public function getRealElementName($e){
            $start = strrpos($e->getName(), '[')+1;
            $end = strpos($e->getName(),']');
            $elName = substr($e->getName(),$start,$end-$start);
            return $elName;
        }
        
        }
        

        您可能需要调整命名空间...

        【讨论】:

          【解决方案6】:

          对于其他想知道...有一个简单的解决方案,使用 css :after 伪类将 '*' 附加到标签。

          label.required:after{
             content:'*';
          }
          

          再简单不过了!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-04-15
            • 2011-04-20
            • 2023-03-14
            • 1970-01-01
            • 1970-01-01
            • 2019-09-14
            • 1970-01-01
            相关资源
            最近更新 更多