Symfony: Add custom classes to form elements when failing validation?
When Symfony finds that your form is invalid, it shows it again but with the addition of errors for each element that failed validation. The errors are basically just unordered lists:
<label>First Name</label>
<ul class='error-list'>
<li>Required.</li>
</ul>
<input type='text' name='first_name'/>
I'm trying to figure out if there is some way to force Symfony to also add custom classes to whatever elements I want when they fail validation. For example add a class='error'
to my label
or input
when the v开发者_Go百科alidation fails. That way I can style those elements.
I started looking at form schema decorators but at first glance it doesn't seem like there is a way to do it. But I could be wrong.
Is there anyway to accomplish this?
If your app requires javascript to be enabled, then the easiest and more flexible way to go is to use javascript to through in some classes/attributes dynamically. For example, you could have a script like this
jQuery(document).ready(function($){
if($('.sf_admin_form .error_list').length>0 ){ // style if erro is present
$('.sf_admin_form .error_list').each(function(){
// label of fields with error should be bold and red
$(this).prev('label').css('font-weight', 'bold');
$(this).prev('label').css('color', 'red');
});
}
});
If you do need not count on javascript being enabled, than your choice is use a custom form formatter. The following is an example.
/**
* Class derived from Table formatter, renders customized version if the row has errors.
*
* @package symfony
* @subpackage widget
* @author Paulo R. Ribeiro <paulo@duocriativa.com.br>
*/
class sfWidgetFormSchemaFormatterCustom extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat = "<tr>\n <th>%label%</th>\n <td>%error%%field%%help%%hidden_fields%</td>\n</tr>\n",
// THIS IS NEW
$rowWithErrorsFormat = "<tr class='has-errors'>\n <th class='has-errors'>%label%</th>\n <td class='has-errors'>%error%%field%%help%%hidden_fields%</td>\n</tr>\n",
//
$errorRowFormat = "<tr><td colspan=\"2\">\n%errors%</td></tr>\n",
$helpFormat = '<br />%help%',
$decoratorFormat = "<table>\n %content%</table>";
$errorListFormatInARow = " <ul class=\"error_list\">\n%errors% </ul>\n",
$errorRowFormatInARow = " <li>%error%</li>\n",
$namedErrorRowFormatInARow = " <li>%name%: %error%</li>\n",
public function formatRow($label, $field, $errors = array(), $help = '', $hiddenFields = null)
{
if(count($erros)==0){ // no errors, renders as usual
return strtr($this->getRowFormat(), array(
'%label%' => $label,
'%field%' => $field,
'%error%' => $this->formatErrorsForRow($errors),
'%help%' => $this->formatHelp($help),
'%hidden_fields%' => null === $hiddenFields ? '%hidden_fields%' : $hiddenFields,
));
} else { // has errors, through in some classes
return strtr($this->getRowWithErrorsFormat(), array(
'%label%' => $label,
'%field%' => $field,
'%error%' => $this->formatErrorsForRow($errors),
'%help%' => $this->formatHelp($help),
'%hidden_fields%' => null === $hiddenFields ? '%hidden_fields%' : $hiddenFields,
));
}
}
public function getRowWithErrorsFormat()
{
return $this->rowWithErrorsFormat;
}
}
To enabled the custom formatter for all the forms, use the ProjectConfiguration class to set it up.
// /config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
/// CODE FOR ENABLING PLUGINS...
// configure your default form formatter
sfWidgetFormSchema::setDefaultFormFormatterName('custom');
}
}
I found another solution using the hasError() method.
<?php if ($form['email']->hasError()) {
echo $form['email']->render( array('class'=>'error') );
} else {
echo $form['email']->render();
} ?>
hasError()
apparently checks to see if there are errors for that form element or not.
Instead of applying a condition to every form field you could globally overide the form_label block:
{%- block form_row -%}
<div>
{{- form_label(form) -}}
{{- form_widget(form) -}}
</div>
{%- endblock form_row -%}
{%- block form_label -%}
{% if label is not sameas(false) -%}
{% if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif %}
{% if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif %}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
{% if errors|length > 0 %}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' error_class')|trim}) %}
{% endif %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}
And in your config:
twig:
form:
resources:
- 'Form/fields.html.twig'
精彩评论