Yii 1.1: Client-side form validation using Twitter Bootstrap's Popovers

11 followers

Introduction

This article explains how to easily turn standard text-line validation errors into beautifully and professionally looking Twitter Bootstrap's 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 Javascript function to power our popovers. For example something like this:

/**
 * 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 necessary 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:

$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 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 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.

Total 4 comments

#16613 report it
jcagentzero at 2014/03/10 10:14pm
Works Great!!!

Thank you for your unbelievable efforts! After reading the comments below, I was able to use popovers.

#16599 report it
jcagentzero at 2014/03/09 11:35pm
Error Encountered using TbActiveForm

I am having this error.

htmlspecialchars() expects parameter 1 to be string, array given

#15964 report it
waitforit at 2014/01/04 03:07pm
Error

Setting the errorOptions() array within htmlOptions results in an exception: htmlspecialchars() expects parameter 1 to be string, array given

EDIT

Put the errorOptions() stuff in the clientOptions() array in the form definition. Works Great!!!

#13517 report it
samilo at 2013/06/03 09:46am
Client-side

I have question if I stop the java script on my browser , form will still validate ?

Leave a comment

Please to leave your comment.

Write new article