Step 2 - Creating a basic ToDo List

Ok let's create the small ToDo list application. We'll end up with this final application:

But in this step we'll focus on the display of the initial list of Todos.

Creating a ToDo Model

To start let's create a list of ToDo items in JavaScript. Later we'll pull this data from the server, but for now let's create a static JavaScript array with the todo items:

function pageController($scope, $http, $timeout) {

    var vm = this;

    // Interface
    vm.todos = [
        {
            title: "SWFox Angular Presentation",
            description: "Try to show up on time this time",
            completed: false
        },
        {
            title: "Do FoxPro presentation",
            description: "Should go good, let's hope I don't fail...",
            completed: false
        },
        {
            title: "Do raffle at the end of day one",
            description: "Should go good, let's hope I don't fail...",
            completed: false
        },
        {
            title: "Arrive at conference",
            description: "Arrive in Phoenix and catch a cab to hotel",
            completed: true
        }
    ];
    
    // Initialization
    initialize();

    return;
    
    ...
}

Next let's go to the HTML page. We'll need a little CSS styling to format the examples. The following can be added to the <head> section of the todo.html page:

There's also some additional styling needed as we build this example, so you can add this to the <head> of the page:

<style>
    #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;
    }

    .todo-item {
        padding: 10px;
        margin-left: 10px;
        border-bottom: 1px solid silver;
        transition: height 1s;
    }

    .todo-header {
        font-weight: bold;
        font-size: 1.2em;
        color: #457196;
    }

    .todo-content {
        padding-left: 40px;
    }

    .completed {
        text-decoration: line-through;
        color: grey !important;
        font-style: italic;
        opacity: 0.6;
    }
</style>

We can now databind this todo list created by the model's todo array. We can bind into the HTML by creating a repeating list of Todo items.

To do this I'll use the ng-repeat directive:

<div ng-repeat="todo in vm.todos"
     class="todo-item">

    <div class="pull-left">
        <i class="fa fa-bookmark-o"></i>                     
    </div>

    <div class="todo-content">
        <div class="todo-header">
            {{ todo.title}}
        </div>
        <div>
            {{todo.description}}
        </div>
    </div>
</div>

You can think of ng-repeat as a for each operation as it iterates over an array. ng-repeat iterates over an array of items and creates a variable that we can then bind to in Angular expressions. So ng-repeat="todo in vm.todos" creates a todo variable that you can then bind with expressions like {{ todo.title}}.

Without doing anything else we can now see the todo items like this in the browser:

Two-way DataBinding

This seems pretty simple and straight-forward, but what you don't see is that this list is actually two way databound, meaning that if you change a the value of the array or an element in the array that value is immediately reflected.

To demonstrate let's make a few changes. First I'm going to add some logic so that we can display the completed status of our ToDo item. I'll use the ng-class directive to affect the CSS class that our items render with.

<div ng-repeat="todo in vm.todos"
     class="todo-item"
     ng-class="{'completed': todo.completed}">

the ng-class allows you to programmatically determine whether a CSS class is applied or not. You write an object literal with properties for the CSS class name and a boolean value that determines whether the class is rendered into the class attribute of the element. So here I'm using the completed CSS style I defined earlier and make it dependent on whether the todo.completed property is true or false.

The page now looks like this:

Now that we can display the completion status of a todo item, let's demonstrate how we can easily toggle it.

Add the following checkbox before the .todo-content element:

<input type="checkbox"
       class="pull-right"
       ng-model="todo.completed"/>

This checkbox uses the ng-model directive to bind the todo.completed from the model to the checkbox. The result of this simple change is:

Not only do you see the checkbox value reflected from the static list, but if you now click on any of the checkboxes you'll see the display immediately change from the normal display to the completed display.

This is a pretty cool idea, right? You are effectively making a change in the UI (checkbox click) that directly updates your underlying model (vm.todo[x].completed), which in turn updates the UI to reflect the changed value. All without writing any code - we haven't written any JavaScript code to update any UI elements.

The key to this is Angular's two-way databinding which makes it possible to avoid writing a lot of JavaScript code to update your HTML UI. This very idea promotes coding to a model, rather than to HTML as you typically have to do when using raw DOM or jQuery for example.

Declaritive Actions

The checkbox is an example of behavior that is implicit - you click on the checkbox and the data-bound value automatically updates your model, which in turn is reflected immediately in the list display.

But of course you can also declaratively make changes. So, instead of clicking the checkbox, let's imagine we want to just click the icon on the left to toggle the value programmatically when we click.

Let's go back to our pageController and add this function to the bottom after initialize():

function toggleCompleted(todo) {
    todo.completed = !todo.completed;
}

Then attach this function to the model:

vm.toggleCompleted = toggleCompleted;

Separating Declaration and Implementation

I could have also written:

vm.toggleCompleted = function(todo) { todo.completed = !todo.completed; }

>which would have the same effect. Howver, I prefer the convention of not attaching code directly to the model, which makes for cleaner code. This follows the <a href="http://weblogs.asp.net/dwahlin/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-module-pattern" target="top">Revealing Module Pattern</a> which is meant to separate interface from implementation.

With the code handler in place we can attach an `ng-click` directive to handle the click on the icon to switch comleted status. To do this we'll change the icon `<div>`:

```html
<div class="pull-left" ng-click="vm.toggleCompleted(todo)">
    <i class="fa "
       style="cursor: pointer"
       ng-class="{'fa-bookmark-o': !todo.completed,'fa-bookmark': todo.completed}"></i>
</div>

The key thing is the ng-click handler. On the click we call the vm.toggleCompleted() method and we pass the active todo instance to the method. This is another cool feature of Angular - it can track actual model values for you and pass those values into a function for you.

So now the explicit JavaScript code in the vm.toggleCompleted() function flips the completed status rather than the implicit binding in the checkbox.

Now when you click on the bookmark icon, you'll see the todo item's display status toggle in the same way it did with the checkbox.

Removing a Todo

Let's also add the ability to remove a ToDo. To do this we'll need a button/icon and another model method that handles the removal. So lets add the function:

function removeTodo(todo, ev) {
    var index = vm.todos.indexOf(todo);
    if (index > -1)
        vm.todos.splice(index, 1);
}

This code looks for a matching todo instance in the list of todos and if it finds removes it using the splice() function of the JavaScript array object.

We can then hook it up the model:

vm.removeTodo = removeToDo;

Next in the HTML let's replace the checkbox we used earlier to toggle the state, with a remove icon like this:

<div class="pull-right" 
     title="click to remove this ToDo item."
     ng-click="vm.removeTodo(todo)">
    <i class="fa fa-remove" style="color: darkred; cursor: pointer"></i>
</div>

And that's it. Here's what the page now looks like:

Summary

In these steps you've seen:

  • ng-repeat and model list binding
  • how two-way databinding affects the UI
  • how to use ng-model to update your model
  • how to use programmatic model updates
  • how to use ng-class to control display behavior

In the next step we add adding new ToDos to this list.


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