After last week’s post on retrieving data from your Rails API you should be able to view a list of tasks in your application. While an essential part of the client, seeing isn’t everything. We need a way to create, update and delete tasks. Let’s get started.

Services

We’ll be sending data using the same services we created last week for retrieving. Here they are in case you missed it:

exampleServices = angular.module('exampleServices', ['ngResource']). factory('Tasks', ['$resource', ($resource) -> $resource('/tasks.json', {}, { query: { method: 'GET', isArray: true } create: { method: 'POST' } }) ]). factory('Task', ['$resource', ($resource) -> $resource('/tasks/:task_id.json', {}, { show: { method: 'GET' } update: { method: 'PUT' } destroy: { method: 'DELETE' } }) ]).

Creating the Templates

To start with, create a form template containing fields for a task. In a slight deviation from traditional methods the name we give the input will not dictate which data is sent to the server by AngularJS. Instead, that association is made using the element’s model. You’re going to give each input field an attribute in the form of “data-ng-model” => “task.field_name”. This binding works both ways; changing the input value will change task.field_name and changing task.field_name will change the input value.

As an added bonus, this binding will affect all other elements in the scope containing the same variable task. For a live preview box all you have to do is include your task show template in the same scope as your edit form. No more hitting the “preview” button and waiting for the server to respond. Everything is automated and live. However, there is something to watch out for.

Heads up: The DOM is not considered canon for data values. AngularJS stores values separately in memory.

This may seem like an irrelevant distinction since in most cases they actually match up. Certain elements, like select options, do not match. Their values instead are the indices for the options array. Don’t be fooled when you inspect the DOM and don’t assume that altering the DOM will change the AngularJS values. In addition, it’s not necessary to create hidden input fields to pass variables along with the form. Simply set the value on the task object before you send it.

Here is an example of a form for a task object found in /templates/tasks/edit.html:

.edit-task{ "data-ng-model" => "task" } %form %label{ for: "task_name" } Task %input{ type: "text", class: "focus", id: "task_name", name: "task[name]", placeholder: "Enter your task here.", "data-ng-model" => "task.name" } %label{ for: "task_due" } Due %input{ type: "text", class: "date", id: "task_due", name: "task[due]", placeholder: "Enter the due date here.", "data-ng-model" => "task.due" } %button{ type: "button", "data-ng-click" => "save(task)", name: "commit", value: "Save Task" } Save Task or %button{ type: "button", "data-ng-click" => "cancel()" } Cancel %span{ "data-ng-show" => "task.id > 0" } or %a{ rel: "nofollow", class: "delete", "data-ng-click" => "destroyTask(task)" } Delete Task

Here we have a template containing the task name and due date along with options to save, cancel or delete the task. The option to delete appears only if the task has an ID, indicating it is saved. This allows us to re-use the form for both creating new and editing existing tasks. How do we do that? The buttons still aren’t functional! That’s true. This code doesn’t stand alone. We’re going to update the index page from the previous post in order to add new tasks.

.tasks{ "data-ng-controller" => "TaskController" } .add-button{ "data-ng-hide" => "task.editing", "data-ng-click" => "newTask()" } New Task .edit-task-form{ "data-ng-show" => "task.editing", "data-ng-include" => "'/templates/tasks/edit.html'" } .index{ "data-ng-repeat" => "task in tasks", "data-ng-include" => "'/templates/tasks/show.html'" }

The first element is the new task button, followed by the task edit form. You’ll only see one of the two at any given time since we hide/show them based on the same boolean flag.

Since we’re planning to make changes to the existing tasks we’ll want a way to update the task index without reloading the page. Add this function to your task controller (TaskController if you followed the previous post):

$scope.reloadIndex = -> Tasks.query({}, (tasks) -> $scope.tasks = tasks )

Adding Tasks

These templates aren’t very useful by themselves. They require some controller updates to be made active. Start by adding the following functions to TaskController:

$scope.newTask = -> $scope.task = {} $scope.task.editing = true $scope.cancel = -> $scope.task.editing = false $scope.save = (task) -> Tasks.create({task_id: task.id, task: task}, (reply) -> task.editing = false task.id = reply.id $scope.reloadIndex() )

The new task function creates a new blank object and sets the editing flag to true while the cancel function simply sets the flag to false. The save function accepts the task model we’ve been editing and sends it to the server, whereupon the server replies with the generated ID. We add this ID to the task before inserting it into the top of the index.

“Wait,” you say, “my server is throwing an error!” This is anticipated. We still need to set up the response format for the create method of our tasks controller on the Rails app:

format.json { render json: { id: @task.id }, status: :ok }

Inserting this inside the respond_to do |format| block of your controller as outlined in the previous post will instruct the server to send the ID along with the success message. Now the client and server can speak to each other!

Editing Tasks

Earlier on I promised you the ability to edit tasks. Here’s how. Add an edit button for each task on the show template (/templates/tasks/show.html):

.edit-button{ "data-ng-hide" => "task.editing", "data-ng-click" => "editTask(task)" } Edit Task

Make it work by adding this function to your TaskController:

$scope.editTask = (task) -> $scope.task = task $scope.task.editing = true

Finally, we’ll need to add a destroyTask function and update the save function in the task controller as follows:

$scope.save = (task) -> if task.id task.$update({task_id: task.id}, -> $scope.reloadIndex() ) else Tasks.create({task_id: task.id, task: task}, (reply) -> task.editing = false task.id = reply.id $scope.reloadIndex() ) $scope.destroyTask = (task) -> Task.destroy({task_id: task.id}, -> $scope.reloadIndex() )

The new destroy function will tell the server to delete the given object. The save function has been modified to send an update command if the task has an ID (thus is already saved) or a create command otherwise. Afterwards they reload the task index to update the display.

There you have it folks! Some might consider this a complete client side web application, but there is still more left unwritten. Right now our app only responds to success. What happens when an API call fails or a requested update fails validation? We still need to handle error notification and fail gracefully. We also have to write our client side tests. Come back in two weeks to read more!