Zend Framework 2 - Form Element Decorators
Asked Answered
C

4

10

I want to force the Zend form into Twitter Bootstrap style. I currently iterate through the form fields and write the form info into my bootstrap div construction.

I saw in Zend Framework 1(!) that there is a way to do this within a decorator. But for some reason the doc for version 2 doesn't cover this point...

I'd like to do something like this:

protected $_format = '<label for="%s">%s</label>'
             . '<input id="%s" name="%s" type="text" value="%s"/>';

public function render($content)
{
    $element = $this->getElement();
    $name    = htmlentities($element->getFullyQualifiedName());
    $label   = htmlentities($element->getLabel());
    $id      = htmlentities($element->getId());
    $value   = htmlentities($element->getValue());

    $markup  = sprintf($this->_format, $name, $label, $id, $name, $value);
    return $markup;
}

Any ideas?

Closed answered 18/12, 2012 at 11:19 Comment(3)
take a look at the various twitterbootstrap modules, some of them have custom view helpers wich support most/all of the options. eg. bitbucket.org/dlu/dlutwbootstrapWording
i checked several of them out but they wont install or need about 1 million lines of code per form... I like my solution but it would be nice to wrap it in a decorator...thats why :)Closed
Zend\Form doesnt have decorator, but you are free to write your view helper as you outlined it. call it whatever you like. and call it like: echo $this->myviewhelper($form->get('elementname'));Wording
C
17

I'm using partials now. I'm iterating over the attributes, build a few exceptions for eg CSRF and Submit... This works pretty smooth:

View

echo $this->partial('partial/form-partial', array(
'form' => $this->form,
'url' =>  $this->url('whatever', array('action' => 'add')))); ?>

Partial

<?php
$form = $this->form;
$form->setAttribute ( 'action', $this->url () );
$form->prepare ();

echo $this->form ()->openTag ( $form );
foreach ( $form as $element ) :
?>
    <div
        class="control-group <?php if($this->formElementErrors($element)) echo "error" ?>">
        <label class="control-label"><?php echo $element->getLabel() ?></label>
        <div class="controls">
                <?php echo $this->formElement ( $element );
                    if ($this->formElementErrors ( $element ))
                ?>
            <span class="help-inline"><?php echo $this->formElementErrors($element) ?></span>
        </div>
    </div>
<?php
endforeach;
echo $this->form ()->closeTag ( $form );
?>

The exceptions are left out for clearity's sake...

Closed answered 9/1, 2013 at 8:39 Comment(2)
Hello, thanks for open my mind Im new in zend, but if i try your code, the hidden field or button, is added to your loop. How to separate only FormRow in loop, except hidden field and button ? ThanksZitazitah
i guess use if-conditionsClosed
P
6

I did it the way @Rufinus mentioned. See this Tutorial on how to create View Helpers in ZF2 http://blog.evan.pro/creating-a-simple-view-helper-in-zend-framework-2

In my case I simply wanted to wrap form elements with list items so I extended the original ZF2 View Helpers and let them do the rendering of the elements. I Just wrapped what they return:

View Helper FormCollection.php

<?php
// ./src/Application/View/Helper/FormCollection.php
namespace Application\View\Helper;

use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormCollection as BaseFormCollection;

class FormCollection extends BaseFormCollection {
    public function render(ElementInterface $element) {
        return '<ul>'.parent::render($element).'</ul>';
    }
}

View Helper FormElement.php

<?php
// ./src/Application/View/Helper/FormElement.php
namespace Application\View\Helper;

use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormElement as BaseFormElement;

class FormElement extends BaseFormElement {

    public function render(ElementInterface $element) {
        if ($element->getOption('required')) {
            $req = 'required';
        }
        $type = $element->getAttribute('type');
        $name = $element->getAttribute('name');
        return sprintf('<li class="%s %s %s">%s</li>', $name, $req, $type,  parent::render($element));
    }
}

while my view looks like this and didn't need to be modified for the changes to take effect.

<?php 
$form = $this->form;
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag($form);

worked like a charm.

Psychologist answered 3/3, 2013 at 21:13 Comment(0)
C
5

I tried Ron's Partial method, the result would like this which not Bootstrap 3 intended.

<form id="tea" name="tea" method="POST" action="/tea/add">
    ...
    <div class="form-group">
        <label class="control-label">Brand</label>
        <div class="form-control">
            <input type="text" value="" name="brand">
        </div>
    ...

We know, in order to use bootstrap 3 predefined Form style, we need to define style to input element: form-control, rather than its wrapping element.

My Partial way is as following.

echo $this->form()->openTag($form);
foreach ($form as $element) :?>
    <div class="form-group">
        <?php 
            if ($element->getOption('required')) { $req = 'required'; }
            $type = $element->getAttribute('type');
            $name = $element->getAttribute('name'); 
            $label = $element->getLabel();
        ?>
        <?php if ($name == 'id') { ?>
            <div class="hidden"><?php echo $this->formElement($element); ?></div>
        <?php } else if ($name == 'submit') { ?>
            <input class='btn' name='submit' type='submit' value='Add'>
        <?php } else if ($label != '') { ?>
            <label class="control-label"><?php echo $label ?></label>
            <input class='form-control' name='<?php echo $name ?>' type='<?php echo $type ?>'>
        <?php } ?> 
    </div>
<?php 
endforeach;
echo $this->form()->closeTag();

Well, we could get the result.

<form id="tea" name="tea" method="POST" action="/tea/add">
    ...
    <div class="form-group">
        <label class="control-label">Brand</label>
        <input class="form-control" type="text" name="brand">
    </div>
...

How to attach custom styles into zf2 forms has mentioned : to add class attribute to the Form element.

class TeaForm extends Form
{
    public function __construct($name = null)
    {
        // we want to ignore the name passed
        parent::__construct('tea');

        $this->add(array(
            'name' => 'id',
            'type' => 'Hidden',
        ));
        $this->add(array(
            'name' => 'brand',
            'type' => 'Text',
            'options' => array(
                'label' => 'Brand',
            ),
            /** **define class attribute** **/
        'attributes' => array(
            'class' => 'form-control',
        ),
        ));
....

It looks quite simple, but, the problem is the input element would be wrapped into the label element, which still not what Bootstrap 3 intended.

<form id="tea" role="form" name="tea" method="POST" action="/tea/add">
    <input type="hidden" value="" name="id">
    <label>
        <span>Name</span>
        <input class="form-control" type="text" value="" name="name">
    </label>
...

In my opinion, the Partial method is still one flexible and light choice. Tea Box is one ZF2 practice, you could find all above mentioned code and description from Gibhub

Close answered 25/11, 2013 at 1:59 Comment(1)
For the element to appear outside the label the element needs an id attribute setting.Europa
D
-6

This will make the code easier.

http://php.net/manual/en/function.echo.php

<?php 

$form->prepare();
echo 
$this->form()->openTag($form),
$this->formCollection($form),
$this->form()->closeTag($form);
Donegan answered 3/8, 2013 at 23:14 Comment(9)
I found it confusing.Judaica
This is my opinion: if you can write easier, to do it. Similar tips: use AnyClass1, AnyClass2;Donegan
The line following echo could be indented, for better reading.Judaica
I looked up your profile to help you understand the analogy here: cout << a; cout << b; This can be written as cout << a << b; And moreover, endl = PHP_EOLDonegan
It depends on your code. Just as cout :) But if you use formCollection, as in this case, my example is accurate.Donegan
PS: By the way, if you the answer understand, remove, please, your "minus" :)Donegan
I see, that you do not understand. formCollection displays the elements of the collection without indents. Next time just ask.Donegan
P.S.: I already anticipate the next question. For more control over render each item is drawn separately. Here is an example of a works with collection: github.com/dphn/ScContent/blob/master/view/sc-content/file/…Donegan
Como você é burro cara.Epoxy

© 2022 - 2024 — McMap. All rights reserved.