Lesson 7: Forms

 

Forms in Bootstrap are the biggest paradigm shift for us as ColdFusion developers. We all use cfform and its cfinput tags to perform standard form functionality and validation. Bootstrap forces us to use HTML5 instead of ColdFusion to perform these tasks. This is actually a good thing. Using the defined HTML5 methods gives us more control over how things are validated.

Another major shift in thinking is using tables to layout your forms. This has been the one exception to the "don't use tables" rule. The cool thing is Bootstrap allows us to use their grid system - or a version of it - to layout forms quickly and easily. We will cover that in "part 2" below.

Part 1: The Fields

Let's start easy here and look over the standard markup for form fields.

Text Input

<label for="yourname">Your name</label>
<input class="form-control" name="yourname" id="yourname" type="text" placeholder="Enter your name">

Nothing too fancy here - just a plain ordinary text field. Let me draw your attention to three key items here.

  1. The label tag is not strictly required but is highly recommended. Note that the value of the for attribute is the same as the value of the field's id attribute.
  2. The id attribute should be there as it is used for HTML5 validation and adds accessibility.
  3. The class is set to form-control. That's really all you need to make your form elements use Bootstrap's visual style!
  4. By default fields in Bootstrap are as wide as their parent container. So of you have one in a col-xl-12 column it will be as wide as the whole layout. If you forget and just plop the field into a blank HTML page it will span the width of the viewport.


Date Select

<label for="yourdate">Your date</label>
<input class="form-control" name="yourdate" id="yourdate" type="date" placeholder="">

E-mail Input

<label for="youremail">Your e-mail address</label>
<input class="form-control" name="youremail" id="youremail" type="email" placeholder="Enter your e-mail address">

This one is pretty nifty. It automatically validates the input as an e-mail address.


Password Input

<label for="yourpass">Your password</label>
<input class="form-control" name="yourpass" id="yourpass" type="password" placeholder="Enter your password">

Readonly Input

<label for="yourssn">Your SSN</label>
<input class="form-control" name="yourssn" id="yourssn" type="text" placeholder="Enter your SSN" readonly>

Readonly plain-text Input

<label for="yourcard">Your card number</label>
<input class="form-control-plaintext" name="yourcard" id="yourcard" type="text" placeholder="Enter your card number" readonly>

This one is useful if you want to display data but not let the user know it is a form field.


Textarea Input

<label for="yournotes">Your notes</label>
<textarea class="form-control" name="yournotes" id="yournotes" rows="3"></textarea>

Like the input fields, the textarea always spans 100% of its parent container. You can adjust the height by changing the value of the rows attribute.


Select Box


<label for="yourfav">Pick one</label>
<select class="form-control" id="yourfav" name="yourfav">
    <option>Select one</option>
    <option value="1">Star Wars</option>
    <option value="2">Star Trek</option>
    <option value="3">Stargate</option>
    <option value="4">Other</option>
</select>

Checkboxes - stacked layout

<div class="form-check">
  <input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
  <label class="form-check-label" for="defaultCheck1">
    Default checkbox
  </label>
</div>
<div class="form-check">
  <input class="form-check-input" type="checkbox" value="" id="defaultCheck2" disabled>
  <label class="form-check-label" for="defaultCheck2">
    Disabled checkbox
  </label>
</div>

Checkboxes - inline layout

<div class="form-check form-check-inline">
  <input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
  <label class="form-check-label" for="inlineCheckbox1">1</label>
</div>
<div class="form-check form-check-inline">
  <input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
  <label class="form-check-label" for="inlineCheckbox2">2</label>
</div>
<div class="form-check form-check-inline">
  <input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled>
  <label class="form-check-label" for="inlineCheckbox3">3 (disabled)</label>
</div>

Radio Buttons - stacked layout

<div class="form-check">
  <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1" checked>
  <label class="form-check-label" for="exampleRadios1">
    Default radio
  </label>
</div>
<div class="form-check">
  <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios2" value="option2">
  <label class="form-check-label" for="exampleRadios2">
    Second default radio
  </label>
</div>
<div class="form-check">
  <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios3" value="option3" disabled>
  <label class="form-check-label" for="exampleRadios3">
    Disabled radio
  </label>
</div>

Radio Buttons - inline layout

<div class="form-check form-check-inline">
  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
  <label class="form-check-label" for="inlineRadio1">1</label>
</div>
<div class="form-check form-check-inline">
  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
  <label class="form-check-label" for="inlineRadio2">2</label>
</div>
<div class="form-check form-check-inline">
  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled>
  <label class="form-check-label" for="inlineRadio3">3 (disabled)</label>
</div>

Part 2: Form Layout

Now we will take the form field elements and put them into an attractive and useful layout. Bootstrap uses a slightly different version of its grid system for form layout.

Each row of fields is contained within a form-row and each field is contained in a form-group which also uses the grid col-{breakpoint}-{column-number} grid class format. You can apply margins and padding to this grid in the same way you would a standard layout grid.

The coolest part of the form grid is that it functions the same as the layout grid - so your forms are instantly mobile-friendly!


<form>

    <div class="form-row">
    	<div class="form-group col-xl-6">
    	    <input type="text" class="form-control" placeholder="First name" name="firstName"  id="firstName">
    	</div>
        <div class="form-group col-xl-6">
            <input type="text" class="form-control" placeholder="Last name" name="lastName"  id="lastName">
        </div>
    </div>
    
    <div class="form-row">
        <div class="form-group col-xl-12">
            <input type="email" class="form-control" placeholder="E-mail address" name="eMail"  id="eMail">
        </div>
    </div>
    
    <div class="form-row">
        <div class="form-group col-xl-4">
            <input type="text" class="form-control" placeholder="MAJCOM" name="Majcom"  id="Majcom">
        </div>    
        <div class="form-group col-xl-4">
            <input type="text" class="form-control" placeholder="Department" name="Dept"  id="Dept">
        </div>
        <div class="form-group col-xl-4">
            <input type="text" class="form-control" placeholder="Rank" name="Rank"  id="Rank">
        </div>
    </div>

</form>

Part 3: Form Validation

Once you get a handle on using Bootstrap to validate your forms, I think you'll agree that it is pretty simple. At first - and if you try to follow their documentation - you might find it confusing.

The first thing I recommend is applying the class needs-validation to your form tag. Then add the keyword novalidate to the form tag. Now, be confused for a second....needs-validation...novalidate? Yes, they cancel out. The needs-validation class tells Bootstrap to validate the form and novalidate tells HTML5 not to validate the form.

To validate a field, add the required keyword to the field then, beneath the field add a DIV with the class invalid-feedback containing the message you want displayed if the user doesn't fill out the field. You can also add a DIV with the valid-feedback class to provide a message if they do fill it in correctly. Personally, I rarely do this - but it changes asynchronously as the user corrects the fields they miss - which is kinda neat.

One last note about that JavaScript: You don't need to edit it in any way. What you see here works globally for any form on your page flagged with that needs-validation class.

Looks good!
Please provide your first name
Looks good!
Please provide your last name
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
You must agree before submitting.
<form class="needs-validation" novalidate>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationCustom01">First name</label>
      <input type="text" class="form-control" id="validationCustom01" value="" required>
      <div class="valid-feedback">
        Looks good!
      </div>
      <div class="invalid-feedback">
        Please provide your first name.
      </div>
    </div>
    <div class="col-md-6 mb-3">
      <label for="validationCustom02">Last name</label>
      <input type="text" class="form-control" id="validationCustom02" value="" required>
      <div class="valid-feedback">
        Looks good!
      </div>
      <div class="invalid-feedback">
        Please provide your last name.
      </div>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationCustom03">City</label>
      <input type="text" class="form-control" id="validationCustom03" required>
      <div class="invalid-feedback">
        Please provide a valid city.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationCustom04">State</label>
      <select class="custom-select" id="validationCustom04" required>
        <option selected disabled value="">Choose...</option>
        <option>...</option>
      </select>
      <div class="invalid-feedback">
        Please select a valid state.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationCustom05">Zip</label>
      <input type="text" class="form-control" id="validationCustom05" required>
      <div class="invalid-feedback">
        Please provide a valid zip.
      </div>
    </div>
  </div>
  <div class="form-group">
    <div class="form-check">
      <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
      <label class="form-check-label" for="invalidCheck">
        Agree to terms and conditions
      </label>
      <div class="invalid-feedback">
        You must agree before submitting.
      </div>
    </div>
  </div>
  <button class="btn btn-primary" type="submit">Submit form</button>
</form>

<script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
  'use strict';
  window.addEventListener('load', function() {
    // Fetch all the forms we want to apply custom Bootstrap validation styles to
    var forms = document.getElementsByClassName('needs-validation');
    // Loop over them and prevent submission
    var validation = Array.prototype.filter.call(forms, function(form) {
      form.addEventListener('submit', function(event) {
        if (form.checkValidity() === false) {
          event.preventDefault();
          event.stopPropagation();
        }
        form.classList.add('was-validated');
      }, false);
    });
  }, false);
})();
</script>

Part 4: Curiosities

There are a few additional elements and oddities with using Bootstrap forms. Thankfully there are some simple work-arounds.

Field Sizes

Yep - there are three possible sizes to your form fields. Small, default, and large - of course. The large fields come in handy if you know your app will be used on a mobile device. The small ones would be better used in an admin environment or in a layout where space is limited.

Setting a field to small or large is very easy. Just add the form-control-sm or form-control-lg class chained after the usual form-control class and the size will change.

<input class="form-control form-control-lg" type="text" placeholder=".form-control-lg">
<input class="form-control" type="text" placeholder="Default input">
<input class="form-control form-control-sm" type="text" placeholder=".form-control-sm">

The Custom File Field

I think we can all agree that Bootstrap form fields are much prettier than normal fields. The one exception to that rule is the file browser field. Every browser has a different way of displaying this poor thing. Thankfully we can standardize it with Bootstrap!

Here's the normal file field:

Here's the "custom" field in Bootstrap.
<div class="custom-file">
  <input type="file" class="custom-file-input" id="customFile">
  <label class="custom-file-label" for="customFile">Choose file</label>
</div>


<script>
$('#customFile').on('change',function(){
    //get the file name
    var fileName = $(this).val();
    //replace the "Choose a file" label
    $(this).next('.custom-file-label').html(fileName);
})
</script>

You should note that there is a chunk of jQuery associated with this field. Without it the field will still work but the name of the file the user selects will not appear in the field. Add the script, edit the $('#customFile') to be the ID of your field and you are in business.


Custom Checkboxes

You can pretty-up the standard radio buttons and checkboxes using the custom-control-input class.

<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheck1" name="mycheck" value="1">
  <label class="custom-control-label" for="customCheck1">Check this custom checkbox</label>
</div>
<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheck2" name="mycheck" value="2">
  <label class="custom-control-label" for="customCheck2">Check this custom checkbox</label>
</div>
<div class="custom-control custom-radio">
  <input type="radio" id="customRadio1" name="customRadio" class="custom-control-input" value="1">
  <label class="custom-control-label" for="customRadio1">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio">
  <input type="radio" id="customRadio2" name="customRadio" class="custom-control-input" value="2">
  <label class="custom-control-label" for="customRadio2">Or toggle this other custom radio</label>
</div>
Custom Radio and Checkboxes - inline

To display custom radio buttons or checkboxes inline, just add the custom-control-inline class to the containing DIV.

<div class="custom-control custom-radio">
  <input type="radio" id="customRadio1" name="customRadio" class="custom-control-input" value="1">
  <label class="custom-control-label" for="customRadio1">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio">
  <input type="radio" id="customRadio2" name="customRadio" class="custom-control-input" value="2">
  <label class="custom-control-label" for="customRadio2">Or toggle this other custom radio</label>
</div>
Switches

One of the new things you see on the web these days is the "switch" style field. These are pretty neat and would really work well with allowing users to select a yes/no or on/off option. The switch is actuallty a skinned checkbox. As with any checkbox, if it's not checked then it doesn't exist when the form is submitted.

You can use my little JavaScript voodoo to change the label when the switch is checked.

<div class="custom-control custom-switch">
  <input type="checkbox" class="custom-control-input" id="customSwitch1">
  <label class="custom-control-label" for="customSwitch1">Toggle this switch element</label>
</div>

<script>
$('#customSwitch1').change(function() {
	//set a variable of the state of the switch - this returns true/false
	var switchstate = $(this).prop('checked');
	if(switchstate == true)
		{document.getElementById("customSwitch1Label").innerHTML = "Yes, please notify me!";}
	else if(switchstate == false)
		{document.getElementById("customSwitch1Label").innerHTML = "Would you like to be notified?";}
})
</script>

Custom Select

This one is pretty simple. It adds the primary color of Bootstrap to the selected element and changes the arrow.

You can apply the size changes to the select boxes by adding custom-select-lg or custom-select-sm in chain with the custom-select class.

<select class="custom-select">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

Accessible Forms and Screen Readers

Screen readers will have trouble with your forms if you don't include a label for every input. Since we are bound by the lovely Section 508 guidelines you must use - or start using if you haven't been - label tags to correspond with your form fields.

Sometimes these labels get in the way. Here's an example

@

See how the username and password labels get in the way? Getting them out of there is simple! And no, you can't just remove the label text because the size and shape of the label still exists. Just add the class sr-only to the label itself and it will only appear on screen readers.

This class can be used to hide ANYTHING you want to display to screen readers alone.

@
<form class="form-inline">
  <label class="sr-only" for="inlineFormInputName2">Name</label>
  <input type="text" class="form-control mb-2 mr-sm-2" id="inlineFormInputName2" placeholder="Jane Doe">

  <label class="sr-only" for="inlineFormInputGroupUsername2">Username</label>
  <div class="input-group mb-2 mr-sm-2">
    <div class="input-group-prepend">
      <div class="input-group-text">@</div>
    </div>
    <input type="text" class="form-control" id="inlineFormInputGroupUsername2" placeholder="Username">
  </div>

  <div class="form-check mb-2 mr-sm-2">
    <input class="form-check-input" type="checkbox" id="inlineFormCheck">
    <label class="form-check-label" for="inlineFormCheck">
      Remember me
    </label>
  </div>

  <button type="submit" class="btn btn-primary mb-2">Submit</button>
</form>

A couple of notes about the code above since it shows off two additional features you will find useful.

  1. This is an example of an inline form. Note the class form-inline added to the form tag. Because we are displaying the form in a single line it does not need any form-row or form-group tags.
  2. Note the super groovy field asking for the username with the @ appended to the field. This is an "input group". Because this is so cool I will give you a separate couple of snippets for this below.

$
.00

<div class="input-group mb-3">
  <div class="input-group-prepend">
    <span class="input-group-text">$</span>
  </div>
  <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
  <div class="input-group-append">
    <span class="input-group-text">.00</span>
  </div>
</div>

Please check the box.


<label for="agree" class="sr-only"></label>

<div class="input-group mb-3">
  <div class="input-group-prepend">
    <div class="input-group-text">
      <input type="checkbox" aria-label="Checkbox for following text input" required>
      <div class="invalid-feedback pl-1">Please check the box.</div>
    </div>
  </div>
  <label for="agreetext" class="sr-only"></label>
  <input id="agreetext" type="text" class="form-control" aria-label="Text input with checkbox" placeholder="I agree with the terms and conditions" readonly>
</div>

Constraint Validation

This is one of those things that when you find out that it exists and how easy is it to use that it makes you kind of mad and excited at the same time. Constraint validation....this is actually an HTML5 thing that the W3 added. It allows you to validate fields against each other with a couple of lines of code. No jQuery or javascript needed.

Let's say you have a form wherein you want to make sure the user enter their e-mail address two times to make sure they get it right. Normally you would need to add a script to do this and it can be a huge mess. All you need to do with this new method is add an event to your form tag.

Here's a simple example:

Please provide a valid e-mail address.
Please provide a valid e-mail address.


<form oninput="email2.setCustomValidity(email2.value != email1.value ? 'Email addresses do not match.' : '');">

<div class="form-row">
    <div class="form-group col-xl-6">
        <label for="email1">Enter your e-mail address</label>
        <input type="email" class="form-control" placeholder="First name" name="email1"  id="email1">
    </div>
    
    <div class="form-group col-xl-6">
        <label for="email2">Enter your e-mail address again</label>
        <input type="email" class="form-control" placeholder="Last name" name="email2"  id="email2">
    </div>
</div>
    
    <input class="btn btn-primary" type="submit" value="Submit!">

</form>


Here's a quick breakdown of what this is doing....

oninput="email2.setCustomValidity(email2.value != email1.value ? 'Email addresses do not match.' : '');"

The oninput event triggers when a user enters something into the specified input field. Then we name the second field by ID, "email2" and set a custom validity through HTML5 if the value of the second field isn't equal to the value of the first field. If you are really sharp you will recognize the syntax is the ternary operator value = value ? 'do this if true' : 'do this if false'. So we check if the values are equal and if they aren't it adds a custom message to the field. Super simple!


That pretty much covers everything you need to implement Bootstrap forms into your apps. The snippets herein are perfect for snippets in your text editor too.