30

I have recently purchased and am using Bootstrap FormValidation from http://formvalidation.io/ and using the example on http://formvalidation.io/examples/requiring-at-least-one-field/ I am trying to set up my for the require EITHER an email or a phone number but I am not able to get the example to work correctly. No matter what I do I see an error message saying "You must enter at least one contact method" only under the Primary Email field.

If the FULL code would be helpful I can post but here are the relevant code snippets.

<div class="form-group">
    <label class="control-label" for="primaryEmail">Primary Email</label>
    <input type="text" class="form-control contactMethod" id="primaryEmail" 
                               name="primaryEmail" value="" placeholder="Enter email">
</div>
<div class="form-group">
    <label class="control-label" for="cPhone">Cell Phone</label>
    <input type="text" class="form-control contactMethod" id="cPhone" name="cPhone" 
            value="" placeholder="Enter cell phone">
</div>

Validation section of the javascript

$('#form').formValidation({
            framework: 'bootstrap',
            icon: {
                valid: 'glyphicon glyphicon-ok',
                invalid: 'glyphicon glyphicon-remove',
                validating: 'glyphicon glyphicon-refresh'
            },
            fields: {

                cPhone: {
                    validators: {
                        phone: {
                            country: 'country',
                            message: 'The value is not valid %s phone number'
                        }
                    }
                },
                primaryEmail: {
                    validators: {
                        emailAddress: {
                            message: 'The value is not a valid email address'
                        }
                    }
                },
                secondaryEmail: {
                    validators: {
                        emailAddress: {
                            message: 'The value is not a valid email address'
                        }
                    }
                },
                wPhone: {
                    validators: {
                        phone: {
                            country: 'country',
                            message: 'The value is not valid %s phone number'
                        }
                    }
                },
                contact : {
                    selector: '.contactMethod',
                    validators: {
                        callback: {
                            message: 'You must enter at least one contact method',
                            callback: function(value, validator, $field) {
                                var isEmpty = true,
                                // Get the list of fields
                                $fields = validator.getFieldElements('contact');
                                console.log($fields);
                                for (var i = 0; i < $fields.length; i++) {
                                    if ($fields.eq(i).val() !== '') {
                                        isEmpty = false;
                                        break;
                                    }
                                }

                                if (!isEmpty) {
              // Update the status of callback validator for all fields
              validator.updateStatus('contact', validator.STATUS_VALID, 'callback');
                                    return true;
                                }

                                return false;
                            }
                        }
                    }
                }
            }
        });

In the exmaple the line $fields = validator.getFieldElements('cm'); has cm replaced with email but it did appear to be anything but an arbitrary label. But it maybe more than a label that matches the validator.updateStatus('cm', validator.STATUS_VALID, 'callback'); line. cm has been changed to contact

All other validations seem to be working on the page.

UPDATE:

if I dump $fields to the console right after $fields = validator.getFieldElements('cm'); I get "input=([name="primaryEmail"])" I would have thought it would have been an object with both primaryEmail and cPhone.

UPDATE 5-18-15 first the HTML then the scripts. I have made things even more difficult by adding a thrid option into the mix but the use can use a cell phone, work phone or primary email as a contact method one one is required.

<div class="form-group">
    <label class="control-label" for="primaryEmail">Primary Email <i class="fa fa-asterisk text-warning"></i></label>
    <input type="text" class="form-control contactMethod" id="primaryEmail" name="primaryEmail" value="" placeholder="Enter email" data-fv-field="contactMethod">
</div>
<div class="form-group">
    <label class="control-label phoneMask" for="cPhone">Cell Phone <i class="fa fa-asterisk text-warning"></i></label>
    <input type="text" class="form-control contactMethod" id="cPhone" name="cPhone" value="" placeholder="Enter cell phone" data-fv-field="contactMethod">
</div>
<div class="form-group">
    <label class="control-label phoneMask" for="wPhone">Work Phone <i class="fa fa-asterisk text-warning"></i></label>
    <input type="text" class="form-control contactMethod" id="wPhone" name="wPhone" value="" placeholder="Enter work phone" data-fv-field="contactMethod">
</div>

I have tried several scripts :

Here is the one that most closely resembled the example on http://formvalidation.io/examples/requiring-at-least-one-field/

$('#leadForm').formValidation({
    framework: 'bootstrap',
    icon: {
        valid: 'glyphicon glyphicon-ok',
        invalid: 'glyphicon glyphicon-remove',
        validating: 'glyphicon glyphicon-refresh'
    },
    fields: {
        fName: {
            validators: {
                notEmpty: {
                    message: 'The first name is required'
                },
                stringLength: {
                    min: 2,
                    max: 30,
                    message: 'The first name must be more than 2 and less than 30 characters long'
                }
            }
        },
        lName: {
            validators: {
                notEmpty: {
                    message: 'The last name is required'
                },
                stringLength: {
                    min: 2,
                    max: 30,
                    message: 'The last name must be more than 2 and less than 30 characters long'
                }
            }
        },
        secondaryEmail: {
            validators: {
                emailAddress: {
                    message: 'The value is not a valid email address'
                }
            }
        },
        contactMethod: {
            selector: '.contactMethod',
            validators: {
                callback:  function(value, validator, $field) {
                        var isEmpty = true,
                            isValid = false,
                            returnIsValid = false,
                            // Get the list of fields
                            $fields = validator.getFieldElements('contactMethod'),
                            fv = $('#leadForm').data('formValidation');
                        for (var i = 0; i < $fields.length; i++) {
                            thisField = $fields[i].id;
                            // trim value of field
                            thisVal = jQuery.trim($('#'+thisField).val());

                            if(thisVal.length == 0){
                               console.log('empty '+thisField);
                                fv.updateStatus(thisField, 'INVALID', 'callback').updateMessage(thisField,validator,'test');
                            } else {
                                if(thisField == 'cPhone' || thisField == 'wPhone'){
                                    console.log('validating '+thisField);
                                } else if(thisField == 'primaryEmail'){
                                    console.log('validating '+thisField);
                                }
                            }



                            if ($fields.eq(i).val() !== '') {
                                isEmpty = false;
                                break;
                            }
                        }


                        if (!isEmpty) {
                            // Update the status of callback validator for all fields
                            validator.updateStatus('contactMethod', validator.STATUS_VALID, 'callback');
                            returnIsValid = false;
                        } else {

                        }
                        return returnIsValid;
                }
            }
        }
    }

}).on('success.form.fv', function(e) {
    e.preventDefault();
    var $form = $(e.target),
        fv    = $form.data('formValidation');
        // console.log($form.serialize());
        // console.log(fv);
    $.ajax({
        type: 'post',
        url: 'api/add.jsp?surveyId='+cfg['lead.surveyID'],
        data: $form.serialize(),
        dataType: 'json',
        async: false,
        success: function(result){
            console.log(result);

            }
        } 
    });     
});

This one more closely resembles what @Béranger had suggested. It actually comes very close but since so much of it is on the keyup it isn't triggered on the click of the submit button. I have tried adding.

$('#leadForm').formValidation({
    framework: 'bootstrap',
    icon: {
        valid: 'glyphicon glyphicon-ok',
        invalid: 'glyphicon glyphicon-remove',
        validating: 'glyphicon glyphicon-refresh'
    },
    fields: {
        primaryEmail: {
            validators: {
                notEmpty: {
                    message: 'You must include at least one contact method'
                },
                emailAddress: {
                    message: 'The value is not a valid email address'
                }
            }
        },
        cPhone: {
            validators: {
                notEmpty: {
                    message: 'You must include at least one contact method'
                },
                phone: {
                    country: 'country',
                    message: 'The value is not valid %s phone number'
                }
            }
        },
        wPhone: {
            validators: {
                notEmpty: {
                    message: 'You must include at least one contact method'
                },
                phone: {
                    country: 'country',
                    message: 'The value is not valid %s phone number'
                }
            }
        }
    }
})
.on('keyup', '[name="primaryEmail"], [name="cPhone"], [name="wPhone"]', function(e) {
    var cPhoneIsEmpty = jQuery.trim($('#leadForm').find('[name="cPhone"]').val()) === '',
        wPhoneIsEmpty = jQuery.trim($('#leadForm').find('[name="wPhone"]').val()) === '',
        primaryEmailIsEmpty = jQuery.trim($('#leadForm').find('[name="primaryEmail"]').val()) === '',
        fv = $('#leadForm').data('formValidation');

    var cPhoneIsValid = fv.isValidField('cPhone') === true ? true : false;
    var wPhoneIsValid = fv.isValidField('wPhone') === true ? true : false;
    var primaryEmailIsValid = fv.isValidField('primaryEmail') === true ? true : false;

    switch ($(this).attr('name')) {
        // User is focusing the primaryEmail field
        case 'primaryEmail':
            fv.enableFieldValidators('cPhone', primaryEmailIsEmpty).revalidateField('cPhone');
            fv.enableFieldValidators('wPhone', primaryEmailIsEmpty).revalidateField('wPhone');

            break;

        // User is focusing the cPhone field
       case 'cPhone':
            fv.enableFieldValidators('primaryEmail', cPhoneIsEmpty).revalidateField('primaryEmail');
            fv.enableFieldValidators('wPhone', cPhoneIsEmpty).revalidateField('wPhone');

            break;

        // User is focusing the cPhone field
       case 'wPhone':
            fv.enableFieldValidators('primaryEmail', wPhoneIsEmpty).revalidateField('primaryEmail');
            fv.enableFieldValidators('cPhone', wPhoneIsEmpty).revalidateField('cPhone');

            break;

        default:
            break;
    }

    // if( (cPhoneIsValid || wPhoneIsValid || primaryEmailIsValid)){
    //  fv.enableFieldValidators('primaryEmail', false, 'notEmpty').revalidateField('primaryEmail');
    //  fv.enableFieldValidators('cPhone', false, 'notEmpty').revalidateField('cPhone');
    //  fv.enableFieldValidators('wPhone', false, 'notEmpty').revalidateField('cPhone');
    // } else {
    //  fv.enableFieldValidators('primaryEmail', true).revalidateField('primaryEmail');
    //  fv.enableFieldValidators('cPhone', true).revalidateField('cPhone');
    //  fv.enableFieldValidators('wPhone', true).revalidateField('cPhone');
    // }

    // fv.enableFieldValidators('primaryEmail', true);
    // fv.enableFieldValidators('cPhone', true);
    // fv.enableFieldValidators('wPhone', true);

}).on('success.form.fv', function(e) {
    e.preventDefault();
    // console.log('submit here');
    var $form = $(e.target),
        fv    = $form.data('formValidation');
        // console.log($form.serialize());
        // console.log(fv);
    $.ajax({
        type: 'post',
        url: 'api/add.jsp?surveyId='+cfg['lead.surveyID'],
        data: $form.serialize(),
        dataType: 'json',
        async: false,
        success: function(result){
            console.log(result);
        } 
    });     
});
Lance
  • 3,193
  • 2
  • 32
  • 49
  • 5
    If you've paid for it, you should contact their support. – emerson.marini May 06 '15 at 15:40
  • @Lance try the option that I have posted. – naim shaikh May 12 '15 at 16:25
  • I have posted additional scripts. It is getting very frustrating. @MelanciaUK. I have tried posting to their twitter account but the site has no actual "support" link or email. So it is frustrating. – Lance May 19 '15 at 05:16
  • @Lance support is on this repo [https://github.com/formvalidation/support](https://github.com/formvalidation/support) – Arkni May 19 '15 at 14:59
  • I have tried posting to the support on github but got no response. So this issue is still open (and frustrating) – Lance May 29 '15 at 21:35

6 Answers6

3

You can disable the second validation and make it enabled only when the first is wrong :

take a look at this link

<form id="profileForm" method="post">
    <p>Please provide one of these information:</p>

    <div class="form-group">
        <label class="control-label">Social Security Number</label>
        <input type="text" class="form-control" name="ssn" />
    </div>

    <div class="form-group text-center">&mdash; Or &mdash;</div>

    <div class="form-group">
        <label class="control-label">Driver's License Number</label>
        <input type="text" class="form-control" name="driverLicense" />
    </div>

    <div class="form-group">
        <button type="submit" class="btn btn-default">Submit</button>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#profileForm')
        .formValidation({
            framework: 'bootstrap',
            icon: {
                valid: 'glyphicon glyphicon-ok',
                invalid: 'glyphicon glyphicon-remove',
                validating: 'glyphicon glyphicon-refresh'
            },
            fields: {
                ssn: {
                    validators: {
                        notEmpty: {
                            message: 'Please provide the Social Security number'
                        },
                        regexp: {
                            regexp: /^(?!(000|666|9))\d{3}(?!00)\d{2}(?!0000)\d{4}$/,
                            message: 'The format of your SSN is invalid. It should be XXXXXXXXX with no dashes'
                        }
                    }
                },
                driverLicense: {
                    // Disable validators
                    enabled: false,
                    validators: {
                        notEmpty: {
                            message: 'Or the Drivers License number'
                        },
                        stringLength: {
                            min: 8,
                            max: 20,
                            message: 'The Drivers License number must be more than 8 and less than 20 characters long'
                        }
                    }
                }
            }
        })
        .on('keyup', '[name="ssn"], [name="driverLicense"]', function(e) {
            var driverLicense = $('#profileForm').find('[name="driverLicense"]').val(),
                ssn           = $('#profileForm').find('[name="ssn"]').val(),
                fv            = $('#profileForm').data('formValidation');

            switch ($(this).attr('name')) {
                // User is focusing the ssn field
                case 'ssn':
                    fv.enableFieldValidators('driverLicense', ssn === '').revalidateField('driverLicense');

                    if (ssn && fv.getOptions('ssn', null, 'enabled') === false) {
                        fv.enableFieldValidators('ssn', true).revalidateField('ssn');
                    } else if (ssn === '' && driverLicense !== '') {
                        fv.enableFieldValidators('ssn', false).revalidateField('ssn');
                    }
                    break;

                // User is focusing the drivers license field
                case 'driverLicense':
                    if (driverLicense === '') {
                        fv.enableFieldValidators('ssn', true).revalidateField('ssn');
                    } else if (ssn === '') {
                        fv.enableFieldValidators('ssn', false).revalidateField('ssn');
                    }

                    if (driverLicense && ssn === '' && fv.getOptions('driverLicense', null, 'enabled') === false) {
                        fv.enableFieldValidators('driverLicense', true).revalidateField('driverLicense');
                    }
                    break;

                default:
                    break;
            }
        });
});
</script>
Béranger
  • 671
  • 8
  • 23
  • So far this is the closest to actually solving the issue. I tried it last night and got it to partially work. I will try some more this evening and if I can get it going I will mark this answer and give bounty. I think I'm getting closer – Lance May 14 '15 at 15:56
  • Nice to read it. Let us know if You have others errors with it – Béranger May 15 '15 at 08:24
  • This is a good start but it still isn't work right. Big issue is that it is dependent on `keyup` if a user comes into the form to edit existing data and they have (in your case) an ssn but not a driver's license if they don't interact with those fields and hit the submit the key ip doesn't fire and the user is displayed a message under the driver's license say that one field is required even though the ssn is already filed out – Lance May 17 '15 at 00:10
1

Reading the documentation of getFieldElements it is not an arbitrary label. It is the name of the element you wish to select. It returns a jQuery[] so I am guessing underneath the hood it is just doing an attribute selection similar to $( "input[name*='elementname']" ); I am basing that on the fact that in their example both fields contain 'email' and that is the name they are selecting on. Granted that does not explain why 'cm' has anything returned but they may be doing some other magic.

I would suspect that if you changed the names of your contact fields to something like 'phoneContact' and 'emailContact'

<div class="form-group">
   <label class="control-label" for="emailContact">Primary Email</label>
   <input type="text" class="form-control contactMethod" id="primaryEmail" 
      name="emailContact" value="" placeholder="Enter email">
</div>
<div class="form-group">
   <label class="control-label" for="phoneContact">Cell Phone</label>
   <input type="text" class="form-control contactMethod" id="cPhone" name="phoneContact" 
      value="" placeholder="Enter cell phone">
</div>

And then changed your field name to 'contact' you should see both fields.

 //...
 $fields = validator.getFieldElements('contact');
 //.. 
 validator.updateStatus('contact', validator.STATUS_VALID, 'callback');
Jared
  • 2,736
  • 1
  • 18
  • 23
1

Since you're using different field types you can't group the validation using same validators for all input fields like they did in the example. Creating a custom validator for each field using callback then updating all fields to valid when input is detected worked for me. See javascript part of code...

<script>
$(document).ready(function() {
    $('#profileForm').formValidation({
        framework: 'bootstrap',
        icon: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        fields: {
            primaryEmail: {
                 validators: {
                    callback: {
                        message: 'You must enter atleast one field ',
                        callback: function(value, validator, $field) {
                       if ($("input[name=primaryEmail]").val()) {
                                // Update the status of callback validator for all fields
                                validator.updateStatus('primaryEmail', validator.STATUS_VALID, 'callback');
                                validator.updateStatus('cPhone', validator.STATUS_VALID, 'callback');
                                return true;
                            }
                             return false;
                        }
                    },
                    emailAddress: {
                        message: 'The value is not a valid email address'
                    }
                }
            },//primaryEmail
            cPhone : {
                validators : {
                    callback: {
                        message: 'You must enter atleast one field',
                        callback: function(value, validator, $field) {

                            if ($("input[name=cPhone]").val()) {
                                // Update the status of callback validator for all fields
                                validator.updateStatus('primaryEmail', validator.STATUS_VALID, 'callback');
                                validator.updateStatus('cPhone', validator.STATUS_VALID, 'callback');
                                return true;
                            }
                            return false;
                        }
                    },
                    stringLength: {
                        min: 8,
                        message: "Please enter a contact number with 8 digits"
                    }
                }
            }//cPhone
        }//fields
    });
        $('#profileForm').formValidation().on("success.form.bv", function (e) {
            e.preventDefault();
                    alert('success');
                    $('.form-group').addClass("hidden");
        });
});
</script>
Sen K
  • 11
  • 1
0

I'm not sure, but I think the "email" in getFieldElements is not arbitrary, but in fact is the name of the field "group" (just after fields: { ). What's your field group name? Is it cm?

Can you post the entire <script>?

Bruno Belotti
  • 2,414
  • 1
  • 31
  • 28
  • I think you and @jared are saying basically the same thing. I did try to modify my `cm` to be `contact` and called the group just after `field: {}` contact. Although that did make some changes to the way things were being handled it didn't make it work as expected. I will post full script this evening – Lance May 07 '15 at 18:40
  • Hmm, the code looks good to me. Silly question: did you try to invert the order, checking *first* the *"emptyness"* and then each field (basically moving the **contact validator** on top, just after the `field: {` )? – Bruno Belotti May 08 '15 at 13:47
0

After Inspecting the Code I found that you are missing the Attribute.

Here is the Valid HTML

<div class="form-group">
    <label class="control-label" for="primaryEmail">Primary Email</label>
    <input type="text" class="form-control contactMethod" id="primaryEmail"
        name="primaryEmail" value="" placeholder="Enter email" data-fv-field="contact" />
</div>
<div class="form-group">
    <label class="control-label" for="cPhone">Cell Phone</label>
    <input type="text" class="form-control contactMethod" id="cPhone" name="cPhone"
        value="" placeholder="Enter cell phone" data-fv-field="contact" />
</div>

You were missing the Attribute data-fv-field

naim shaikh
  • 1,103
  • 2
  • 9
  • 20
0

As @Sen k said in his answer:

Since you're using different field types you can't group the validation using same validators for all input fields...

A simple solution, you can use the event success.field.fv as described in the following code:

$('#YourFormId').formValidation({
    framework: 'bootstrap',
    icon: {
        valid: 'glyphicon glyphicon-ok',
        invalid: 'glyphicon glyphicon-remove',
        validating: 'glyphicon glyphicon-refresh'
    },
    fields: {
        primaryEmail: {
            validators: {
                notEmpty: {},
                emailAddress: {
                    message: 'The value is not a valid email address'
                }
            }
        },
        cPhone: {
            validators: {
                notEmpty: {},
                phone: {
                    country: 'country',
                    message: 'The value is not valid %s phone number'
                }
            }
        }
    }
})
.on('success.field.fv', function(e, data) {
    var primaryEmail = $('[name="primaryEmail"]'),
        cPhone       = $('[name="cPhone"]'),
        validator    = data.fv;

    if (data.field === "primaryEmail" && cPhone.val() === '') {
        // mark the cPhone field as valid if it's empty & the
        // primaryEmail field was valid.
        validator.updateStatus('cPhone', validator.STATUS_VALID, null);
    }

    if (data.field === "cPhone" && primaryEmail.val() === '') {
        // mark the primaryEmail field as valid if it's empty & the
        // cPhone field was valid.
        validator.updateStatus('primaryEmail', validator.STATUS_VALID, null);
    }
});

NOTE:

I used the notEmpty validator, because emailAdress and phone validators mark empty field as valid.

Arkni
  • 1,177
  • 9
  • 15