Difference between #2 and #3 of Client-side form validation using Twitter Bootstrap's Popovers

unchanged
Title
Client-side form validation using Twitter Bootstrap's Popovers
unchanged
Category
How-tos
unchanged
Tags
client-side, validation, bootstrap, Twitter Bootstrap
changed
Content
## Introduction

This article explains how to easily turn standard text-line validation errors
into beautifully and professionally looking [Twitter Bootstrap's
Popovers](http://twitter.github.io/bootstrap/javascript.html#popovers).

Solution is quite easy (very easy) and can be implemented in nearly every
project, no matter which Bootstrap extension for Yii you're using
(_Yii-Bootstrap_, _Yii-Booster_, _YiiStrap_). It can be also easily implemented,
if you're not using it at all, i.e. if you're adding Twitter Bootstrap to your
project manually, by referencing its CSS and JS files.

## The code

### Javascript code

We need a quite simple Javascript function to power our
popovers.function. For example something like this:

~~~
[php]
/**
 * Adds client-side forms validation as Bootstrap's Popovers.
 */
function afterValidateAttribute(form, attribute, data, hasError)
{
    var field = (attribute.hasOwnProperty('id')) ? attribute['id'] : '';

    if(field !== '')
    {
        var text = (data.hasOwnProperty(field)) ? data[field] : '';
        field = '#' + field;

        if(hasError && (text !== ''))
        {
            var
                tTemp = '',
                dotTemp = '';

            /**
             * We use a trick with temporary disabling title, if user is
also 
             * using tooltip for this field. Our popover would share title used 
             * in that tooltip, which is rather unwanted effect, right?
             */
            if($(field).attr('rel') == 'tooltip')
            {
                tTemp = $(field).attr('title');
                dotTemp = $(field).attr('data-original-title');
                
                $(field).attr('title', '');
                $(field).attr('data-original-title', '');
            }
            
            /**
             * 'destroy' is necessaryneccessary
here, if your field can have more than one
             * validation error text, for example, if e-mail field can't be
empty
             * and entered value must be a valid e-mail; in such cases, not
using
             * .popover('destroy') here would result in incorrect validation
errors
             * being displayed for such field.
             */    
            $(field)
                .popover('destroy')
                .popover
                ({
                    trigger : 'manual',
                    content : text
                })
                .popover('show');
                
            if($(field).attr('rel') == 'tooltip')
            {
                $(field).attr('title', tTemp);
                $(field).attr('data-original-title', dotTemp);
            }
        }
        else $(field).popover('destroy');
    }
}
~~~

You can register it using _clientScript_ or simply put it to any of your *.js
files and reference it in you layout.

### PHP Code

In your form's widget definition (i.e. in line starting with `$form =
$this->beginWidget('CActiveForm', array()`) and
`'enableClientValidation'=>true` to configuration array.

Then create another array like this:

~~~
[php]
$htmlOptions = array
(
	'class'=>'span5',
	'errorOptions'=>array
	(
		'errorCssClass'=>'',
		'successCssClass'=>'',
		'validatingCssClass'=>'',
		'style'=>'display: none',
		'hideErrorMessage'=>TRUE,
		'afterValidateAttribute'=>'js:afterValidateAttribute',
	)
);
~~~

Finally, we change our field definition into

~~~
[php]
<?php echo($form->textFieldRow($model, 'login', $htmlOptions)); ?>
~~~

Note. I'm using `afterValidateAttribute` event here, instead of
`clientValidation`, because I don't find it to useful in this context. It is
more useful for pushing error messages to a general array, which then will be
printed out to screen, then actually serving the entie process of client
validation.

## Some strange issues and side-effects

Presented solution works best, when you're using Twitter Bootstrap extension
only for additional GUI elements like Popovers, but you're building your form
using standard `CActiveForm`. If you decide to use `TbActiveForm` you may run
into several strange issues, as in current implementation, it has some bugs.

Both `'hideErrorMessage'=>TRUE,` and custom classes
(`'errorCssClass'=>''`, `'successCssClass'=>''`,
`'validatingCssClass'=>''`) are respected only during client validation
stage. After you submit your form, they're all ignored. Error messages are
displayed and fields are getting their `error` class.

You can workaround first problem (`'hideErrorMessage'=>TRUE` being ignored)
with adding (`'style'=>'display: none'`) just as I did. You can't do much for
second problem and until this is fixed, you have to live on, with the fact that
after submit you'll see your form fields decorated with default, not with your
custom CSS classes. Pity!

## Summary

My solution is based on Javascript code, that is fired internally by Yii
(`afterValidateAttribute`) and also by Yii it is fead with proper data. This is
why this solution works only for client side validation -- i.e. Bootstrap
Popovers take role of validation errors rendered dynamically by Javascript. I
have no idea, how to use them to display validation errors that are rendered by
PHP, after form submit.

Using [Twitter Bootstrap's
Popovers](http://twitter.github.io/bootstrap/javascript.html#popovers) to
display client-side validation errors instead of standard line errors does seems
to be a good idea. But keep in mind that this solution works best when you're
dealing with `CActiveForm`. Expect many strange issues when you use
`TbActiveForm`.

Let's hope that next-generation Bootstrap for Yii (Yiistrap + YiiWheels) will
have all such issues fixed.