Twitter: raymondcamden


Address: Lafayette, LA, USA

My first AngularJS application

11-29-2011 25,105 views Mobile, JavaScript, HTML5 38 Comments

Earlier this year I blogged about the AngularJS framework. I thought the docs and the tutorial were cool and overall it felt like a real cool way to build a JavaScript "Application" - and yes, with the quotes there I'm talking about something more than a simple one page, Ajaxified file. Unfortunately, I never got the time to play more with AngularJS until this week. Yesterday I reviewed the tutorial again and decided to take a stab at building my own application. I ran into a few hiccups, but in general, the process was straight forward, and after actually building something with the framework, I love it, and can't wait to try more with it. Let's take a look at my application. (And of course, please keep in mind that this is my first AngularJS application. It's probably done completely wrong. Keep that in mind....)

For my first application, I decided to build a simple Note manager. This is something I've done many times before. I'm a huge fan of Evernote and I keep hundreds of notes stored in it. My application will allow for adding, editing, deleting, and viewing of simple notes. In this first screen shot you can see the basic UI.

You can click a note to see the details...

And of course basic editing is supported...

So what does the code look like? First, I've got my core index page:

view plain print about
1<!DOCTYPE html>
2<html xmlns:ng="http://angularjs.org/">
3<head>
4    <meta charset="utf-8">
5    <title>Note Application</title>
6    <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
7    <link rel="stylesheet" href="css/main.css"/>
8</head>
9
10<body ng:controller="NoteCtrl">
11
12    <div class="container">
13
14        <ng:view></ng:view>
15
16    </div>
17    
18    <script src="lib/angular/angular.js" ng:autobind></script>
19    <script src="js/controllers.js"></script>
20    <script src="js/services.js"></script>
21</body>
22</html>

All of the real display will happen within the view, which is managed in my controller. Let's look at that file next.

view plain print about
1/* App Controllers */
2
3function NoteCtrl($route) {
4 var self = this;
5
6 $route.when('/notes',
7 {template: 'partials/note-list.html', controller: NoteListCtrl});
8 $route.when('/notes/add',
9 {template: 'partials/note-edit.html', controller: NoteEditCtrl});
10 $route.when('/notes/edit/:noteId',
11 {template: 'partials/note-edit.html', controller: NoteEditCtrl});
12 $route.when('/notes/:noteId',
13 {template: 'partials/note-detail.html', controller: NoteDetailCtrl});
14 $route.otherwise({redirectTo: '/notes'});
15
16 $route.onChange(function(){
17 self.params = $route.current.params;
18 });
19
20 $route.parent(this);
21}
22
23function NoteListCtrl(Note_) {
24    var self = this;
25    self.orderProp = 'title';
26    self.notes = Note_.query();
27
28    self.delete = function(id) {
29        console.log("delete "+id);
30        Note_.delete(id);
31        self.notes = Note_.query();
32        //refreshes the view
33        self.$root.$eval();    
34    }
35    
36    self.edit = function(id) {
37        window.location = "./index.html#/notes/edit/"+id;
38    }
39}
40
41function NoteDetailCtrl(Note_) {
42 var self = this;
43 self.note = Note_.get(self.params.noteId);
44
45 if(typeof self.note === "undefined") window.location = "./index.html";
46}
47
48function NoteEditCtrl(Note_) {
49    console.log('EDIT CTRL');
50    var self = this;
51        
52    if(self.params.hasOwnProperty("noteId")) self.note = Note_.get(self.params.noteId);
53    else self.note = { title:"", body:""};
54
55    self.cancel = function() {
56        window.location = "./index.html";
57    }
58
59    self.save = function() {
60        Note_.store(self.note);
61        window.location = "./index.html";
62    }
63
64}

Essentially, this file consists of the main controller which simply handles routing based on the URL. The other three controls handle listing, editing, and the detail view. Now let's look at the note service.

view plain print about
1/* http://docs.angularjs.org/#!angular.service */
2
3angular.service('Note', function(){
4    return {
5        query:function() {
6            var notes = [];
7            for(var key in localStorage) {
8                if(key.indexOf("note_") == 0) {
9                    notes.push(JSON.parse(localStorage[key]));
10                }
11            }
12            console.dir(notes);
13            return notes;
14        },
15        delete:function(i) {
16            localStorage.removeItem("note_"+i);
17        },
18        get:function(i) {
19            if(localStorage["note_"+i]) return JSON.parse(localStorage["note_"+i]);
20            console.log("no note for "+i);
21        },
22        store:function(note) {
23            if(!note.hasOwnProperty('id')) {
24                //yep, hack, get all notes and find highest id
25                var notes = this.query();
26                var highest = 0;
27                for(var i=0; i<notes.length; i++) {
28                    if(notes[i].id >
highest) highest=notes[i].id;
29                }
30                note.id = ++highest;
31            }
32            note.updated = new Date();
33            localStorage["note_"+note.id] = JSON.stringify(note);
34        }
35    }
36    
37});

As you can see, my note service is simply a collection of methods. My application makes use of HTML5's Local Storage feature. It looks for any key that begins with note_ and considers that application data. It stores notes as JSON strings and the service handles both deserializing and serializing the information. What's cool is that I could - in theory - replace this with a SQL version and nothing else would change.

All that's really left are the views. Here is the first view - and keep in mind - this is the entire file for the display of notes on the first screen.

view plain print about
1Search: <input type="text" name="query"/>
2 Sort by:
3 <select name="orderProp">
4 <option value="title">Title</option>
5 <option value="-updated">Newest</option>
6 </select>
7
8
9<h2>Notes</h2>
10
11<table class="bordered-table">
12    <tr>
13        <th>Title</th>
14        <th>Updated</th>
15        <th>&nbsp;</th>
16    </tr>
17    <tr ng:repeat="note in notes.$filter(query).$orderBy(orderProp)">
18        <td><a href="#/notes/{{note.id}}">{{note.title}}</a></td>
19        <td>{{note.updated | date:'MM/dd/yyyy @ h:mma'}}</td>
20        <td><a href="" ng:click="edit(note.id)" title="Edit"><img src="images/page_edit.png"></a> <a href="" ng:click="delete(note.id)" title="Delete"><img src="images/page_delete.png"></a></td>
21    </tr>
22</table>
23
24<p ng:show="notes.length==0">
25    Sorry, there are no notes yet.
26</p>
27
28<a href="#/notes/add" class="btn primary">Add Note</a>

Take note of the ng:repeat and the embedded tokens wrapped in {}. Also note how easy it is to format the date. The detail view is incredibly boring.

view plain print about
1<h1>{{note.title}}</h1>
2<p>
3<b>Last Updated:</b> {{note.updated | date:'MM/dd/yyyy @ h:mma'}}
4</p>
5
6{{note.body}}

And the edit one is also pretty trivial:

view plain print about
1<h1>Edit Note</h1>
2
3<form class="form-stacked" onSubmit="return false" id="editForm">
4    <div class="clearfix">
5        <label for="title">Title:</label>
6        <div class="input">
7            <input type="text" id="title" name="note.title" class="xlarge">
8        </div>
9    </div>
10
11    <div class="clearfix">
12        <label for="body">Body</label>
13        <div class="input">
14            <textarea class="xxlarge" id="body" name="note.body" rows="3"></textarea>
15        </div>
16    </div>
17
18    <div>
19        <button ng:click="save()" class="btn primary">Save</button> <button ng:click="cancel()" class="btn">Cancel</button>
20    </div>
21
22</form>

Pretty simple, right? For the heck of it, I ported this over into a PhoneGap application, and it worked well. The UI is a bit small, but I was able to add, edit, delete notes just fine on my Xoom table. I've included a zip file of the core code for the application and the APK as well if you want to try it. You may demo the app yourself below, and note, it makes use of console for debugging, so don't run it unless you've got a proper browser.

Download attached file

38 Comments

  • Commented on 11-29-2011 at 3:54 PM
    Awesome!

    (Also you probably shouldn't use delete as a key for an object, or as a method name. delete is a keyword in JS and some browsers won't allow you to use it like that).
  • Commented on 11-29-2011 at 4:05 PM
    Good point Elliott. I didn't notice that until I put it into Android. Eclipse complained about it. I just deleted the errors though and it ran on my device.
  • Commented on 12-08-2011 at 2:44 PM
    I wonder if AngularJS has been used by any major company. There are so many javascript mvc frameworks, I'm confused which one to use. The two main ones that I hear a lot are backbone.js and JavaScript MVC.
  • Commented on 12-08-2011 at 2:45 PM
    Hopefully Elliott will chime in here. I'm not sure who out there is using it.
  • Commented on 12-10-2011 at 9:47 AM
    I convinced my company to go with Angularjs for a new project and so far so good. There is a bit of a learning curve but I think it is worth it. Anyway great tut!

    For those interested I converted a standard JS application (Kevin's HTML5 Quiz) to Angularjs in a couple of hours. Link: http://www.webappstogo.com/html5quiz
  • Commented on 12-10-2011 at 11:33 AM
    Nice - and yes - I viewed source so I could cheat.
  • Commented on 12-10-2011 at 11:45 AM
    Thanks! I forgot to add I have the source on github -> http://bit.ly/rCVLXO
  • Johan #
    Commented on 12-12-2011 at 5:16 PM
    I've been using angular on a few projects and really like it. Apparently angular is used quite a lot internally at google.

    Some pointers:

    - Use ng:href in your templates for links that have bindings

    - Rather than using a function to change browser location you could just use an href with the path, for example to edit: ng:href="#/notes/edit/{{note.id}}";

    - If you do want to set location in your controller use: $location.path('/notes/edit' + self.noteId);

    - you can access the $routeParams in your controller and use that to load a note using something like this
    self.noteid = $routeParams.noteId as alternative to self.params.hasOwnProperty("noteId")
  • Commented on 12-12-2011 at 5:19 PM
    Thanks for the tips Johan. I've been trying to find time to build another Angular app - I just haven't had the time.
  • Johan #
    Commented on 12-12-2011 at 7:43 PM
    No worries Ray - given the diversity and amount of useful stuff you explore/publish I'll cut you a bit of slack on getting to your next angular post ;-)
  • Johan #
    Commented on 12-12-2011 at 8:23 PM
    @Dmitry - yes there seems to be a new JavaScript MVC framework each day. I tried a few and found the TODO MVC resource from Addy Osmani useful for comparision:
    https://github.com/addyosmani/todomvc/tree/master/...

    Angular was the best fit for me. The community is still small compared to backbone but core developers (they work for google) are very helpful and answer questions on the forums promptly.

    For angular I'd recommend using latest release (unstable - 10.x) available here:
    https://github.com/angular/angular.js/tags

    Make sure to consult the latest docs here:
    http://docs-next.angularjs.org/api
  • Commented on 12-12-2011 at 8:25 PM
    "Angular was the best fit for me." - And isn't that the most important thing? I know for me in ColdFusion, there were multiple frameworks I tried until I found the one that made me effective.
  • nilay #
    Commented on 08-26-2012 at 11:37 AM
    I tried to compile a angular js application into phonegap....But the angular.js directives {{ng:repeat}} does not seems to be picked up when I compile with ant debug..... But the rest of the html stuff seems to be ok...Is there any special rules that needs to be followed for compiling it with the the angular.js ? Any complete example of an android app which contains the index.html in assets/www ? Thanks for any pointer...
  • Commented on 08-26-2012 at 12:50 PM
    I'd try checking DDMS and the console output. Check my post here:

    http://www.raymondcamden.com/index.cfm/2012/5/10/S...
  • Commented on 09-19-2012 at 2:48 AM
    What icon set are you using in your test app Ray? For the edit and delete buttons?
  • Commented on 09-19-2012 at 7:04 AM
    famfamfam Silk Icons - http://www.famfamfam.com/lab/icons/silk/
  • asic #
    Commented on 12-04-2012 at 4:31 AM
    Thanks for that wonderfull tuto.
    What is the licence for your code ? Mit ? LGPL ?
    Can we use your code ?
  • Commented on 12-04-2012 at 6:34 AM
    In general, my code is Apache licensed, and practically, it is: Free to use, give me credit in the source, and visit the Amazon Wish List if you can.

    BUT - please note this blog post is very old. I know it's just one year, but Angular has changed a lot since then. (In fact, I kinda don't like it anymore. No offense to the Angular folks, but I'm just not happy with it's current form.)
  • Asic #
    Commented on 12-05-2012 at 2:25 AM
    What bothers you in the current version of angular ?
    Have you moved to another framework ? If so which one ?
    Thanks again
  • Commented on 12-05-2012 at 11:24 AM
    It's hard to really say. I feel like how Angular does stuff now in 1.0 is just... overly complex and weird. I accept that it is a completely "gut feel" type reaction, but when I tried Angular 1.0, I had none of the feelings I did when I tried the earlier editions.

    The framework I'm most interested in now (but I've yet to build a POC in it it) is Knockout.
  • Pardeep #
    Commented on 12-06-2012 at 1:08 AM
    Hi Ray,

    I've been looking at Angular.js the last few days. Looks v intriguing. The 3 finalists were Angular, Knockout and Ember. The latter looks too complex, and I was heading into Knockout territory until I took a closer look at Angular.

    I found a good resource for knockout called Knockout.js Succinctly, helped me wrap my head around it better than the website. You can download it at:

    http://www.syncfusion.com/resources/techportal/ebo...

    Are you currently still working with Knockout or Angular. If the latter, did you get to the point of using a cfc to feed the view or post data to the db?


    I've also been learning Coffeescript, quite like the syntax. Have you checked that out?

    Best,
    Pardeep.
  • Commented on 12-06-2012 at 6:22 AM
    "Are you currently still working with Knockout or Angular." As I said above - I'm not using Angular, I want to use Knockout, but i haven't had a chance to yet.

    Coffeescript: I don't care for it. I'm no JS expert, but I find it easy enough to write that a 'meta language' on top just feels wrong. The syntax feels weird to me too. Now that being said, I can say I like TypeScript quite a bit as it feels like a "slightly extended JavaScipt".
  • Lou #
    Commented on 12-18-2012 at 1:14 PM
    When I try to run this app, I get an error:

    Unhandled exception at line 3, column 1 in http://localhost:36184/app/js/services.js

    0x800a01b6 - JavaScript runtime error: Object doesn't support property or method 'service'

    Have you seen this before?
  • Commented on 12-18-2012 at 1:16 PM
    Sorry, I don't know. I haven't run this in a long time. Does the online demo do the same?
  • Lou #
    Commented on 12-18-2012 at 1:17 PM
    Figures, after I posted this, I replaced the Partial files and it worked, must of been a type-o somewhere
  • Lou #
    Commented on 12-18-2012 at 1:20 PM
    This is a great app for your first application in AngularJS.
    I am just learning this too, How did you figure out how to use local storage?
    For a first app it you seem to have a pretty good handle on AngularJS. Great job!
  • Commented on 12-18-2012 at 1:24 PM
    "This is a great app for your first application in AngularJS."

    Thanks. However, I don't really use Angular anymore. I'm not a fan with how it evolved.

    "How did you figure out how to use local storage? "
    LocalStorage is one of the easiest, and most practical, of the HTML5 features out there. I love it. If you search my blog you will find many examples of it. And here is the MDN doc on it:

    https://developer.mozilla.org/en-US/docs/DOM/Stora...
  • Pardeep #
    Commented on 12-18-2012 at 4:45 PM
    What didn't you like about how Angular evolved?

    I'm still trying out Angular, Knockout and Ember. Found a lesser known but quite elegant framework called serenade.js. V diff less verbose approach.
  • Commented on 12-18-2012 at 8:09 PM
    It isn't a very scientific thing - just a gut feeling. It seems like it's gotten overly complex and it isn't as friendly as when I first saw it. This is in no way meant to be an attack on them. I'm sure it is still a good framework. It just doesn't feel right for me.

    Knockout is the one I'm most interested in now, but I haven't had a chance to build a demo with it yet.
  • Ravi Gupta #
    Commented on 01-27-2013 at 4:54 AM
    I downloaded the files of your project & tried to run this application on Eclipse(helios) but it didn't work.
    It was not showing any error also.
    When i tried to execute this application, only I found is a BLANK PAGE.
    Could you please help me with this??
  • Commented on 01-27-2013 at 8:17 AM
    Your comment about running it in Eclipse doesn't make sense. Eclipse is your editor. You should be running it in the browser. If you get a blank page, then open your browser's dev tools to see if an error has shown up.
  • Gomes #
    Commented on 01-28-2013 at 10:19 AM
    Interested to see you are going from angular to knockout.In my case i started with knockout and endup with angularjs. knockout support only binding. AngularJS is more than that it is framework whereas knockout is api. knockout has to work with other JS framework but Angular has everthing built in.
  • Commented on 01-30-2013 at 9:13 AM
    Knockout may support less, but that doesn't necessarily make it better for me. :) Frameworks are - imo - a very personal thing. They either work well for you and your team or they don't. To me, Angular just isn't working for me. As I've said now multiple times, this is no reflection on the product itself, but just my personal feelings about it. :)

    I did get a chance to play with Knockout finally and - I dig it. :)
  • Augustin Riedinger #
    Commented on 02-22-2013 at 5:14 AM
    I have been working with this project to learn angular :
    https://github.com/kevinpet/angular-phonecat-coffe...

    and I still haven't managed to understand quite some stuff in the code that I could not find in any other projects but yours.
    As I read in the previous comments, this may be because the code is old and Angular evolved (and they kept the compatibility, but without documenting it).

    The biggest thing I wonder about is where does the Note_ variable comes from ? I can't see anywhere in the doc written that the 'Note' service will be accessible by giving it to the NodeListCtrl by adding an underscore ... but so it seems.

    Besides, I want to change mycode to a module structure, and whenever I write
    app = angular.module('phonecat', ['phonecat.filters', 'phonecat.services'])
    I get this error :
    Uncaught TypeError: Object #<Object> has no method 'module'

    which I don't understand since I have seen it everywhere in any other project.

    A great thanks in advance, thanks for the tutorial, it has already helped me a lot.
  • Commented on 02-22-2013 at 10:50 AM
    Augustin, I really can't help anymore with this. I've not used Angular in so long that I simply do not remember how my old app here works. I wouldn't try to make it work with modern Angular either. As you said, it may work for backwards compat reasons, but it would seem (imo) to be a bad idea to try to use 'old' style Angular on a new project.
  • Steve Gentile #
    Commented on 02-25-2013 at 2:21 PM
    KnockoutJS is good but doesn't contain what you need to build any large SPA. (You could look at Durandal) - this is where AngularJS shines - it's routing, view composition, etc... is baked right in from the start.

    That said, it really depends on the type of application you are building - if it's smaller app and your just looking for the data-binding KnockoutJS is simple and easy to use. If your building a full fledge application, you'll have to provide 3rd party routers, dynamic template html loading, etc... then I'd lean toward AngularJS.

    EmberJS for me is nice - last time I checked though Ember-Data was pre-alpha - maybe when it's matured it would be a good candidate. Also, it had some performance issues that scared me away
  • Deepak MP #
    Commented on 03-25-2014 at 12:47 AM
    I have a simple angulajs application and I can run it on web browser.

    How can I create apk file for this aaplication so that I can install it on an android device.

    Is there any changes needed in my web application for this?
  • Commented on 03-25-2014 at 5:49 AM
    Please see the PhoneGap or Cordova docs for creating APKs.

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty