Step 3 - Using Forms and Validation to Add new ToDo Items

In this step we'll add the ability to add new ToDo items by using a form at the top of the page to enter new Todos.

In order to do this we'll need to store the data for the 'active' ToDo in the model so that we can bind to it. So as the first step lets add a new property to our model:

vm.activeTodo = {
    title: null,
    description: null,
    completed: false
};

We also want a method called addToDo:

// top of controller
vm.addTodo = addTodo;

// in implementation section
function addTodo() {
    // copy the todo - important!
    var td2 = $.extend({}, vm.activeTodo);

    // and add it to the model array
    vm.todos.splice(0, 0, td2);

    // clear out the form values
    vm.activeTodo.title = null;
    vm.activeTodo.description = null;
}

This code copies the todo into a distinct copy of the activeTodo. This is necessary since JavaScript objects are references and otherwise everytime you added a new Todo you'd just overwrite the same instance. By copying you create a new instance which is then separate.

The new ToDo is then added to the array at the top and finally the activeTodo is cleared out, ready for the next new one to be entered.

Next let's create the HTML for the input form which uses Bootstrap and FontAwesome layout features, plus a few Angular directives.

<div class="well">
    <form name="form1" id="form1" novalidate>
        <div class="form-group">
            <div class="input-group">
                <span class="input-group-addon"><i class="fa fa-bookmark"></i> </span>
                <input type="text" class="form-control"
                       name="name"
                       ng-model="vm.activeTodo.title"
                       placeholder="Enter the title for this ToDo"
                       
                       required />
            </div>
        </div>

        <div class="form-group">                        
            <textarea class="form-control"
                      name="description"
                      style="height: 100px"
                      ng-model="vm.activeTodo.description"
                      ng-minlength="10"
                      required
                      placeholder="Enter the description for this placeholder">
                      </textarea>
        </div>

        <button class="btn btn-primary"
                ng-click="vm.addTodo()"
                ng-disabled="form1.$invalid">
            <i class="fa fa-plus" ></i> 
            Add Todo
        </button>
    </form>
</div>

Notice that both input controls are bound using ng-model to vm.activeTodo.title and vm.activeTodo.description respectively, so any value you type immediately updates the activeTodo model object. The button then has an ng-click that fires the addTodo() method that we'll use to add the new item to the list.

Your form should now look like this:

If you type a title and entry, you should now be able to click the Add Todo button to add a new Todo item to the list. When you click the button, the model is updated and the new item added to the array.

Easy, huh?

Form Validation

Notice that the form shows red and green background colors to indicate valid input. The Add Todo button also is disabled until the form is valid.

This is handled by Angular's form validation features that I've enabled on this form. Let's go over some of these simple features.

First you'll want to turn off native Form validations in browsers. These validations tend to interfere with any custom validation you do so add novalidate to your form declaration:

<form name="form1" id="form1" novalidate>

Next you can use a number of built in validations to specify that specific fields need to validate.

<input type="text" 
       name="name"
       ng-model="vm.activeTodo.title"
       ng-minlength="5"
       required/>

Note that in order to identify controls by name in Angular Validation, you need to use the name attribute rather than id to identify the control which has thrown me a for a loop on a few occasions.

A couple of values that are used here are:

  • required - field cannot be empty
  • ng-minlength - the minimum input required

So in the form above if the title is empty or the description is less than 10 characters long the form is considered invalid and can't be submitted.

Angular automatically validates certain HTML input types like type=email or type=number (text, number, url, email, date, radio, checkbox) as well as some attributes (required, pattern, minlength, maxlength, min, max). You can also implement custom validators by creating a directive which is a bit more complex.

You can also reference the form in a given controller scope as form1 (where form1 is the form's name) and each control within the form such as form1.title or form1.description in our example. Remember it's the name attribute not id that enables this naming structure. Each of these controls then has a control state like form1.title.$touched or form1.title.$invalid and you can bind to these values in the UI.

It's quite surprising what you can accomplish with these few directives and combinations without ever writing a single line of validation code.

Validation Error Styling

The various validation states of controls cause Angular to apply certain CSS styles to the controls. When you run the example you see the input control turn green on valid and red on invalid input which is controlled in the styles I initially set up.

To affect the input and textarea controls I'm using:

#form1 input.ng-invalid.ng-touched, #form1 textarea.ng-invalid.ng-touched {
    background-color: #ffced1;
}

#form1 input.ng-valid.ng-touched, #form1 textarea.ng-valid.ng-touched {
    background-color: #baffc3;
}

Again, this happens without code as Angular updates the state of the input controls and the styling automatically updates accordingly as soon as the validation state changes.

This is very powerful in that it allows you do many things without any code based logic in the background, letting the two way bindings and style updates manage the UI for you.


© West Wind Technologies, 1996-2018 • Updated: 02/07/16
Comment or report problem with topic