I was recently (today if it’s Friday) asked to build a simple JS/HTML/CSS “to-do” application using whatever tools I wanted as long as it wasn’t a copy of an application elsewhere. Well, it’s time to get working! And I thought this offered a unique opportunity to build that application and document it via a tutorial for all you guys that are beginners in Knockout. Let’s define a few things and talk about the overview and we can get started ūüôā

Overview

Let’s define the goals of the application:

  • store task list
  • each task will have the following properties:
    • Completed/Pending status
    • Title
  • Each task can be added
  • Each task can be removed

What tools are we using this to build this?

  • Knockout JS
  • CSS3
  • HTML5

What’s not required?

  • persistent storage
  • cross-browser compatibility (since it’s a quick app)

Step 1

In step 1, we will throw together some HTML to hold our application and define the general look. We’ll focus on the UI once the JS to convert input into the tasks. I created a pretty thorough header that does all kinds of things (like add DC Core, random meta stuff, and create a structure). The main part we’re concerned with here is the body of the page:

 <!-- Input Container where new tasks can be added -->
  <section id="input-container">
    <form>
      <input type="text" name="task-input" id="task-input" />
      <input type="submit">
    </form>
  </section>
  <!-- end input-container --> 

  <!-- Task Container where tasks are located -->
  <section id="task-container">
      <h2>Tasks</h2>
      <div class="task-list" >
      <article class="task"> <span class="checkbox">[]</span><span class="task-name" ></span> <span class="task-date" ></span><span class="task-remove">X</span> 
      </article>
      </div>
  </section>
  <footer>
  Created by AntJanus
  </footer>

I included a few sample tasks to work with when using CSS on it.

Adding JS

There are several JS files I want to include. jQuery for UI, such as animation and stuff like that, Knockout.JS for MVC, and Head.JS for ¬†script loading, caching, and everything else. Let’s add those files (stored locally).

First, I’ll load Head.js so I can load the rest of the scripts. It can shave up to 1 second off loading time. I’m adding most of my JS into a function after the scripts load. What that basically does is skip that whole “jQuery(document).ready” function because Head.js loads the scripts after that function fires.

<script src="js/head.js" type="text/javascript"></script>
<script type="text/javascript">
// loading my scripts
head.js(
{jquery: "js/jquery.js"}, 
{knockout: "js/knockout.js"},
function(){
// code to add after both libs above are loaded
});
</script>

Prep HTML for Knockout and basic declarations

So, to have knockout working efficiently, we have to prep the HTML for it and we have to create a class to deal with all the different states of the app:

  • pending/done
  • date placement (today, yesterday, earlier)

And functions that affect the state of the app:

  • change completion
  • delete

Reminds me of my Java days to be honest ūüėČ So here’s the meat of it to display the data:

     <div class="task-list" data-bind="foreach: tasks">
      <article class="task"> <span class="checkbox">[]</span><span class="task-name" data-bind="text: task"></span> <span class="task-date" data-bind="text: date"></span><span class="task-remove">X</span> 
      </article>
      </div>

And here’s the model of it in javascript along with two sample objects:

// what's a task? class declaration
function todoTask(task, date){
	var self = this;
	self.task = task;
	self.date = date;
	self.completion = false;

}

function taskViewModel(){

	self.tasks = ko.observableArray([
		new todoTask("Pickup Milk", "6/15/2012"),
		new todoTask("Call Bank", "6/15/2012")
	]);	
}

ko.applyBindings(new taskViewModel());

});

Let me explain these real quick. The div that contains the data-bind=”foreach: tasks”¬†is basically the beginning of a “foreach” loop inside the div. When the div ends (</div>) it’s like an¬†endforeach. Everything inside is a template for the task. Each variable attached to each task is printed out overhere from “task” to “date”. We’ll add the “remove” function and make the checkbox work later.

The javascript is pretty self-explanatory if you’re used to OOP. The first function (todoTask) is like a class, or an object. It’s a function that takes in data ( task and date), and stores it along with ¬†a default “false” for completion. As you can see, the vars “task” and “date” are printed out in the HTML “text: task“.

The next piece of javascript deals with the “view” (or the data that will be passed to the html). I set up a basic “observableArray” (an array that will hold our tasks and change when tasks are added or subtracted) and added some basic data. As you can see, I used our previous “todoTask()” function to create two tasks and have them part of the observableArray called “tasks”. The “tasks” array is referenced in the foreach aboveforeach: tasks. That breaks it down nicely. Phew. The last function puts ALL of this code into effect.

 Adding New Tasks

Okay, it’s time to put on our big-boy underwear and add some new tasks because picking up milk and calling the bank does not take an eternity. To do this, we’ll need a couple of things. A “today’s date” function, a function to add data, and hooking up the html to that data function.

First, I’m going to modify our “todoTask” object so that it can accept and store the raw date() object but still print out nicely showing date:

function todoTask(task, rawDate){
	var self = this;
	self.task = task;
	// needs to be parsed to be viewed
	self.rawDate = rawDate; 
	self.completion = false;
	self.date = ko.computed(function(){
		var day = rawDate.getDate();
		var month = rawDate.getMonth()+1;
		var year = rawDate.getFullYear();
		var dateFull = month + '/' + day + '/' + year;
		return dateFull;
	});
}

Okay, so now, instead of just accepting a pre-formatted date. It accepts the full date. What this allows is keeping a full date object that can be easily reformatted later. It’s for organization. Let’s see how we changed the taskViewModel:

function taskViewModel(){

	// task array and defaults
	self.tasks = ko.observableArray([
		new todoTask("Pickup Milk", new Date()),
		new todoTask("Call Bank", new Date())
	]);	

	// Input Data 
	self.newTask = "";

	//Let's add some data
	 self.addTask = function() {

        self.tasks.push(new todoTask(self.newTask, new Date()));
    }
}

Okay, you’ll notice the sample data has a “new Date” object there instead of a particular date. If I had more time and wanted to make this more thorough, I’d set the date for each task so that there’s some variety. Alas, that’s not one of my tasks in this project ūüôā Next, you’ll notice an empty variable newTask that accepts input. We could make this into its own object but simplicity sake, we’ll keep it inside the taskViewModel object since we only have one input and one submit button.

The newTask¬†variable is used in the next function called¬†addTask that pushes the new¬†todoTask object to our observable Array that holds our tasks. You’ll notice the new¬†Date()¬†object being passed through.

    <form>
      <input type="text" name="task-input" id="task-input" data-bind="value: newTask"/>
      <input type="button" data-bind="click: addTask" />
    </form>

This part changed a bit so that the button triggers the addTaskfunction and the input field holds the¬†newTask value. Sweet. If you try the app out the way it is right now, it’ll work pretty well. We only have a FEW things to make this work ūüôā

Ew Tasks, get rid of them!

Let’s remove those pesky tasks that we just DON’T want to do. ¬†Also, let’s add up the number of tasks and show them up top (you probably forgot that part). What this basically involves is binding a click to a function that removes the task from the array. Easy-peasy. However, I did encounter a problem and it took me full 20 minutes to realize what I did. Here’s the complete code for the function so far (with a few improvements):

// what are we looking at again?
function taskViewModel(){

	var self = this;

	// task array and defaults
	self.tasks = ko.observableArray([
		new todoTask("Pickup Milk", new Date()),
		new todoTask("Call Bank", new Date())
	]);	

	// Input Data - Observable so that it can be reset
	self.newTask = ko.observable('');

	//Let's add some data
	 self.addTask = function() {
        self.tasks.push(new todoTask(self.newTask(), new Date()));
		//input reset after submit
		self.newTask('');
    }

	//remove task
	self.removeTask = function(){
		self.tasks.remove(this);
	}
}

If you can’t see it, here’s a hint. I kept referring to “self” but never defined “self” as being “this”. I decided to jump ahead and change a few things since I was working on it so much. Here are the improvements:

  • added “self = this”
  • made the variable “newTask” observable so that I could later reset it once a task was filled out and submitted
  • added “removeTask”

removeTask is a function, the same way as¬†addTask¬†and as you can see it just removes a task from the array. Here’s the html:

<article class="task"> <span class="checkbox">[]</span><span class="task-name" data-bind="text: task"></span> <span class="task-date" data-bind="text: date"></span> <a href="javascript:void(0)" class="task-remove" data-bind="click: $root.removeTask">X</a> 
      </article>

Since the element was clickable, I decided to make it an anchor. Notice the $root. It’s a variable that refers to the main view/model of the application.

Javascript Wrap-up

Phew. Just a couple of things to wrap up before we can start skinning. The first is the counter that I mentioned before. The second being the “completed/pending” status and possible sorting feature. Blahhh, okay. I’m so ready to jump on the UI (in the next article). Okay, here we go.

First, let’s add the “completed/pending” feature. Here’s the JS:

self.completion = ko.observable(false);

//task completion toggle
	self.completeTask = function(){
		if(self.completion() == false){
		self.completion(true);
		}
		else{
		console.log('false answer');
		}
	};

This goes into the¬†function todoTask , as you can see I had to change the¬†completion variable to be observable. Knockout will update our UI when this variable changes (we’ll see that in the HTML). The next function checks the current state of the variable and changes it. Sweet! That’s our basic mechanism. Let’s put it to action:

 <article class="task" data-bind=" css: { completed: completion() == true}"> <span class="checkbox" data-bind="click: completeTask"></span>

This is a portion of the foreach¬†loop that we’re concerned with right now. The main class of each task gets updated with the “completed” class when the ¬†completion variable is true. The span element following it will trigger our toggle function.

Phew. Okay, one last thing, right? Let’s add a counter of tasks left (tasks that have a “completion” of false). The JS:

self.completedTasks = ko.computed(function(){
		var total = 0;
		var i = 0;
		for (i; i < self.tasks().length; i++){
			if(self.tasks()[i].completion() == false){
				total++;	
			}
		}

		return total;
	});

Perhaps not the most efficient method right now but it works. Add it to your main ViewModel. What this little piece of code does is create a for loop that counts all of the array elements which have their¬†completion¬†variable set to false. The computed¬†function is akin to observable in that the variable will be recomputed whenever something changes. Sweet. Let’s print that data out:

 <h3>You've <span class="tasks-left" data-bind="text: completedTasks" > </span> to do</h3>

Now the UI

I’ll cover this part (or perhaps not at all) elsewhere. ūüôā Here’s the demo and here’s the Github repository for the files.