11. Example: Todo List Part 2 (Client-side w/ jQuery)

In Chapter 10, “Example: Todo List Part 1 (Server-side),” we had just finished writing the server-side components needed for us to write a todo list application. In this chapter we will write a front end for that application that will run in a browser. We’re going to do this using some pretty fun technology, such as Bootstrap1 from Twitter2 and jQuery.

Priming the HTML with Twitter Bootstrap

Let’s start by setting up some basic styles and HTML for our application. To help get our CSS and styling off to a good start, we are going to use the Bootstrap project from Twitter. Bootstrap is a simple set of CSS and JavaScript files to help you, well, bootstrap your application. It gives you a simple grid to use to help align the elements on the page. It also provides some nice styling for forms, buttons, lists, and much more. I highly recommend that you check out the project for full details about what it can offer you, because we are using only a tiny portion of it for this application.

The first thing we need to do is update our src/views/index.ejs to use Bootstrap, as well as our own CSS, so we can make the few tweaks necessary for our application.

Example: (source: app.1/src/views/index.ejs)


<!DOCTYPE html>
<html>
  <head>
    <title>Todos</title>
    <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
    <%- css('/application') %>
  </head>
<body>

</body>
</html>


As you can see, we linked to the CSS file that Bootstrap offers us. We also have a rather unusual bit of dynamic code in there, a function called css, to which we are passing /application. That is how we will be adding our custom CSS, and later our CoffeeScript, into our application.

To do this we are going to use the connect-assets NPM module by Trevor Burnham.3 This will provide a couple of hooks by which we automatically find our CSS and CoffeeScript files and add them into the HTML. In the case of CoffeeScript, it automatically converts them to JavaScript for us, saving us a step of compiling the files manually ourselves.


Tip

For those of you familiar with the asset-pipeline in Ruby on Rails, the connect-assets module attempts to mimic that in an Express application.


For connect-assets to find our assets, we need to place them in a folder called assets at the root of our application. So let’s do that now, and while we’re at it, let’s place a little bit of CSS in there to help pretty up the HTML we’re going to write.

Example: (source: app.1/assets/application.css)


#todos li {
  margin-bottom: 20px;
}

#todos li .todo_title {
  width: 800px;
}

#todos li .completed {
  text-decoration: line-through;
}

#todos #new_todo .todo_title{
  width: 758px;
}


Because this is not a book on CSS, I’m not going to explain the little CSS that we did there. If you are really curious, you can easily comment it out later and see how it affects the application.

Now we need to install the connect-assets module so that what we just wrote will work.

> npm install connect-assets

[email protected] ./node_modules/connect-assets
__ [email protected]
__ [email protected]
__ [email protected]
__ [email protected]

Finally, we need to tell Express to use the connect-assets module to serve up our assets. We can do that by adding a line to the end of the configuration.coffee file:

Example: (source: app.1/src/configuration.coffee)


# Configure Express.js:
app.configure ->
  app.use(express.bodyParser())
  app.use(express.methodOverride())
  app.use(express.cookieParser())
  app.use(express.session(secret: 'd19e19fd62f62a216ecf7d7b1de434ad'))
  app.use(app.router)
  app.use(express.static(__dirname + '../public'))
  app.use(express.errorHandler(dumpExceptions: true, showStack: true))
  app.set('views', "#{__dirname}/views")
  app.set('view engine', 'ejs')
  app.use(require('connect-assets')())


If we were to start up the application right now, we would be greeted by a rather dull, blank, white page on http://localhost:3000, which is what we want to see. If you see anything, something isn’t right.

Let’s finish up this section by adding a form so we can create a new Todo. This will prove especially useful because we don’t have any todos in our database right now, and this will be a great way of getting some in there.

Example: (source: app.2/src/views/index.ejs)


<!DOCTYPE html>
<html>
  <head>
    <title>Todos</title>
    <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap. min.css">
    <%- css('/application') %>
  </head>
<body>
<div class="container">
  <h1>Todo List</h1>
  <ul id='todos' class='unstyled'>
    <li id='new_todo'>
      <div class="clearfix">
        <div class="input">
          <div class="input-prepend">
            <span class='add-on'>New Todo</span>
            <input class="xlarge todo_title" size="50" type="text" placeholder="Enter your new Todo here..." />
          </div>
        </div>
      </div>
    </li>
  </ul>
</div>

</body>
</html>


Now if we were to start up our application and browse to it, we would see a nicely styled form in which to enter a new todo. That form doesn’t do anything yet, but we’ll take care of that in a minute. In case you were wondering where all those CSS classes came from in the HTML—they all come from Bootstrap, which we set up just a minute ago.

Interacting with jQuery

Now that we have a form, let’s hook it up and see what happens. To do that let’s start by using jQuery, that wonderful library that almost everyone on the Internet seems to love. To me there is no more powerful tool set in the JavaScript eco system than that of jQuery. Originally released in 2006 by John Resig,4 jQuery is now used on 49% of the top 10,0005 websites in the world and is currently one of the most popular JavaScript libraries available. jQuery is a JavaScript library that lets you write clean and concise code for manipulating the HTML DOM, executing AJAX requests, and handling events and simple animations. It does all this and is cross-platform, meaning it supports most major browsers and operating systems.


Tip

In the dark old days of the Web, developers had to write multiple versions of the same JavaScript. One version would work for Internet Explorer, another version would work in Netscape, and so on. Nowadays, jQuery lets us write our code once and trust that it will work the way it should on most modern browsers.


Adding jQuery to our application is simple; we just need to require it in our index.ejs file:

Example: (source: app.3/src/views/index.ejs)


<!DOCTYPE html>
<html>
  <head>
    <title>Todos</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
    <%- js('/application') %>

    <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
    <%- css('/application') %>
  </head>
<body>

<div class="container">
  <h1>Todo List</h1>
  <ul id='todos' class='unstyled'>
    <li id='new_todo'>
      <div class="clearfix">
        <div class="input">
          <div class="input-prepend">
            <span class='add-on'>New Todo</span>
            <input class="xlarge todo_title" size="50" type="text" placeholder="Enter your new Todo here..." />
          </div>
        </div>
      </div>
    </li>
  </ul>
</div>

</body>
</html>



Tip

In real-world applications, I tend to frown on linking to external libraries, like we have just done. It’s possible that those libraries could get updated and introduce bugs in your application; or worse, the external reference could be removed or not available and then your application no longer works. I like to serve up local copies. However, just linking them here is easy for our purposes.


Hooking Up the New Todo Form

In addition to adding jQuery into our app, I used the js function provided by connect-assets to include an application file, which we can place into assets/application.coffee.

Example: (source: app.3/assets/application.coffee)


#= require_tree "jquery"


The application.coffee file is pretty small, and we’re going to keep it that way. To help us do that, we are going to use a feature of connect-assets that lets us require other CoffeeScript or JavaScript files. In this case, we are requiring a directory called jquery. This means that every file we create under this directory will be required automatically for us.

In the assets folder, create a new folder called jquery, and within that folder create a new file called new_todo_form.coffee. It’s in the new_todo_form.coffee file that we will place all the code that handles the new todo form we have in our HTML. Let’s take a quick run at that code now.

Before we start writing the code to handle the new todo form, let’s talk about what we want to happen. When a person types a todo into the form and presses the Enter key, we want to first test to make sure that the todo is valid, in that it has at least one non-whitespace character. If it’s invalid, we want to raise an error letting the user know what the problem was. If the todo is valid, we want to post that data back to our API. If the response from the API is a success, we want to add the new todo to our list of todos on the page and reset the form. If the response from the server is an error, we want to show that message back to the user. Here’s the code:

Example: (source: app.3/assets/jquery/new_todo_form.coffee)


$ ->
  # Focus the new todo form when the page loads:
  $('#new_todo .todo_title').focus()

  # Handle the keypress in the new todo form:
  $('#new_todo .todo_title').keypress (e) ->
    # we're only interested in the 'enter' key:
    if e.keyCode is 13
      todo = {title: $(e.target).val()}
      if !todo.title? or todo.title.trim() is ""
        alert "Title can't be blank"
      else
        request = $.post "/api/todos", todo: todo
        request.fail (response) =>
          message = JSON.parse(response.responseText).message
          alert message
        request.done (todo) =>
          $('#new_todo').after("<li>#{JSON.stringify(todo)}</li>")
          $(e.target).val("")


The first thing we do after the page has loaded is focus the new todo form. This is a nice thing to do because it allows the user to start typing todos right away, without having to manually navigate to the form.


Tip

In jQuery you can pass a function into the $ variable that aliases to jQuery, and everything in that function will be executed after the page is fully loaded. You can do this as many times as you need. Handy.


Next, we attach a function to the form that will get executed every time someone presses a key (event) in the form. This can get a little noisy, especially if we are looking for only one particular key, the Enter key. The Enter key has a numeric code of 13, so using that we can look at the keyCode attribute of the event we receive from jQuery. If the key code is 13 we continue on; if we get something other than 13 we simply ignore the event.

Knowing that we are looking at the right event, we can continue. Next, we want to capture the value of the form. That will be the title attribute of the todo we are hoping to create. We then create an object that will represent all the data we want to send back to the server.

With the proposed title of the todo, we can now proceed to do some local validation of it. This is nice because it is faster, and therefore a nicer user experience, than posting it back to the server, waiting for the validations there to execute, and then coming back to the client with any errors.


Tip

There are a lot of ways to share validations across both the server and client side so you don’t have to write them twice. If you find one that works for you, that’s great. It’s not always possible, though, to have validations that work both on the client and the server side the same way. A great example of this is username validation, which usually needs to hit a server to check for uniqueness. You can do that by using AJAX and hitting an API call, or you can validate that the username is not blank in the form and let the server side do the validation later.


Assuming the validations pass locally, we can proceed to posting the data back to the API. We create a new AJAX request using the post function that jQuery provides. We provide the url, /api/todos, and an object that represents the data we want to send. We assign the return value of the post function to a variable called request, which we can use in a minute to hang callbacks on when the request does certain things.


Tip

In jQuery 1.5 deferred objects were introduced. Before deferred objects, you would have to include any callbacks inside the original call to the post, or ajax, function when you originally call it. That was quite limiting. With deferred objects you can attach callbacks anytime, even after the request has finished processing. This makes it easier to write more isolated code that hangs on a request.


In our case, we want to add two callbacks to our request. The first callback function will be called if the request fails for any reason. The function will be passed a response object. Off that response object, we will need to get the responseText, which is JSON, parse it, extract the error message, and display it to the screen.

The second callback will be executed on successful completion of the request, in our case the creation of a new todo in the database. Because we haven’t yet written a nice template for printing out our todos nicely, we’ll print out the JSON representation of the todo inside of a li tag and append it to our list of todos just after the new todo form, so that the newest todos are always at the top.

If you fire up the application and try to create a new todo, you should see something similar to the following appear below the form:

{"title":"My New Todo","_id":"4efa82bdf65049000000001a","created_at":"2011-12-28T02:45:17.992Z","state":"pending"}

Cleaning Up the Todo List with Underscore.js Templates

There are quite a few templating systems out there for JavaScript applications, and choosing the right one for your application is really a matter of taste. Because our needs here are quite simple, I’m going to make our choice of templating systems just as simple. We will use the templating system that ships with the library underscore.js.6 Why did I choose this templating system over all the other ones out there? Simple, underscore.js is a dependency of the Backbone.js library we will be using later, so because we have to include it then as a dependency, we might as well use it now.


Tip

My personal favorite templating system is eco,7 which lets you embed CoffeeScript in your templates. A couple of other popular templating systems include Handlebars,8 Mustache,9 and Jade.10 The template plug-in11 for jQuery was quite a popular choice for a while, but has been deprecated and is no longer under active development, so if you were planning on using that, I would strongly suggest you look elsewhere for your templating needs.


Let’s update the index.ejs file to add in the dependency on underscore.js:

Example: (source: app.4/src/views/index.ejs)


<!DOCTYPE html>
<html>
  <head>
    <title>Todos</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
    <script src="http://documentcloud.github.com/underscore/underscore-min.js" type="text/javascript"></script>
    <%- js('/application') %>

    <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
    <%- css('/application') %>
  </head>
<body>

<div class="container">
  <h1>Todo List</h1>
  <ul id='todos' class='unstyled'>
    <li id='new_todo'>
      <div class="clearfix">
        <div class="input">
          <div class="input-prepend">
            <span class='add-on'>New Todo</span>
            <input class="xlarge todo_title" size="50" type="text" placeholder="Enter your new Todo here..." />
          </div>
        </div>
      </div>
    </li>
  </ul>
</div>

</body>
</html>


That’s one less thing we need to do later when we talk about Backbone.

Next let’s create a new file, assets/templates.coffee, that will hold our templates for the application.

Example: (source: app.4/assets/templates.coffee)


# Change the syntax for underscore.js templates.
# The pattern is now {{some_var}} instead of <%= some_var %>
_.templateSettings =
  interpolate : /{{(.+?)}}/g

@Templates = {}

Templates.list_item_template = """
<div class="clearfix">
  <div class="input">
    <div class="input-prepend">
      <label class="add-on active"><input type="checkbox" class="todo_state" /></label>
      <input class="xlarge todo_title" size="50" type="text" value="{{title}}" />
      <button class='btn danger'>X</button>
    </div>
  </div>
</div>
"""


The first thing we are going to do in this file is to change the default settings for how underscore.js interpolates dynamic data in the template. The reason for this is that I find {{ }} easier to type than <%= %>. This is also a popular convention among some of the other templating systems out there, so if you should want to port to one of them later it will be easier.

All that is left to do is define the template. To do this we are going to create a new Templates object and assign a property named list_item_template to the HTML template we want to use, using CoffeeScript’s heredoc support that we learned about in Chapter 2, “The Basics.”

The template is fairly simple; all we are going to dynamically push into the template is the title of the todo. We will update the state of the check box outside of the template. Let’s create a new file, todo_item.coffee, under the assets/jquery folder. In this file we will create a function that lets us append a new todo to our list using the template we just created.

Example: (source: app.4/assets/jquery/todo_item.coffee)


@TodoApp ||= {}

TodoApp.appendTodo = (todo) ->
  li = $("<li>#{_.template(Templates.list_item_template)(todo)}</li>")
  $('#new_todo').after(li)


First, we create a new variable that is accessible outside of this file called TodoApp. By prepending the declaration of the variable TodoApp with @, we are telling CoffeeScript we want to attach that variable to the this object, which in this case is the window object in a browser. After it is attached to the window object, the TodoApp variable will be available to any scope that has access to the window scope.

Next we need to create a function that will take in a todo and print out to the screen using our template. When we required the underscore library, it gave us a variable named _ and off that variable is a function called template. Here we call that function, passing in the HTML template we want to use and the JSON representation of the todo we want to have access to in that template. The underscore library does the rest and returns back the appropriate HTML we want. We then append that HTML to our list of todos right after the new todo form.

Now let’s update new_todo_form.coffee to use this new function when we create a new todo.

Example: (source: app.4/assets/jquery/new_todo_form.coffee)


$ ->
  # Focus the new todo form when the page loads:
  $('#new_todo .todo_title').focus()

  # Handle the keypress in the new todo form:
  $('#new_todo .todo_title').keypress (e) ->
    # we're only interested in the 'enter' key:
    if e.keyCode is 13
      todo = {title: $(e.target).val()}
      if !todo.title? or todo.title.trim() is ""
        alert "Title can't be blank"
      else
        request = $.post "/api/todos", todo: todo
        request.fail (response) =>
          message = JSON.parse(response.responseText).message
          alert message
        request.done (todo) =>
          TodoApp.appendTodo(todo)
          $(e.target).val("")


In the request.done callback function, we replace the line where we print out the JSON representation of the todo we got from the server with a call to the new TodoApp.appendTodo function.

Restart the application, add a new todo, and you should see it add a nicely styled todo to your list.

Listing Existing Todos

Now that we have a way of creating new todos in our database, and we have a way of printing out those new todos to our list when we create them, we need a way to display the todos that are already in the database when we load the page. Right now, if you were to add a few todos using our application and you reloaded the page, those todos would seem to have disappeared. In reality, they are safe and sound in our database, but because we haven’t written any code to fetch them from our API and print them out, we don’t see them. Fortunately, this is a relatively simple piece of code to write.

Example: (source: app.5/assets/jquery/retrieve_existing_todos.coffee)


$ ->
  request = $.get('/api/todos')
  request.done (todos) ->
    for todo in todos.reverse()
      TodoApp.appendTodo(todo)


First we need to wait until the page has been fully loaded before we retrieve the todos from the API. Then we create a new request that points to our API for retrieving the todos, and add a callback that will get executed when the response comes back with our todos. In that callback we are reversing the order of the todos and then passing them individually into the appendTodo function we wrote. Why are we reversing the order of the todos? The answer is as simple as it is annoying. The todos are actually coming back from the server in the correct order. However, the way our appendTodo function works is to append each todo at the top of the list, which would effectively place the todos in the wrong order on the page. We could change the way the API works, but it’s behaving correctly; it’s our client that isn’t. We could write another function that places them in the right order, or add some sort of conditional in the appendTodo function, but at the end of the day this is probably the cleanest solution, and the least likely to cause issues.

If we were to reload our application now we should see all the existing todos that we have created in the database.

Updating Todos

With the ability to create new todos and to list our existing todos, it would stand to reason that we might want to make some changes to those todos at some point. In our application, we want to be able to update the title attribute of the todo as well as toggle the state of the todo.

We are going to do this by adding two functions. The first function will watch the check box and the text field associated with each todo. If there are changes to either of those, it will call the second function, which will send the updates back to the API.

Example: (source: app.6/assets/jquery/watch_todo_for_changes.coffee)


@TodoApp ||= {}

# Watch the todo for changes:
TodoApp.watchForChanges = (li, todo) ->
  # If the checkbox is checked/unchecked:
  $('.todo_state', li).click (e) =>
    TodoApp.updateTodo(li, todo)
  # If someone hits "enter" in the title field:
  $('.todo_title', li).keypress (e) =>
    if e.keyCode is 13
      TodoApp.updateTodo(li, todo)


The watchForChanges will do what its name implies—it will watch the specified li for the todo, and if the check box is checked/unchecked, or if the “enter” key is pressed in the title text field, the updateTodo function will be called. To make sure that the watchForChanges function gets called, we’ll update the appendTodo function to call it when we append a new todo:

Example: (source: app.6/assets/jquery/todo_item.coffee)


@TodoApp ||= {}

TodoApp.appendTodo = (todo) ->
  li = $("<li>#{_.template(Templates.list_item_template)(todo)}</li>")
  $('#new_todo').after(li)
  TodoApp.watchForChanges(li, todo)


The todos are now being watched like a hawk. Let’s write the updateTodo function so we can save those changes.

Example: (source: app.6/assets/jquery/update_todo.coffee)


@TodoApp ||= {}

# Update the todo:
TodoApp.updateTodo = (li, todo) ->
  todo.title = $('.todo_title', li).val()
  if !todo.title? or todo.title.trim() is ""
    alert "Title can't be blank"
  else
    if $('.todo_state', li).attr('checked')?
      todo.state = 'completed'
    else
      todo.state = 'pending'
    request = $.post "/api/todos/#{todo._id}",
      todo: todo
      _method: 'put'
    request.fail (response) =>
      message = JSON.parse(response.responseText).message
      alert message


The updateTodo function is similar to the code we wrote for creating a new todo. There are, however, a few important differences. First, we get the value of the title text field and do a simple validation on it to make sure it’s not blank. If it passes the validation, we need to build the rest of the data we want to send back to the server. In this case we need to update the state attribute of the todo based on whether the check box is checked.

Next we create the request to the API. Because we want to update a specific todo, we have to make sure to include the ID of the todo as part of the API URL. Now, the more observant of you might have noticed that we are actually using POST to send this data to the server when, in fact, our API requires that it be sent via PUT. There are a couple of reasons for this. The first is that historically not all browsers support HTTP verbs beyond GET and POST; the same holds true of jQuery. To get around this limitation, a lot of frameworks, such as Express and Ruby on Rails, have taken to looking for a specially named parameter, _method. The frameworks will then consider the request to be of whatever type is set on that parameter. In our case we are sending along the value of PUT for the _method parameter, so Express will consider this not as a POST request, but rather a PUT request.

Finally we will look to see if there are error messages from the server because of our update. If there are errors, we display them; otherwise, we just let the users get on with their todo activities.

We do have a small problem here, however. If we were to mark a todo as completed and refresh the page, it would appear as though it was still pending because the check box wouldn’t be checked and none of the completed styles would be applied to the todo to let the users know they’ve completed that task. Let’s write a function that will update these styles as appropriate:

Example: (source: app.7/assets/jquery/style_by_state.coffee)


@TodoApp ||= {}

# Update the style based on the state:
TodoApp.styleByState = (li, todo) ->
  if todo.state is "completed"
    $('.todo_state', li).attr('checked', true)
    $('label.active', li).removeClass('active')
    $('.todo_title', li).addClass('completed').attr('disabled', true)
  else
    $('.todo_state', li).attr('checked', false)
    $('label', li).addClass('active')
    $('.todo_title', li).removeClass('completed').attr('disabled', false)


This function is very simple. When it is called, it will check the state of the todo, and if it’s marked as completed it will apply the necessary CSS classes to the elements. If it is not, it will remove those styles. One of the nice things about jQuery is that it lets us write code like this, without having to first check to see if the element in question already has the CSS class. If it already has the class, it ignores the request. The same goes for removing the class; if the element doesn’t have the class in question applied to it, then it fails silently—just the behavior we were looking for.

All that is left to do now is call the styleByState function in the few places we need to make sure that we apply the correct styles. The first place we need to do that is in the appendTodo function. If an existing todo is passed into the appendTodo function, we want to make sure it gets styled appropriately.

Example: (source: app.7/assets/jquery/todo_item.coffee)


@TodoApp ||= {}

TodoApp.appendTodo = (todo) ->
  li = $("<li>#{_.template(Templates.list_item_template)(todo)}</li>")
  $('#new_todo').after(li)
  TodoApp.watchForChanges(li, todo)
  TodoApp.styleByState(li, todo)


The other place we need to make sure we update the classes associated with the todo is when a todo is updated. We can do that by adding a callback to the request object in the updateTodo function:

Example: (source: app.7/assets/jquery/update_todo.coffee)


@TodoApp ||= {}

# Update the todo:
TodoApp.updateTodo = (li, todo) ->
  todo.title = $('.todo_title', li).val()
  if !todo.title? or todo.title.trim() is ""
    alert "Title can't be blank"
  else
    if $('.todo_state', li).attr('checked')?
      todo.state = 'completed'
    else
      todo.state = 'pending'
    request = $.post "/api/todos/#{todo._id}",
      todo: todo
      _method: 'put'
    request.fail (response) =>
      message = JSON.parse(response.responseText).message
      alert message
    request.done (todo) ->
      TodoApp.styleByState(li, todo)


Deleting Todos

Our application is almost complete. All that is left is to hook up the delete button so users can delete their unwanted todos. At this point, this code should be pretty simple for you to write, but let’s quickly look at it.

We will start by writing a deleteTodo function:

Example: (source: final/assets/jquery/delete_todo.coffee)


@TodoApp ||= {}

# Delete the todo:
TodoApp.deleteTodo = (li, todo) ->
  if confirm "Are you sure?"
    request = $.post "/api/todos/#{todo._id}", _method: 'delete'
    request.done =>
      li.remove()


The deleteTodo function makes a request back to the API using the DELETE HTTP verb, via the special _method parameter we discussed earlier. If the request to destroy the todo is successful, we remove the todo from the page—nice, clean, and simple.

Now we need to hook up the delete button and we are done. We can do this in the watchForChanges function we wrote earlier:

Example: (source: final/assets/jquery/watch_todo_for_changes.coffee)


@TodoApp ||= {}

# Watch the todo for changes:
TodoApp.watchForChanges = (li, todo) ->
  # If the checkbox is checked/unchecked:
  $('.todo_state', li).click (e) =>
    TodoApp.updateTodo(li, todo)
  # If someone hits "enter" in the title field:
  $('.todo_title', li).keypress (e) =>
    if e.keyCode is 13
      TodoApp.updateTodo(li, todo)
  $('button.danger', li).click (e) =>
    e.preventDefault()
    TodoApp.deleteTodo(li, todo)


That’s it! Our application is now finished! Congratulations.

Wrapping Up

There you have it. We have used jQuery to write an interactive web client for our todo list application. It was pretty simple, and you can see how CoffeeScript can help us write some very nice looking jQuery.

The approach we took in this chapter, from a code “architecture” perspective, is that of what a lot of jQuery developers probably would have done. Write a bunch of functions, pass around some objects, and do what is necessary. We could have taken another approach to this application, which would have involved writing classes that managed each of the todos and more cleanly wrapped the HTML elements and their events to the todo itself.

So why didn’t I show you the second approach? I did this for two reasons. The first I have already stated; the approach we have shown here is one that is common of someone writing plain old JavaScript and jQuery, so I wanted to give you a feel for what that would look like in CoffeeScript. The second reason I didn’t write our code in the “class” style is because doing so would mean we would have reinvented the wheel that is Backbone.js.

Backbone.js is a simple framework that lets us write views that bind to elements of a page and associate those views with models, such as our todos, and have them easily listen to each other and respond to events accordingly. As a matter of fact, thinking about it, why don’t we see Backbone in action? Turn the page to Chapter 12, “Example: Todo List Part 3 (Client-side w/ Backbone.js)” and let’s get started!

By the way, if you are curious to see what the jQuery example would look like if we had written it using classes instead of the approach we took here, I happen to have it already written for you. Enjoy!12

Notes

1. http://twitter.github.com/bootstrap/

2. http://twitter.com

3. https://github.com/trevorBurnham/connect-assets

4. http://en.wikipedia.org/wiki/John_Resig

5. http://en.wikipedia.org/wiki/Jquery

6. http://documentcloud.github.com/underscore

7. https://github.com/sstephenson/eco

8. http://www.handlebarsjs.com/

9. https://github.com/janl/mustache.js/

10. http://jade-lang.com/

11. https://github.com/jquery/jquery-tmpl

12. https://github.com/markbates/Programming-In-CoffeeScript/tree/master/todo2/alt-final

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.62