Skip to content
January 5, 2013 / jphoward

End to end web app in under an hour–Part 3

Here is the video tutorial that goes with this post:

Continued from Part 2.

Create/Update

Let’s now create a form to add new items. Here’s the basic HTML:

<form name="form" class="form-horizontal">
<div class="control-group" ng-class="{error: form.Todo.$invalid}">
    <label class="control-label" for="Todo">Todo</label>
    <div class="controls">
        <input type="text" ng-model="item.Todo" id="Todo">
    </div>
</div>
<div class="form-actions">
    <button class="btn btn-primary">
        Submit
    </button>
    <a href="#/" class="btn">Cancel</a>
</div>
</form>

The ‘control-group’ div needs to be repeated for each item. This could be made easier by writing a directive, but you know how to do that yourself now… The ng-model is showing something new here – the ability to create properties of an object on-the-fly. In this case, an object called $scope.item is being created.

The Cancel button simply redirects to the ‘/’ template. Note that all AngularJS internal links must start with ‘#’, in order to stay within the same server page. We need to add a route to app.js to allow us to go to this page:

 

when('/new', { controller: CreateCtrl, templateUrl: 'detail.html' }).

Create an empty controller called CreateCtrl for now, and add a TH to list.html to allow us to jump to the create page.

<th><a href="#/new"><i class="icon-plus-sign"></i></a></th>

Check the new form displays correctly.

image

 

To make this actually do something useful, add ‘ng-click="save()"’ to the Submit button, and add your save method to CreateCtrl.

$scope.save = function () {
    Todo.save($scope.item, function() {
        $location.path('/');
    });
};

Hopefully the general approach to this method is familiar from our earlier use of query() – we pass a second parameter, which is a callback called on success. We really should add a third parameter: a callback called on failure. I’ll let you tackle that yourself. The good news is that our Create form is now fully working! In the future we will add validation and also use a date-picker to make it easier to pick a due date.

Edit Form

We can use the same template for the edit form. But we’ll need a new route and a new controller that captures and displays the item to edit. The route:

when('/edit/:itemId', { controller: EditCtrl, templateUrl: 'detail.html' }).

The controller needs to load the item when it initializes, and save to the appropriate id.

var EditCtrl = function ($scope, $routeParams, $location, Todo) {
$scope.item = Todo.get({ id: $routeParams.itemId });
    
$scope.save = function () {
    Todo.update({id: $scope.item.TodoItemId}, $scope.item, function () {
        $location.path('/');
    });
};
};

Note that we’ve added $routeParams to our parameters in order to grab the id we captured from the url (i.e. where ‘:itemId’ appears in the route). Finally, let’s add an edit link as the final TD to each row.

<td><a href="#/edit/{{item.TodoItemId}}">
        <i class="icon-edit"></i>
    </a></td>

That’s all we need – nice to see that we’re needing less and less code to add features; that’s a very good sign!

Delete

Our last major piece of functionality is deletions. Add a delete icon to our last TD that calls an appropriate method:

<a ng-click="delete()"><i class="icon-remove"></i></a>

And also add a unique id to the TR, so we can refer to it later.

id="item_{{item.TodoItemId}}"

Finally, add the delete() method to ListCtrl:

$scope.delete = function () {
    var itemId = this.item.TodoItemId;
    Todo.delete({ id: itemId }, function () {
        $("#item_" + itemId).fadeOut();
    });
};

I’ve used jQuery’s fadeOut() method here so that the user gets some feedback about the successful deletion. Strictly speaking, code that manipulates the DOM should really be in a directive, not a controller – but that seems like overkill for this single line.

It works!

So now we have a complete CRUD application. In the next part, we’ll learn how to add indexes using EF Migrations, and we’ll also learn how to make our app available to the public using AppHarbor. I hope you are ready for the fame and fortune that you will receive once your peers can use your amazing todo application!

Continued in Part 4.

Advertisements

9 Comments

Leave a Comment
  1. Chris Wheat / Feb 24 2013 5:49 pm

    Thank you for these videos, very straight forward and easy to follow! I am looking forward to using Angular on my next project.

  2. Renish / Mar 3 2013 6:03 pm

    Thank you, This was excellent to start with.
    I was trying to extend the functionality and got stuck with sending collection of items so can you please help out?

    e.g.
    $scope.save = function () {
    Todo.save({ todoitems : $scope.items}, function() {
    $location.path(‘/’);
    });
    };

    I am getting null in todoitems
    public HttpResponseMessage PostToDoItem(IEnumerable todoitems)
    { … }

    But when I see the request using fiddler I get the proper JSON request
    {“todoitems”:[{“TodoItemId”:23,”Todo”:”Test todo: 23″,”Priority”:1,”DueDate”:”2012-09-12T00:00:00″},
    {“TodoItemId”:14,”Todo”:”Test todo: 14″,”Priority”:4,”DueDate”:”2012-06-24T00:00:00″},
    {“TodoItemId”:26,”Todo”:”Test todo: 26″,”Priority”:4,”DueDate”:”2012-08-13T00:00:00″},
    {“TodoItemId”:39,”Todo”:”Test todo: 39″,”Priority”:4,”DueDate”:”2012-08-19T00:00:00″},
    {“TodoItemId”:47,”Todo”:”Test todo: 47″,”Priority”:4,”DueDate”:”2012-11-06T00:00:00″},
    {“TodoItemId”:48,”Todo”:”Test todo: 48″,”Priority”:4,”DueDate”:”2012-03-01T00:00:00″}]}

    Any idea what am I missing over here

  3. g2ux / Mar 4 2013 6:58 pm

    Great comprehensive tutorial. Thank you for the effort.

    I am just getting started with Angular and am hoping you can weigh in one aspect of your app.

    For reasons that seem obvious, you elected to remove deleted todo items from the listing locally using the jquery fadeout. However, on both the item create and update, it appears that the mechanism for refreshing the list view is a fresh request back to the REST interface.

    Question:

    If your design called for the listview to remain visible on the screen (perhaps in the left column) while you handled the detail create/update in a view in the right column, after a create/update you’d want to update the listview without a complete refresh from the server to avoid the delay.

    Does Angular have a mechanism for handling the listview update locally (without refreshing the entire list from the server) when an item has been added or edited?

  4. Brack / Apr 20 2013 6:22 pm

    Great tutorial. Did you write your own snippets for Angular or did you download them? They look really useful

  5. Aarif Shah (@Razeelbache) / Jan 16 2016 6:46 pm

    I cannot add icon-remove for my delete .i added the same code in list.html

  6. Bawge Chandu / Nov 17 2016 4:58 pm

    I got this exception and I’m not getting how to resolve it as I’m new to it.

    An exception of type ‘System.InvalidOperationException’ occurred in EntityFramework.dll but was not handled in user code

    Additional information: The model backing the ‘AngularWebApplicationContext’ context has changed since the database was created. Consider using Code First Migrations to update the database.

Trackbacks

  1. End to end web app in under an hour–Part 2 « Jeremy Howard
  2. End to end web app in under an hour–Part 4 « Jeremy Howard
  3. Todo Web App Part 3 - ASP.NET MVC Angular Web API | Share You All

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: