Client-side form validation using Twitter Bootstrap's Popovers

  1. Introduction
  2. The code
  3. Some strange issues and side-effects
  4. Summary

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.

4 0
9 followers
Viewed: 45 474 times
Version: 1.1
Category: How-tos
Written by: Trejder
Last updated by: Trejder
Created on: Apr 24, 2013
Last updated: 11 years ago
Update Article

Revisions

View all history

Related Articles