• 2009-12-01 14:00:00

    The world with Angular - Part III

    Continued from Part II

    Presentation of Data

    Presenting data with <angular/> is really easy. All we need to do is to tell <angular/> how we want the data to be presented and where.

    From Part II you have seen that I have declared an entity called Incident. Also in the <body> tag I have initialized the <angular/> engine, requested all() the Incident objects and stored them in the incidents variable.

    <body ng-entity="incident=Incident" ng-init="incidents=Incident.all()">
    

    I am creating a HTML table where I will display all the records. The header of the table is as follows:

    <tr>
      <th> </th>
      <th>Date</th>
      <th>Shift Start/End</th>
      <th>Animal Code</th>
      <th>Situation Code</th>
      <th>Resolution Code</th>
      <th>City</th>
      <th>State</th>
      <th>Description</th>
    </tr>
    

    Now I need to show the data on screen. I can reference the variables that I have used earlier in my input elements i.e. incident.shiftstart, incident.shiftend, incident.animalcode etc. The variables represent the fields of each record so incident is the document/record if you like and animalcode is the field. If I add them just like that, <angular/> will correctly show me data but only one record since it will not know otherwise. As usual I will need a loop for this. The loop will work directly on the <tr> definitions to give me a row per record.

    <tr ng-repeat="record in incidents">
      <td class="centercell" style="white-space:nowrap;">
        E D
      </td>
      <td class="centercell">{{record.shiftdate}}</td>
      <td class="centercell">{{record.shiftstart}}<br />{{record.shiftend}}</td>
      <td class="centercell">{{record.animalcode}}</td>
      <td class="centercell">{{record.situationcode}}</td>
      <td class="centercell">{{record.resolutioncode}}</td>
      <td class="centercell">{{record.city}}</td>
      <td class="centercell">{{record.state}}</td>
      <td>{{record.details}}</td>
    </tr>
    

    I am using the ng-repeat directive to repeat that block of code. Since I am using it in a <tr> the ng-repeat will match the closing tag for that element i.e. </tr>. ng-repeat repeats that block of code for every recordin incidents. incidentsis the variable I have stored all the results earlier from the <body> declaration. Each piece of information is stored in the database and referenced the same way that it was stored. Note that I have used the variable name recordto access the loop elements but I could have used anything I liked. recordrefers to one object of the incidentsvariable at a time.

    The full block of code is below:

    <table style='width:100%'>
    <tr>
      <th> </th>
      <th>Date</th>
      <th>Shift Start/End</th>
      <th>Animal Code</th>
      <th>Situation Code</th>
      <th>Resolution Code</th>
      <th>City</th>
      <th>State</th>
      <th>Description</th>
    </tr>
    <tr ng-repeat="record in incidents">
      <td class="centercell" style="white-space:nowrap;">
        E D
      </td>
      <td class="centercell">{{record.shiftdate}}</td>
      <td class="centercell">{{record.shiftstart}}<br />{{record.shiftend}}</td>
      <td class="centercell">{{record.animalcode}}</td>
      <td class="centercell">{{record.situationcode}}</td>
      <td class="centercell">{{record.resolutioncode}}</td>
      <td class="centercell">{{record.city}}</td>
      <td class="centercell">{{record.state}}</td>
      <td>{{record.details}}</td>
    </tr>
    </table>
    

    So now I have a basic application that allows me to store and display data. If you have tried this code so far and run it, you will see that the data is entered immediately on the screen and there are no page refreshes which significantly enhances the user experience.

    You might have noticed that I have "E D" in the first column of every row of the HTML table. These are for Edit and Delete.

    Editing Data

    Each record has a unique identifier as I showed you earlier when adding records. In order for the "E" to link to the respective record, I need to use that id. The hyperlink around the "E" becomes then:

    <a href="#incident={{record.id}}">E</a>
    

    Every document/record has an id field. I can it as usual with the double brackets and referencing the incident loop variable (inside the <tr> loop). Note that since I have decided to name my entity incident, I am referencing each record with that parameter in the URL. If my entity's name was different, say testentity, then the URL would have changed to:

    <a href="#testentity={{record.id}}">E</a>
    

    html

    Deleting Data

    To delete data, instead of referencing the unique identifier of each record, I will use the record variable and the $delete() method on it. Note that the recordvariable is the one that allows us to have access to every object in the incidentsvariable and it is used in the display data loop.:

    <a href="#" ng-action="record.$delete()">D</a>
    
    Searching Data

    The initial design never catered for emptying the database in regular intervals. Therefore a search function is in order to ensure quick retrieval of information. As you will see it is really easy and it does not need any <form> elements or anything else.

    The search box is just an <input> element with a specific name (which can be whatever you like)

    [ Search: <input name="wrlfilter" /> ]
    

    This piece of code appears above the table that displays data in my example. I need to make one more change to ensure that the data is filtered. The change is in the <tr> statement where I run the loop to display the data. So the block of code:

    <tr ng-repeat="record in incidents">
    

    becomes

    <pre class="brush:html"><tr ng-repeat="record in incidents.$filter(wrlfilter)">
    

    As you can see I am using the $filter() method and the parameter passed is the name of the input box that I have defined earlier. This way whatever I type in the search box, <angular/> will try to match it with the currently displayed data and filter accordingly, thus giving me the search functionality that I want.

    Sorting data

    Another easy task in <angular/> is sorting. Since I already have a table that I present data with, I am going to use that and its table headings to allow my user to sort. Also I am going to have a default sorting option of Shift Date descending and Shift Start descending.

    In general sorting is done by the $orderBy(), $orderByToggle() and $orderByDirection() methods. The + or - prefixing the name of a field passed defines ascending or descending order. To create a compound sorting key with multiple fields I need to enclose the field names in quotes and separate them with commas.

    First of all I need to initialize the sorting mechanism. The best place for that is the table that displays the data. Therefore I get:

    <table style='width:100%' ng-init="wrlorder=['-shiftdate','-shiftstart']">
    

    This statement initializes the wrlorder variable to contain a shiftdate and a shiftstart field in descending order. Note that the shiftdate and shiftstart are the same names of the fields that I have used throughout this article.

    I save my changes and reload the page but there is no sorting. I actually haven't told <angular/> what data to sort. Since the data that I want to sort are in the table, I will enhance my $filter() method with an $orderBy(). This is the statement that we have worked with earlier.

    <tr ng-repeat="record in incidents.$filter(wrlfilter).$orderBy(wrlorder)">
    

    Save and refresh and voila! The results are sorted by Shift Date descending and Shift Start descending. If I enter a new record, it will be displayed on screen sorted in the correct position. Note that the statement above allows for a combination of methods to be run against the incidents variable (filter and sort).

    For the purposes of this exercise and for debugging, I have also added the wrlorder variable next to the search box so that I know what my sort fields are.

    [ Search: <input name="wrlfilter" /> ] - [ Order: {{wrlorder}} ]
    

    Refreshing the page shows me now

    - [ Order: -shiftdate, -shiftstart ]
    

    I am nearly there. All I need to do now is to make the table headings clickable so that the data is sorted anyway I want to. I will use the $orderByToggle() and $orderByDirection() methods on the wrlorder variable. My table heading becomes:

    <th> </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('shiftdate')" 
           ng-class="order.$orderByDirection('shiftdate')">Date</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('shiftstart')" 
           ng-class="order.$orderByDirection('shiftstart')">Shift Start</a>/
        <a href="" 
           ng-action="order.$orderByToggle('shiftend')" 
           ng-class="order.$orderByDirection('shiftend')">End</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('animalcode')" 
           ng-class="order.$orderByDirection('animalcode')">Animal Code</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('situationcode')" 
           ng-class="order.$orderByDirection('situationcode')">Situation Code</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('resolutioncode')" 
           ng-class="order.$orderByDirection('resolutioncode')">Resolution Code</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('city')" 
           ng-class="order.$orderByDirection('city')">City</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('state')" 
           ng-class="order.$orderByDirection('state')">State</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('details')" 
           ng-class="order.$orderByDirection('details')">Description</a>
    </th>
    

    The ng-action allows for the interchange in sorting order (ascending/descending) while the ng-class shows a nice arrow next to the header name, indicating the current sort order.

    Refreshing the page shows me the final product. I can now sort in any way I like and the sorting is compounded.

    Widgets

    <angular/> has a lot of widgets that can be used as validators but also as means to enhance the user experience. One of them is the DatePicker. I am going to use it to collect data in the shiftdate field. So the:

    <input name="incident.shiftdate" />
    

    becomes:

    <input name="incident.shiftdate" ng-widget="datepicker" size="8" />
    

    and that's it. Now when I click on the input box or when it gets focus, a nice dropdown calendar appears that allows me to select the date.

    Final thoughts

    The example above is not the final product for the WRL. There are some things missing, such as clearing up the order, enhancing the search, expanding/collapsing the add new record etc. This article is not meant as the final solution but more as a guide on what is feasible.

    <angular/> is definitely a new way of looking at web programming. It is fast, agile and easy to learn. <angular/> provides the hobbyist a tool that they can use to create an online application that will suit their needs without complex installations, expensive hosting companies, RDBMS management etc. I can also see experienced programmers using it to address a quick fix or a very urgent requirement that demands RAD.

    I encourage you to visit getangular.com} and give <angular/> a try. I am sure you will not be disappointed.

  • 2009-12-01 13:00:00

    The world with Angular - Part II

    Continued from Part I

    The Wildlife Rescue League application

    Design

    For those that do not know, the Wildlife Rescue League

    is a non-profit organization providing care for sick, injured and orphaned wildlife in order to return them to the wild. Our licensed rehabilitators, located throughout Virginia and suburban Maryland, work with animal shelters, humane societies, wildlife groups, nature centers and veterinary hospitals to provide care to creatures in need. WRL operates a wildlife hotline in the Northern Virginia and surrounding areas to assist the public in obtaining information and assistance in locating a wildlife rehabilitator. WRL is also committed to educating the public about the natural history of native wildlife, coexisting with it and preventing the need for wildlife rehabilitation. We can provide brochures, educational material and educational programs to suit your needs.

    The WRL hotline records all phone calls that the volunteers answer and organizes that data in a manner that would help the organization in the future (anticipated call volume etc.) In the past the data collection method was a simple sheet of paper that was mailed to one volunteer, who then had to decode everything and create the relevant spreadsheet for data analysis. Later on this model evolved into a spreadsheet which was copied and distributed to the volunteers. Again the data had to be collected (via email now) and merged for a meaningful analysis to take place.

    I have therefore created a simple application using <angular/> to allow the volunteers to enter their data in a centralized repository. I am going to explain in detail how I utilized <angular/> to perform this task, needing only a few hours (mostly spent in cosmetic changes) until I had a working copy of what I wanted.

    This blog post will be missing the authentication mechanism and a couple of other bits and pieces. I am hoping to show you a different way or programming and encourage you to explore <angular/> and its power.

    Thinking about my application I will need to store the data. I will need the following fields:

    Date
    Shift Start
    Shift End
    Animal Code
    Situation Code
    Resolution Code
    City
    State
    Description of incident
    
    Creating the database

    The data needs to be stored in the database. So logging into my account in getangular.com (do so if you haven't done this already) I created my library and my new database (both called testwrl) and I am set. Everything else will be controlled in the HTML document.

    Initial HTML file

    My HTML file is very simple.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=utf-8" />
    <meta name="robots" content="index, follow" />
    <style type="text/css">
    body {
        font-family:Verdana,Arial,Helvetica;
        font-size:10pt;
    }
    th,td {
        text-alignment:center;
    }
    th, .inputBox {
        background:#000066;
        color:#00FF00;
        font-weight:bold;
    }
    .incident {
        border:1px solid #000000;
    }
    .incidentheader {
        padding:5px;
        border:1px solid #000000;
        font-weight:bold;
        background:#000099;
        color:#00EE00;
    }
    .centercell {
        text-align:center;
    }
    img {
        border:none;
    }
    </style>
    <script type="text/javascript"
            src="http://testwrl.getangular.com/angular-1.0a.js#database=testwrl"></script>
    </head>
    <body>
    </body>
    </html>
    

    Note the JavaScript line at the bottom part of the snippet. It references a subdomain of getangular.com (testwrl.getangular.com) as well as the database I am using to store data (database=testwrl).

    Creating the HTML input elements

    I need to create a form to store the data. The programming is done with HTML <input> elements. Once I have everything mapped nicely on screen I need to bind them in the database. First of all I need to describe what I want to work with. I will use the ng-entity attribute in my body element. The ng-entity uses the expression [instance=]Entity[:template]. Effectively Entity is the name of the entity that will be stored in my database under that name.

    Therefore in my HTML file I need to change the body element:

    <body ng-entity="incident=Incident" ng-init="incidents=Incident.all()">
    

    So I am storing the Incident in the database. incident is the document of this entity (Incident) that is stored under the name of that instance.

    The ng-init declaration in the body element assigns all the records of the entity Incident (Incident.all()) to a variable (incidents). The name of the variable is arbitrary - you can choose whatever you like.

    Since incident is the document, I need to reference all my elements (the input ones) towards that. The way this is done is via the name attribute of each of the HTML elements. So for instance for the Date that I want to store, my HTML input declaration changes to:

    <input name="incident.shiftdate" />
    

    I repeat the same methodology and I name my elements incident.shiftstart, incident.shiftend, incident.animalcode etc.

    Notable is the fact that I haven't used the <form> element at all. I am going to add a Save button at the end of this HTML block which is nothing more than a submit element:

    <input type="submit" value="Save" />
    

    <angular/> takes care of all the data posting from the browser to the server so I do not need any <form> elements and POST control.

    The HTML script as is right now is below:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
    <meta http-equiv="Content-Type"
             content="text/html; charset=utf-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="-1" />
    <meta http-equiv="Cache-Control" content="no-cache" />
    <meta name="description"
             content="Wildlife Rescue League Hotline" />
    <meta name="keywords"
             content="WRL, wildlife, hotline, volunteer work" />
    <meta name="robots" content="index, follow" />
    <style type="text/css">
    body {
      font-family:Verdana,Arial,Helvetica;
      font-size:10pt;
    }
    th,td {
      text-alignment:center;
    }
    th, .inputBox {
      background:#000066;
      color:#00FF00;
      font-weight:bold;
    }
    .incident {
      border:1px solid #000000;
    }
    .incidentheader {
      padding:5px;
      border:1px solid #000000;
      font-weight:bold;
      background:#000099;
      color:#00EE00;
    }
    .centercell {
      text-align:center;
    }
    img {
      border:none;
    }
    </style>
    <script type="text/javascript"
      src="http://testwrl.getangular.com/angular-1.0a.js#database=testwrl"></script>
    </head>
    <body ng-entity="incident=Incident" ng-init="incidents=Incident.all()">
      <span style='float:right;font-size:10px;'>
        Powered by <a href='http://www.getangular.com'>`<angular/>`</a>
      </span>
      <br />
      <div class="incident">
        <div class="incidentheader">WRL Hotline Incidents</div>
        [ <a href="#">New Incident</a> ]
        <br />
        <table style='width:100%'>
        <tr>
          <td style='text-align:right;'>
            <label>Date</label>
          </td>
          <td>
            <input name="incident.shiftdate" />
          </td>
          <td style='text-align:right;'>
            <label>Shift Start/End</label>
          </td>
          <td>
            <select name="incident.shiftstart">
              <option value=""></option>
              <option value="08:00 AM">08:00 AM</option>
              <option value="08:30 AM">08:30 AM</option>
              <option value="09:00 AM">09:00 AM</option>
              <option value="09:30 AM">09:30 AM</option>
              <option value="10:00 AM">10:00 AM</option>
              <option value="10:30 AM">10:30 AM</option>
              <option value="11:00 AM">11:00 AM</option>
              <option value="11:30 AM">11:30 AM</option>
              <option value="12:00 PM">12:00 PM</option>
              <option value="12:30 PM">12:30 PM</option>
              <option value="13:00 PM">01:00 PM</option>
              <option value="13:30 PM">01:30 PM</option>
              <option value="14:00 PM">02:00 PM</option>
              <option value="14:30 PM">02:30 PM</option>
              <option value="15:00 PM">03:00 PM</option>
              <option value="15:30 PM">03:30 PM</option>
              <option value="16:00 PM">04:00 PM</option>
              <option value="16:30 PM">04:30 PM</option>
              <option value="17:00 PM">05:00 PM</option>
              <option value="17:30 PM">05:30 PM</option>
              <option value="18:00 PM">06:00 PM</option>
              <option value="18:30 PM">06:30 PM</option>
              <option value="19:00 PM">07:00 PM</option>
              <option value="19:30 PM">07:30 PM</option>
              <option value="20:00 PM">08:00 PM</option>
              <option value="20:30 PM">08:30 PM</option>
            </select>
             /
            <select name="incident.shiftend">
              <option value=""></option>
              <option value="08:00 AM">08:00 AM</option>
              <option value="08:30 AM">08:30 AM</option>
              <option value="09:00 AM">09:00 AM</option>
              <option value="09:30 AM">09:30 AM</option>
              <option value="10:00 AM">10:00 AM</option>
              <option value="10:30 AM">10:30 AM</option>
              <option value="11:00 AM">11:00 AM</option>
              <option value="11:30 AM">11:30 AM</option>
              <option value="12:00 PM">12:00 PM</option>
              <option value="12:30 PM">12:30 PM</option>
              <option value="13:00 PM">01:00 PM</option>
              <option value="13:30 PM">01:30 PM</option>
              <option value="14:00 PM">02:00 PM</option>
              <option value="14:30 PM">02:30 PM</option>
              <option value="15:00 PM">03:00 PM</option>
              <option value="15:30 PM">03:30 PM</option>
              <option value="16:00 PM">04:00 PM</option>
              <option value="16:30 PM">04:30 PM</option>
              <option value="17:00 PM">05:00 PM</option>
              <option value="17:30 PM">05:30 PM</option>
              <option value="18:00 PM">06:00 PM</option>
              <option value="18:30 PM">06:30 PM</option>
              <option value="19:00 PM">07:00 PM</option>
              <option value="19:30 PM">07:30 PM</option>
              <option value="20:00 PM">08:00 PM</option>
              <option value="20:30 PM">08:30 PM</option>
            </select>
          </td>
        </tr>
    
        <tr>
          <td style='width:20%;text-align:right;'>
            <label>Animal Code</label>
          </td>
          <td>
            <select name="incident.animalcode">
              <option value=""></option>
              <option value="B">Songbird</option>
              <option value="C">Corvine</option>
              <option value="M">Mammal</option>
              <option value="R">Raptor</option>
              <option value="RE">Reptile</option>
              <option value="U">Unknown</option>
              <option value="W">Waterfowl</option>
    
              <option value="OTH">Other</option>
            </select>
          </td>
          <td style='text-align:right;'>
            <label>Situation Code</label>
          </td>
          <td>
            <select name="incident.situationcode">
              <option value=""></option>
              <option value="A">Attacked (details for attacker)</option>
              <option value="I">Injured</option>
              <option value="K">Killed</option>
              <option value="N">Nuisance (explain in details)</option>
              <option value="O">Orphaned</option>
              <option value="OTH">Other (explain in details)</option>
              <option value="U">Unknown</option>
            </select>
    
            <label>Resolution Code</label>
    
            <select name="incident.resolutioncode">
              <option value=""></option>
              <option value="D">Died</option>
              <option value="GA">Gave advice only</option>
              <option value="LM">Left Message</option>
              <option value="OTH">Other</option>
              <option value="RR">Referred to Rehabber</option>
              <option value="RS">Referred to Shelter</option>
              <option value="RV">Referred to Vet</option>
              <option value="WCB">Watch and Call Back</option>
            </select>
          </td>
        </tr>
        <tr>
    
          <td style='width:20%;text-align:right;'>
            <label>City</label>
          </td>
          <td>
            <input name="incident.city" type="text" />
          </td>
          <td style='text-align:right;'>
            <label>State</label>
          </td>
          <td>
            <select name="incident.state">
              <option value=""></option>
              <option value="MD">Maryland</option>
              <option value="VA">Virginia</option>
              <option value="DC">Washington DC</option>
            </select>
          </td>
        </tr>
        <tr>
          <td style='width:20%;text-align:right;'>
            <label>Description</label>
          </td>
          <td colspan="3">
            <textarea name="incident.details" rows="5" cols="80"></textarea>
          </td>
        </tr>
        </table>
        <input type="submit" value="Save" class="inputBox" />
      </div>
    
    Adding data

    The entry screen is complete and if you type some data and click the Save button, you will store that data in the database. You will notice that this is the case since your URL will change to something like

    .../#incident=abcdefg7896456464532132135498a
    

    That is effectively the id of that record. If you delete that #incident..... and reload the page, you will be presented with an empty form where you can add more data. Clicking the Save button will save that record and you will notice that the new id is different than the previous one.

    I really don't want to have to delete the #incident.... from my address bar every time I want to add a new record. I am sure that the users of the WRL will not like it either. For that, I have added a link at the top of the screen which is already in the script that I have a few lines up. The line:

    [ <a href="#">New Incident</a> ]
    

    is the one that will reload the page allowing me to add a new record whenever I click that link.

    Continued in Part III

  • 2009-12-01 12:00:00

    The world with Angular - Part I

    If you haven't heard about <angular/>, this article is intended to give you a brief overview of the features and functionality that it exposes.

    <angular/> is a declarative programming language that provides the programmer with a wealth of tools using a simplistic model. <angular/> lives in the HTML file and references all the necessary libraries and tools using a single JavaScript file. This approach removes the need of complex scripts, installations of development/production environments (like AMP, Microsoft equivalent or other) and makes upgrading a breeze since the developer doesn't have to deal with it - it's all managed in the cloud. Development IDEs are rendered obsolete and all the developer needs is Notepad/Textmate/gEdit or a similar text editor.

    Backed by the latest cloud technologies, <angular/> is built for scalability. It can handle virtually any task or load right off the box but should the developer need more resources, they can easily contact Brat Tech LLC. and additional arrangements can be made. Ruby on Rails and Amazon EC2 along with CouchDB are the core components that utilizes, making it highly scalable, reliable and extremely fast!

    According to their website (http://www.getangular.com/) <angular/> provides:

    • A managed database - manages data for you
    • HTML & CSS - Full control of the look and feel
    • Security - Authentication and permissions
    • Embeddable - In third-party sites such ad blogs and wikis
    • Declarative - No need to learn a new language
    • Rich Widgets - Date-picker, Barcodes, Charts, etc.

    All of the above (and much more available through the documentation make <angular/> a really good and wise choice for the starter in web programming, the hobbyist who wants some variety in his/her website, the knowledgeable web developer or the expert in web programming.

    So why am I excited about this? For starters it is a really easy language to learn. It is by no means intimidating to any user and people that always wanted to enhance their website with some 'scripting' magic, can do so now with with no installations of any software on their sites.

    To start you need to go and create an account on the website (http://www.getangular.com/). In there you can select your subscription level. There is a free subscription (the default) and two paid ones at 14.95 USD and 45.95 USD per month respectively. The different subscription levels provide more functionality (i.e. SSL support, versioning, more documents etc.)

    Continued in Part II