Sometime the user need to fill multiple fields in form, also each of them must be unique.
Any of them has to be different to the remaining. You might think of using the different validator which requires two fields to
be different to each other, such as an username and password shouldn't be the same. In
our case, the different validator isn't useful because the number of fields for doing
comparison are unknown.
The form in this example asks user to provide some email addresses. User is also required
to fill at least one email address. Each of them, if present, must be unique. This kind
of form can be seen in a lot of forms nowadays.
The approach illustrated in this example is that:
Use the emailAddress validator to ensure
each field need to be a valid email address
Use the callback validator to check if the list
of email addresses consist duplicated item
If all fields pass these validators, we then use the updateStatus() method to set them as valid fields
That are straight forward steps. The next sections show the implementation in details.
Checking duplicate items in array
In order to check whether an array contains duplicated items or not, we can use the easy
way suggested by Rhett Anderson :
function hasDuplicatedItems ( inputArray ) {
var obj = {},
numItems = inputArray . length ,
duplicateRemoved = [];
for ( var i = 0 ; i < numItems ; i ++ ) {
obj [ inputArray [ i ]] = 0 ;
}
for ( i in obj ) {
duplicateRemoved . push ( obj [ i ]);
}
return duplicateRemoved . length === numItems ;
}
Since the array of email addresses might consist of empty item, we need to adjust the
code above a little bit to ensure that the array has at least one not-empty item and
doesn't contain any duplicated items:
// Assume that $emails are the list of email elements
var numEmails = $emails . length ,
notEmptyCount = 0 ,
obj = {},
duplicateRemoved = [];
for ( var i = 0 ; i < numEmails ; i ++ ) {
var v = $emails . eq ( i ). val ();
if ( v !== '' ) {
obj [ v ] = 0 ;
notEmptyCount ++ ;
}
}
for ( i in obj ) {
duplicateRemoved . push ( obj [ i ]);
}
if ( duplicateRemoved . length === 0 ) {
// All the items are empty
} else if ( duplicateRemoved . length !== notEmptyCount ) {
// The list of emails have duplicated items
}
Using the same names
The example code below demonstrates the implementation when all email fields use the same names ,
email[]
, for example.
It also uses the dynamic message feature that
allows to show a different message depending on a particular condition:
$ ( '#profileForm' ). formValidation ({
fields : {
'email[]' : {
err : '#messageContainer' ,
validators : {
emailAddress : {
message : 'The value is not a valid email address'
},
callback : {
callback : function ( value , validator , $field ) {
...
if ( duplicateRemoved . length === 0 ) {
return {
valid : false ,
message : 'You must fill at least one email address'
};
} else if ( duplicateRemoved . length !== notEmptyCount ) {
return {
valid : false ,
message : 'The email address must be unique'
};
}
// Set all fields as valid
validator . updateStatus ( 'email[]' , validator . STATUS_VALID , 'callback' );
return true ;
}
}
}
}
}
})
The code also shows an usage of the err option which
displays the message at the given container. This option also ensures that all the
messages are shown once in case we use the same name for fields. If this option is
omitted, then you will see different messages for each of invalid field.
< form id = "profileForm" method = "post" class = "form-horizontal" >
< div class = "form-group" >
< label class = "col-xs-3 control-label" > Emails</ label >
< div class = "col-xs-5" >
< input type = "text" class = "form-control" name = "email[]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control" name = "email[]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control" name = "email[]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control" name = "email[]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control" name = "email[]" />
</ div >
</ div >
<!-- Message container -->
< div class = "form-group" >
< div class = "col-xs-9 col-xs-offset-3" >
< div id = "messageContainer" ></ div >
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< button type = "submit" class = "btn btn-default" > Submit</ button >
</ div >
</ div >
</ form >
< script >
$ ( document ). ready ( function () {
$ ( '#profileForm' ). formValidation ({
framework : 'bootstrap' ,
icon : {
valid : 'glyphicon glyphicon-ok' ,
invalid : 'glyphicon glyphicon-remove' ,
validating : 'glyphicon glyphicon-refresh'
},
fields : {
'email[]' : {
err : '#messageContainer' ,
validators : {
emailAddress : {
message : 'The value is not a valid email address'
},
callback : {
callback : function ( value , validator , $field ) {
var $emails = validator . getFieldElements ( 'email[]' ),
numEmails = $emails . length ,
notEmptyCount = 0 ,
obj = {},
duplicateRemoved = [];
for ( var i = 0 ; i < numEmails ; i ++ ) {
var v = $emails . eq ( i ). val ();
if ( v !== '' ) {
obj [ v ] = 0 ;
notEmptyCount ++ ;
}
}
for ( i in obj ) {
duplicateRemoved . push ( obj [ i ]);
}
if ( duplicateRemoved . length === 0 ) {
return {
valid : false ,
message : 'You must fill at least one email address'
};
} else if ( duplicateRemoved . length !== notEmptyCount ) {
return {
valid : false ,
message : 'The email address must be unique'
};
}
validator . updateStatus ( 'email[]' , validator . STATUS_VALID , 'callback' );
return true ;
}
}
}
}
}
});
});
</ script >
Using different names
What if the email fields have different
names ? How we can set the validator rules for them?
In this case, we can use a same CSS class for all email fields
< input type = "text" class = "form-control userEmail" name = "user.email[0]" />
< input type = "text" class = "form-control userEmail" name = "user.email[1]" />
< input type = "text" class = "form-control userEmail" name = "user.email[2]" />
< input type = "text" class = "form-control userEmail" name = "user.email[3]" />
<!-- and so forth -->
and then use the selector option to apply the
same set of validation rules for them:
$ ( '#profileForm' ). formValidation ({
fields : {
emails : {
// All email fields have .userEmail class
selector : '.userEmail' ,
err : '#messageContainer' ,
validators : {
...
}
}
}
});
< form id = "profileForm" method = "post" class = "form-horizontal" >
< div class = "form-group" >
< label class = "col-xs-3 control-label" > Emails</ label >
< div class = "col-xs-5" >
< input type = "text" class = "form-control userEmail" name = "user.email[0]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control userEmail" name = "user.email[1]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control userEmail" name = "user.email[2]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control userEmail" name = "user.email[3]" />
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< input type = "text" class = "form-control userEmail" name = "user.email[4]" />
</ div >
</ div >
<!-- Message container -->
< div class = "form-group" >
< div class = "col-xs-9 col-xs-offset-3" >
< div id = "messageContainer" ></ div >
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< button type = "submit" class = "btn btn-default" > Submit</ button >
</ div >
</ div >
</ form >
< script >
$ ( document ). ready ( function () {
$ ( '#profileForm' ). formValidation ({
framework : 'bootstrap' ,
icon : {
valid : 'glyphicon glyphicon-ok' ,
invalid : 'glyphicon glyphicon-remove' ,
validating : 'glyphicon glyphicon-refresh'
},
fields : {
emails : {
// All email fields have .userEmail class
selector : '.userEmail' ,
err : '#messageContainer' ,
validators : {
emailAddress : {
message : 'The value is not a valid email address'
},
callback : {
callback : function ( value , validator , $field ) {
var $emails = validator . getFieldElements ( 'emails' ),
numEmails = $emails . length ,
notEmptyCount = 0 ,
obj = {},
duplicateRemoved = [];
for ( var i = 0 ; i < numEmails ; i ++ ) {
var v = $emails . eq ( i ). val ();
if ( v !== '' ) {
obj [ v ] = 0 ;
notEmptyCount ++ ;
}
}
for ( i in obj ) {
duplicateRemoved . push ( obj [ i ]);
}
if ( duplicateRemoved . length === 0 ) {
return {
valid : false ,
message : 'You must fill at least one email address'
};
} else if ( duplicateRemoved . length !== notEmptyCount ) {
return {
valid : false ,
message : 'The email address must be unique'
};
}
validator . updateStatus ( 'emails' , validator . STATUS_VALID , 'callback' );
return true ;
}
}
}
}
}
});
});
</ script >
Supporting dynamic fields
The last section shows how to keep the code above working with dynamic fields. We can add
or remove field by using the addField() and removeField() methods, respectively.
You can look at the Adding dynamic field
example to see how these methods are used in action.
< form id = "profileForm" method = "post" class = "form-horizontal" >
< div class = "form-group" >
< label class = "col-xs-3 control-label" > Emails</ label >
< div class = "col-xs-5" >
< input type = "text" class = "form-control" name = "email[]" />
</ div >
< div class = "col-xs-4" >
< button type = "button" class = "btn btn-default addButton" >< i class = "fa fa-plus" ></ i ></ button >
</ div >
</ div >
<!-- The template containing an email field and a Remove button -->
< div class = "form-group hide" id = "emailTemplate" >
< div class = "col-xs-offset-3 col-xs-5" >
< input class = "form-control" type = "text" name = "email[]" />
</ div >
< div class = "col-xs-4" >
< button type = "button" class = "btn btn-default removeButton" >< i class = "fa fa-minus" ></ i ></ button >
</ div >
</ div >
<!-- Message container -->
< div class = "form-group" >
< div class = "col-xs-9 col-xs-offset-3" >
< div id = "messageContainer" ></ div >
</ div >
</ div >
< div class = "form-group" >
< div class = "col-xs-5 col-xs-offset-3" >
< button type = "submit" class = "btn btn-default" > Submit</ button >
</ div >
</ div >
</ form >
< script >
$ ( document ). ready ( function () {
$ ( '#profileForm' )
. formValidation ({
framework : 'bootstrap' ,
icon : {
valid : 'glyphicon glyphicon-ok' ,
invalid : 'glyphicon glyphicon-remove' ,
validating : 'glyphicon glyphicon-refresh'
},
fields : {
'email[]' : {
err : '#messageContainer' ,
validators : {
emailAddress : {
message : 'The value is not a valid email address'
},
callback : {
callback : function ( value , validator , $field ) {
var $emails = validator . getFieldElements ( 'email[]' ),
numEmails = $emails . length ,
notEmptyCount = 0 ,
obj = {},
duplicateRemoved = [];
for ( var i = 0 ; i < numEmails ; i ++ ) {
var v = $emails . eq ( i ). val ();
if ( v !== '' ) {
obj [ v ] = 0 ;
notEmptyCount ++ ;
}
}
for ( i in obj ) {
duplicateRemoved . push ( obj [ i ]);
}
if ( duplicateRemoved . length === 0 ) {
return {
valid : false ,
message : 'You must fill at least one email address'
};
} else if ( duplicateRemoved . length !== notEmptyCount ) {
return {
valid : false ,
message : 'The email address must be unique'
};
}
validator . updateStatus ( 'email[]' , validator . STATUS_VALID , 'callback' );
return true ;
}
}
}
}
}
})
// Add button click handler
. on ( 'click' , '.addButton' , function () {
var $template = $ ( '#emailTemplate' ),
$clone = $template
. clone ()
. removeClass ( 'hide' )
. removeAttr ( 'id' )
. insertBefore ( $template ),
$email = $clone . find ( '[name="email[]"]' );
// Add new field
$ ( '#profileForm' ). formValidation ( 'addField' , $email );
})
// Remove button click handler
. on ( 'click' , '.removeButton' , function () {
var $row = $ ( this ). closest ( '.form-group' ),
$email = $row . find ( '[name="email[]"]' );
// Remove element containing the email
$row . remove ();
// Remove field
$ ( '#profileForm' ). formValidation ( 'removeField' , $email );
});
});
</ script >