I am building an application using Zend_Framework and I would like to ask you for an advice regarding Zend_Form. All forms on the site should be decorated with tables, instead of definition lists. My idea is that I should create a common form (with no elements) and then just instantiate it across the site and add elements as needed. This common form would need to modify the standard decorators, so that tables are used instead of definition lists. How do you suggest to do this? Override Zend_Form's addElement() method so that it alters decorators of new elements?
But there's another caveat. There should be an option of using a different set of decorators for a particular element, if needed. So I'm a bit puzzled how to do this. Do you have any advice?
There is no simple way to override the default decorators. The solution I use is to override all the elements and redefine the loadDefaultDecorators method.
The problem is that each element have a specific set of decorator. For example, the hidden element needs only the ViewHelper decorator while file element needs File, Errors, Description, HtmlTag (td), Label (th), HtmlTag (tr).
You can also use Zend_Form::setElementDecorators at the end of your init method (after calls to addElement). But you need to customize it for each form...
Use an intermediate class for your project wide configuration. You will then extend this class instead of Zend_Form
file My/Form.php
<?php
abstract class My_Form extends Zend_Form {
public function __construct ( $options = null ) {
parent::__construct($options);
$this->setElementDecorators(array(
// the base <input, <select, <textarea markup
'ViewHelper',
// wrap that into a <div class="input-wrap" />
array (
'HtmlTag',
array (
'tag' => 'div',
'class' => 'input-wrap',
)
),
// append errors in <ul/li>
'Errors',
// then prepend <label markup
'Label',
));
}
}
then in file My/Form/Demo.php
<?php
class My_Form_Demo extends My_Form {
public function init () {
// Your elements here
}
}
You can do this for specific element as well
file My/Form/Element/Group.php
<?php
class My_Form_Element_Group extends Zend_Form_Element_Select {
public function init () {
// Specific options
$this->addMultiOptions(array(
'A' => 'group A',
'B' => 'group B',
));
// This element doesn't need the div.input-wrap
$this->removeDecorator('HtmlTag');
}
}
Related
I'd like to tweak the Illuminate\Html\FormBuilder class in Laravel 4 to automatically include a CSS class with each input, corresponding to the type of input, e.g. <input type="radio" class="radio" />
I've created my own class, which extends the original:
namespace Shady;
class FormBuilder extends Illuminate\Html\FormBuilder {
public function input($type, $name, $value = null, $options = array()) {
$options['class'] = isset($options['class'])
? implode(' ', array_unique(array_merge(array($type), explode(' ', $options['class']))))
: $type;
parent::input($type, $name, $value, $options);
}
}
Now, the question is, how do I get Laravel 4 to use it?
I've had a look in app/config/app.php, and there's only a reference to the HTML Fascade class. I'm not sure how or where that translates into the Illuminate\Support\ServiceProvider\HtmlServiceProvider class, but that class hard-codes the FormBuilder class, ala
protected function registerFormBuilder()
{
$this->app['form'] = $this->app->share(function($app)
{
$form = new FormBuilder($app['html'], $app['url'], $app['session']->getToken());
return $form->setSessionStore($app['session']);
});
}
I'm wondering whether I can either trick it into loading my version of the class somehow? Or if not, how else do I add in this functionality smoothly and neatly? Cheers.
There's a new section in the docs about extending Laravel that should help: http://laravel.com/docs/extending
I also tried battling this issue as I wanted to do exactly the same as you, partly out of interest of extending core classes but also to stop repeating myself (DRY) from passing the same two classes in the second (options) array parameter each time I wanted a submit button, but to no avail.
Instead, I opted in the end to make a HTML macro, as others suggested above but here's an actual example for others who might come across this.
Example:
Save the following code to app/macros.php (this file doesn't exist by default so create it):
HTML::macro( 'submit', function( $value = null, $options = array() )
{
$options = array_merge( $options, array( 'class' => 'btn btn-info' ) );
return Form::input( 'submit', null, $value, $options );
});
Now include macros.php by adding require app_path().'/macros.php'; to the bottom of app/start/global.php.
And now in your views, instead of:
Form::submit( 'Submit', array( 'class' => 'btn btn-info' ) );
Use:
HTML::submit( 'Submit' );
And it will produce the same submit button, allowing the same parameters as Laravel by default but will automatically add btn and btn-info classes to the button, without you needing to repeat yourself each time. Hope this helps someone. =)
It's unnecessary to extend the HTML helper class. Instead, use CSS selectors to pick the elements.
As for making your own methods, you can use the HTML::macro()
See: http://laravel.com/api/source-class-Illuminate.Html.HtmlBuilder.html#32
And: http://laravel.com/api/source-class-Illuminate.Html.HtmlBuilder.html#390
I created element in form object:
function createElement()
{
$template = new Zend_Form_Element_Hidden('field');
$template->addDecorator('ViewScript', array('placement' => 'prepend', 'viewModule' => 'admin', 'viewScript' => 'values.phtml'))
$this->addElement($template);
}
function setViewTemplate($values)
{
$view = new Zend_View();
$view->setScriptPath(APPLICATION_PATH . '/scripts/');
$view->assign('values', $values);
$this->getElement('field')->setView($view);
}
But in the view script 'values.phtml' I cannot get access to values like $this->values.
What I'm doing wrong here?
I know that it would be good to add own decorator, but it is interesting to use zends' decorators.
From the Zend Framework Documentation: Standard Form Decorators Shipped With Zend Framework Section
Zend_Form_Decorator_ViewScript
Additionally, all options passed to
the decorator via setOptions() that
are not used internally (such as
placement, separator, etc.) are passed
to the view script as view variables.
function setViewTemplate($values)
{
$this->getElement('field')
->getDecorator('ViewScript')
->setOptions('values', $values);
}
you can reslove it with using attribs
$template->setAttrib('key', 'value');
and in template
<?php echo $this->element->getAttrib('key'); ?>
I am using Zend Framework. For a particular form, there is not enough space to show the errors next to the form elements. Instead, I want to be able to display the errors above the form. I imagine I can accomplish this by passing $form->getErrorMessages() to the view but how do I disable error messages from being shown be each element?
The proposal above does not take into account that the default decorators may change. Instead of clearing the decorators and then reapplying all except the ones you do not need, it would be better to disable the decorators you don't need at form-initialization time, like:
class My_Form_Login extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$username = new Zend_Form_Element_Text('auth_username');
$username->setLabel('Username')
->setRequired(true)
->addValidator('NotEmpty')
->removeDecorator('Errors')
->addErrorMessage("Please submit a username.");
etc.....
You could then, wherever you use the form, decide how to display the messages (if you're planning to display them apart from your form). Of course if they should be part of the form, just create a suitable decorator and add it to the form-element init method above. Here is a nice tutorial on form decorators from ZendCasts.com
Example of displaying messages apart from the form itself.
$elementMessages = $this->view->form->getMessages();
// if there actually are some messages
if (is_array($elementMessages))
{
foreach ($elementMessages as $element)
{
foreach ($element as $message)
{
$this->view->priorityMessenger($message, 'notice');
}
}
}
The priorityMessenger-helper used above can be found here: http://emanaton.com/code/php/zendprioritymessenger
You can add decorators to form elements using setElementDecorators. Zend_Form has a function called just after init entitled loadDefaultDecorators. In your subclass, you can override this like so:
/**
* Load the default decorators for forms.
*/
public function loadDefaultDecorators()
{
// -- wipe all
$this->clearDecorators();
// -- just add form elements
// -- this is the default
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'Form'
));
// -- form element decorators
$this->setElementDecorators(array(
"ViewHelper",
array("Label"),
array("HtmlTag", array(
"tag" => "div",
"class" =>"element",
)),
));
return $this;
}
Assuming you added your elements in init, that applies those decorators to each element in the form. You'll note the absence of the "Errors" decorator in setElementDecorators. You could also try looping through the form elements and using removeDecorator to remove just the Errors decorator.
Is it possible to alter an html attribute of a Zend_Form_Element in a Decorator previously added ?
Lets say I have a decorator named RichTextArea. When I add it to a Zend_Form_Element_Textarea, I want the decorator to add the class "rich" to the textarea.
The final output should look like this :
<textarea name="content" id="content" class="rich" />
It is possible, but the syntax depends a little on how you are building the form. Easiest way is to do it on the element itself as you add it:
$element = new Zend_Form_Element_Text('something');
$element->class = 'rich';
$form->addElement($element);
or if you mass-assigned the decorators, e.g.:
$element = new Zend_Form_Element_Text('something');
$element->setDecorators(array(
'Errors',
'Label',
array(array('row' => 'HtmlTag'), array('tag' => 'div'))
));
[...]
$decorator = $element->getDecorator('row');
$decorator->setOption('class', 'rich');
If you are using a rich text editor like TinyMCE or similar, another option might be to create a custom form element that extends Zend_Form_Element_Textarea and always add your class to it.
It's possible to add any HTML-Attribute with
// #var Zend_Form_Element $element
$element->setAttribute($key, $value);
But you also can access the Attributes as a property like
$element->key = $value;
For more Information read this Section in Zend-Documentation: http://framework.zend.com/manual/1.12/en/zend.form.elements.html#zend.form.elements.metadata
I am using two decorator
- To get tabular form alignment
- To get date picker (ZendX_JQuery_Form_Element_DatePicker)
both are working individually, but not at a same time
Error:
Warning: Exception caught by form: Cannot render jQuery form element without at least one decorator implementing the 'ZendX_JQuery_Form_Decorator_UiWidgetElementMarker' interface. Default decorator for this marker interface is the 'ZendX_JQuery_Form_Decorator_UiWidgetElement'. Hint: The ViewHelper decorator does not render jQuery elements correctly.
My Get Form Function:
$form = new Form_Job();
$form->setDecorators(Decorator::$formDecorators);
$form->setElementDecorators(Decorator::$elementDecorators);
$form->getElement('submit')->setDecorators(Decorator::$buttonDecorators);
Form class Form_Job()
class Form_Job extends ZendX_JQuery_Form {
public function init() {
$element = new ZendX_JQuery_Form_Element_DatePicker('date_from');
$element->setLabel('Campaign Period From :');
$element->setRequired(true);
$element->setAttrib('size', '10');
$element->setJQueryParam('dateFormat', 'yy-mm-dd');
$this->addElement($element);
}
}
I got this help from http://framework.zend.com/manual/en/zend.form.decorators.html
jQuery Decorators: Beware the Marker
Interface for UiWidgetElements
By default all the jQuery Form
elements use the
ZendX_JQuery_Form_Decorator_UiWidgetElement decorator for rendering the jQuery
element with its specific view helper.
This decorator is inheritly different
from the ViewHelper decorator that is
used for most of the default form
elements in Zend_Form. To ensure that
rendering works correctly for jQuery
form elements at least one decorator
has to implement the
ZendX_JQuery_Form_Decorator_UiWidgetElementMarker
interface, which the default decorator
does. If no marker interface is found
an exception is thrown. Use the marker
interface if you want to implement
your own decorator for the jQuery form
element specific rendering.
But i need code to implement this, please suggest
Got my answer:-
I used
public static $formJQueryElements = array(
array('UiWidgetElement', array('tag' => '')), // it necessary to include for jquery elements
array('Errors'),
array('Description', array('tag' => 'span')),
array('HtmlTag', array('tag' => 'td')),
array('Label', array('tag' => 'td', 'class' =>'element')),
array(array('row' => 'HtmlTag'), array('tag' => 'tr')),
);
$form->getElement('jq_date')->setDecorators(Decorator::$formJQueryElements);
this works well for tabular alignment, for jquery elements !!!!!