Google+

AngularJS - Introducing Templates

AngularJS is a Javascript MVC framework from the fine folks over at Google. The focus of Angular is building complex HTML based client applications. Its design philosophy is data first, where your data will be updating the DOM. Contrast this to a framework like JQuery where the DOM will update your data.

AngularJS Logo

This is the sixth in a series of posts on AngularJS where we are using Chemistry data from the periodic table to help us understand the framework. The others posts are

  1. AngularJS - Introduction
  2. AngularJS - Introducing AngularJS Controllers
  3. AngularJS - Introducing NG-Repeat
  4. AngularJS - More with NG-Repeat
  5. AngularJS - Image Binding
  6. AngularJS - Introducing Templates
  7. AngularJS - Introducing Routing
  8. AngularJS - Introduction to Services
  9. AngularJS - Introduction to Directives
  10. AngularJS - Further with Directives
  11. AngularJS - Best Practices
  12. AngularJS - CSS Animations

Note: AngularJS does not allow for more than one ng-app directive. When I have multiple angular posts on the home page of my blog, only one application will work. I need to refactor the entire site to account for this. All of that to say this, you are best clicking on a single article so you can see the pages in action.

AngularJS has a lot of great features for working with HTML that allows you to split up development when you are doing team projects. The binding approach and directives allows for a nice separation between developers and designers. Another great piece of the platform is the ability to separate out HTML that you are using over and over again into a template file that can then be called by multiple different pages.

In our previous example, we were consistently displaying the same repeatable line in our ng-repeat directive. This is a great example of repeatable markup that we can extract from our page and consolidate into a single location using a template.

To do this, we take our HTML line we were using for the data binding.

    <td><a href="http://en.wikipedia.org/wiki/{{element.name}}" Title="Wikipedia article on {{element.name}}" target="_top">{{element.name}}</a></td>
            <td>{{element.atomicNumber}}</td>
            <td >{{element.atomicWeight}}</td>
            <td>{{element.phase}}</td>
            <td>{{element.ionization}}</td>
            <td>{{element.melting}}</td>
            <td>{{element.boiling}}</td>

and we move it into a separate HTML file, in our case we will call it element1.html. Since we are only dealing with a fragment of HTML, we only want to include the markup we will use for binding, and no other semantically correct markup like the HTML header, etc.

In our controller file, we setup an object that identifies the path to our template as in the following code snippet

$scope.template =  {name: 'template1.html', url: 'js/templates/element1.html'};

Within the HTML page, we then reference the template file using the ng-include directive like so

   <tr data-ng-repeat="element in periodic.elements | filter:{name:elementNameOnlyTable} | limitTo:15" data-ng-include="template.url">

Our page then renders the same as before, but we can now use the template in multiple places. For large teams, a most excellent added benefit is you have removed contention issues on the file. Note: We are only displaying 15 results to limit the page size.

{{$$index}}
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling

Since we our look and feel is controlled by templates, we can also have programmatic control over the look and feel by dynamically setting the template. In our last post, we had committed crimes against web design by using Packer colors.

We can do something similar here by having a second template get loaded by our page. To do this, we update our controller to have an array of templates, and choose which one we want, in this case, an ugly Gold.

controller('chemTemplateCtrl2',
    function chemCtrl($scope) {
        $scope.periodic = perioicData;

        $scope.templates =
            [ { name: 'template1.html', url: 'js/templates/element1.html'}
                , { name: 'template2.html', url: 'js/templates/element2.html'}];
        $scope.template = $scope.templates[1];

    }

Our table now looks even more unattractive

{{$$index}}
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling

Next, given that we have an array of template objects, we can dynamically change the look and feel on the fly using a drop down list to select the template for display. In the example below, we are taking our array of templates and binding to it in a select list. The value of the selected item is then used as the template for page display, dynamically changing the UI.

<div id="periodicTable3"  data-ng-controller="chemTemplateCtrl2" >
    <form id="frmChem3">
        <select data-ng-model="templateSelect" ng-options="template.name for template in templates"></select>


        <input type="text" data-ng-model="elementNameOnlyTable"/>
        <table class="table table-striped table-bordered table-hover table-condensed">
            <tr>
                <th>Name</th><th>Atomic Number</th><th>Weight</th><th>Phase</th><th>Ionization Energy</th><th>Melting Point</th><th>Boiling</th>
            </tr>
            <tr data-ng-repeat="element in periodic.elements | filter:{name:elementNameOnlyTable} | limitTo:15" data-ng-include="templateSelect.url">

            </tr>
        </table>
    </form>
</div>
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling

We can also hide and show various columns based on templates. We will add a third template to our list of elements, but this template will just show the element name, the atomic weight and the atomic number. Since our table header rows exist outside of our template, what we want to be able to do is dynamically hide table header rows when our subset template, element3.html, is selected. We can do this by using a ng-show attribute on the table header elements that binds to our template name like so

<th>Name</th><th>Atomic Number</th><th>Weight</th><th data-ng-show="templateSelect.name!='template3.html'">Phase</th><th data-ng-show="templateSelect.name!='template3.html'">Ionization Energy</th><th data-ng-show="templateSelect.name!='template3.html'">Melting Point</th><th data-ng-show="templateSelect.name!='template3.html'">Boiling</th>
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling

Having "logic" like this, where we are setting columns based on the selected template is not the most correct way to do this though. You rarely (and I mean rarely) want any of the logic that controls the display of the page in the page. We can easily create a function in our controller to set if we are displaying all of the data or just a subset based on the selected template. You can see this in the displayAll function in the final version of our controller

controller('chemTemplateCtrl3',
    function chemCtrl($scope) {
        $scope.periodic = perioicData;

        $scope.templates =
            [ { name: 'template1.html', url: 'js/templates/element1.html'}
                , { name: 'template2.html', url: 'js/templates/element2.html'}
                , { name: 'template3.html', url: 'js/templates/element3.html'}];
        $scope.templateSelect = $scope.templates[0];

        $scope.displayAll =  function() {
            if ($scope.templateSelect.name == $scope.templates[2].name) {
                return false;
            }
            else {
                return true;
            }
        }
    }

and our HTML gets updated to

<th>Name</th><th>Atomic Number</th><th>Weight</th><th data-ng-show="displayAll()">Phase</th><th data-ng-show="displayAll()">Ionization Energy</th><th data-ng-show="displayAll()">Melting Point</th><th data-ng-show="displayAll()">Boiling</th>
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling

As you can see, templates in AngularJS offer a lot of functionality. They allow for splitting up of work across large development teams. You can also dynamically update the look and feel based on the template the user selects. This is all happening client side too, reducing round trips to the server. Good stuff!

You can either visit http://angularperiodic.azurewebsites.net/ to see the code in action and as always find the code out on GitHub


 

AngularJS - Image Binding in AngularJS with Lou Reed

AngularJS is a Javascript MVC framework from the fine folks over at Google. The focus of Angular is building complex HTML based client applications. Its design philosophy is data first, where your data will be updating the DOM. Contrast this to a framework like JQuery where the DOM will update your data.

AngularJS Logo

This is the fifth in a series of posts on AngularJS where we are using Chemistry data from the periodic table to help us understand the framework. The others posts are

  1. AngularJS - Introduction
  2. AngularJS - Introducing AngularJS Controllers
  3. AngularJS - Introducing NG-Repeat
  4. AngularJS - More with NG-Repeat
  5. AngularJS - Image Binding
  6. AngularJS - Introducing Templates
  7. AngularJS - Introducing Routing
  8. AngularJS - Introduction to Services
  9. AngularJS - Introduction to Directives
  10. AngularJS - Further with Directives
  11. AngularJS - Best Practices
  12. AngularJS - CSS Animations

Note: AngularJS does not allow for more than one ng-app directive. When I have multiple angular posts on the home page of my blog, only one application will work. I need to refactor the entire site to account for this. All of that to say this, you are best clicking on a single article so you can see the pages in action.

If you have spent any time with me in the real world, you quickly come to realize I am a music fan. If you have spent a little time with me, especially when I was a younger, you realize I am a huge Lou Reed fan too. My boss, Tim VanHammond still holds it against me the one time I played Lou Reed's the Possum song without headphones when he was in the office we shared. The reality is, I probably spent WAY too much time listening to Lou Reed and Velvet Underground music when I was a teenager. So, having said all that, soon after Lou Reed died, I was doing a presentation at the NorthEast Wisconsin Developer UserGroup (aka, NEWDUG and yes it is a lot to type) and I thought it would be fun to have a little Lou Reed shout out. Since the talk was about AngularJS, it of course had an Angular flavor. So we did a little Lou Reed segue, and talked about binding images using AngularJS. Most important and of interest to you dear reader, is how I found out a way to deal with missing images!

Lou Reed

Instead of using chemistry data, we used Lou data. This data was a JSON object of record releases. Lou put out a lot of albums, and we are loading images, so to keep things quick, we are just doing the first few releases. One of our Lou objects looks like

 {
            "album": "Lou Reed",
            "artist": "Lou Reed",
            "releaseDate": 6/1/1972,
            "image": "http://upload.wikimedia.org/wikipedia/en/8/88/Lour72.jpg",
            "label": "RCA"
        }

Essentially what we are doing is the same stuff that came before, instantiating the app and referencing our controller and looping through the albums and making a little table. This is quickly becoming old hat for us!

Lou Reed Albums

AlbumReleaseProducerLabel
{{album.album}} {{album.releaseDate|date}} {{album.producer}} {{album.label}} {{album.album}} Cover Image

Note: There is an issue with the markup being generated by DocPad prepending the local URL. You can see the actual correct HTML at http://angularperiodic.azurewebsites.net/Demo05-ImageBindingWithLouReed/lou.html until the I fix this.

Again, we have done this before. The interesting thing to note though is the album release date. JavaScript and dates are something that make me a little crazy, but having said that, AngularJS does a nice job. You can pass a JSON date object to a filter and have it format, {{album.releaseDate|date}} as above. More info on the date filter at the AngularJS site.

The second thing to notice is we are using a src attribute in our img tag. Things are loading fine, but if you look at the page load, you will actually see a 404 error that happens.

404 Error on image Get

This is because the AngularJS functionality is executed AFTER the page loads. We are still making the get request that the src attribute had identified because we have yet to get into the javscript processing on the page. With src attributes and href tags you that reference AngularJS objects, this is something you need to be cognizant of. The fix is easy enough, we just prepend a ng tag on our attribute. This will then be part of the page processing that happens during the Angular load. Our image reference is now

<img ng-src="{{album.image}}" alt="{{album.album}} Cover Image" width="300px;" height="300px;">

Adding ng-src

AlbumReleaseProducerLabel
{{album.album}} {{album.releaseDate|date}} {{album.producer}} {{album.label}} {{album.album}} Cover Image
The last thing to hit on is on the image loading, and a cool little trick I learned. One of the customer sites I work at it is using AngularJS and they are integrating with a third party data source. What they wanted to be able to do is not show a broken or missing image. I sat there and thought to myself that this is going to be pretty difficult. Our client side scripting language was going to have to not call the source directly, but go through some kind of nutty proxy we would develop that would check if the image existed and then send that image or else a proxy for the missing image, etc. It wasn't going to be fun. However, a little time on the Google found a MUCH easier way to get by this. To expand on our discussion, our images are coming from Wikipedia and reference images they have for entries on Lou Reed albums. If one of these images changes, we would get an error and display a broken image marker on our page. To illustrate, I have created a second controller where we change the image for Metal Machine Music and Transformer so they would return 404s. We now are receiving missing image links in our page. This is NOT use friendly 404 Error on Album Cover You can see the example below

Missing Images in Javascript Object

AlbumReleaseProducerLabel
{{album.album}} {{album.releaseDate| date:'medium'}} {{album.producer}} {{album.label}} {{album.album}} Cover Image

The fix is easy enough though! We can add an on error function within our img tag. First, we want to add a default local image we can reference. So let us use the Wikipedia image that I have at the top of our page.

Within our img tag we add an onerrror function like so

 onerror="this.src='Lou_Reed.jpg'"

so that our entire img tag is now

<img ng-src="{{album.image}}" alt="{{album.album}} Cover Image" width="300px;" height="300px;"  onerror="this.src='Lou_Reed.jpg'">

and we now handle missing images on page load (with ng-src) and missing images in our data source, with onerror by placing a default placeholder image for missing images.

Resolve missing images

AlbumReleaseProducerLabel
{{album.album}} {{album.releaseDate| date:'medium'}} {{album.producer}} {{album.label}} {{album.album}} Cover Image

So the when I gave my presentation someone asked me favorite Lou Reed album... Hard one, I think at various times most of them have been a favorite, but I will go with New York. Favorite song, same as album, it changes, but let's call it Street Hassle.

You can either visit http://angularperiodic.azurewebsites.net/ to see the code in action and as always find the code out on GitHub

(I am working on some display issues with the post with the Markdown/DocPad combo, I should have updated soon)


 

AngularJS - More with ng-repeat

AngularJS is a Javascript MVC framework from the fine folks over at Google. The focus of Angular is building complex HTML based client applications. Its design philosophy is data first, where your data will be updating the DOM. Contrast this to a framework like JQuery where the DOM will update your data.

AngularJS Logo

This is the fourth in a series of posts on AngularJS where we are using Chemistry data from the periodic table to help us understand the framework. The others posts are

  1. AngularJS - Introduction
  2. AngularJS - Introducing AngularJS Controllers
  3. AngularJS - Introducing NG-Repeat
  4. AngularJS - More with NG-Repeat
  5. AngularJS - Image Binding
  6. AngularJS - Introducing Templates
  7. AngularJS - Introducing Routing
  8. AngularJS - Introduction to Services
  9. AngularJS - Introduction to Directives
  10. AngularJS - Further with Directives
  11. AngularJS - Best Practices
  12. AngularJS - CSS Animations

Note: AngularJS does not allow for more than one ng-app directive. When I have multiple angular posts on the home page of my blog, only one application will work. I need to refactor the entire site to account for this. All of that to say this, you are best clicking on a single article so you can see the pages in action.

In this post, we are going to dig a bit deeper into ng-repeat and discover a few things.

To level set, we have a controller called chemistryApp.js. This creates our AngularJS application

chemistryApp.js

var chemistryApp = angular.module('chemistryApp', []);

and a controller that references are json file of chemistry data and sets it to a scope variable

chemistryController.js

chemistryApp.controller('chemCtrl',
    function chemCtrl($scope) {
        $scope.periodic = perioicData;
    }
);

First up, instead of creating an unordered list, we are going to create a table. This is done by putting the ng-repeat directive on a <tr> tag. For example

 <tr data-ng-repeat="element in periodic.elements ">

Then we can add our data on a cell by cell basis. For this example, we are going to link to a Wikipedia article about each element, and then display things like the atomic number, atomic weight, etc. Tying this with our ng-repeat directive and our table markup becomes

<table class="table table-striped table-bordered table-hover table-condensed">
            <tr>
                <th>Name</th><th>Atomic Number</th><th>Weight</th><th>Phase</th><th>Ionization Energy</th><th>Melting Point</th><th>Boiling</th>
            </tr>
            <tr data-ng-repeat="element in periodic.elements ">
                <td><a href="http://en.wikipedia.org/wiki/{{element.name}}" Title="Wikipedia article on {{element.name}}" target="_top">{{element.name}}</a></td>
                <td>{{element.atomicNumber}}</td>
                <td>{{element.atomicWeight}}</td>
                <td>{{element.phase}}</td>
                <td>{{element.ionization}}</td>
                <td>{{element.melting}}</td>
                <td>{{element.boiling}}</td>
            </tr>
        </table>

We now have a list of the Chemical elements in a table.

NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling
{{element.name}} {{element.atomicNumber}} {{element.atomicWeight}} {{element.phase}} {{element.ionization}} {{element.melting}} {{element.boiling}}

We can also, just like in our previous example, bind a filter to an input box and search for a chemical element by name and have it filter the list automatically.

   <div id="periodicTable"  data-ng-controller="chemCtrl" >
        <input type="text" data-ng-model="elementNameOnlyTable"/>
        <table class="table table-striped table-bordered table-hover table-condensed">
            <tr>
                <th>Name</th><th>Atomic Number</th><th>Weight</th><th>Phase</th><th>Ionization Energy</th><th>Melting Point</th><th>Boiling</th>
            </tr>
            <tr data-ng-repeat="element in periodic.elements | filter:{name:elementNameOnlyTable}">
                <td><a href="http://en.wikipedia.org/wiki/{{element.name}}" Title="Wikipedia article on {{element.name}}" target="_top">{{element.name}}</a></td>
                <td>{{element.atomicNumber}}</td>
                <td>{{element.atomicWeight}}</td>
                <td>{{element.phase}}</td>
                <td>{{element.ionization}}</td>
                <td>{{element.melting}}</td>
                <td>{{element.boiling}}</td>
            </tr>
        </table>
    </div>
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling
{{element.name}} {{element.atomicNumber}} {{element.atomicWeight}} {{element.phase}} {{element.ionization}} {{element.melting}} {{element.boiling}}

The ng-repeat also has some intrinsic variable definitions. One is $index which is the current key of the element of the loop you are on.

Name$IndexAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling
{{element.name}} {{$index}} {{element.atomicNumber}} {{element.atomicWeight}} {{element.phase}} {{element.ionization}} {{element.melting}} {{element.boiling}}

It is interesting to note that the index is for the values you are filtering on too. So if you were to type Aluminum you will see it becomes the zero element, instead of Hydrogen.

Angular also has directives like ng-hide, ng-show and ng-class along with a plethora of other attributes.

We can use these in conjunctions with $index. This is really the key step along the journey in thinking the "Angular way". You don't want to update the dom via javascript, but use Angular to control what is displayed on your page. As an example of this, let's say we want to hide every other element in the list. We could do something like

 <tr data-ng-repeat="element in periodic.elements" data-ng-hide="$index%2">
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling
{{element.name}} {{element.atomicNumber}} {{element.atomicWeight}} {{element.phase}} {{element.ionization}} {{element.melting}} {{element.boiling}}

We can also perform some Crimes Against web design. For example, nothing is uglier than wrapping your table in Green Bay Packer colors of Green and Gold. So I can add CSS elements like

<style>
        .tableOdd {
            background-color: green;
        }
        .tableEven {
            background-color: gold;
        }
    </style>

Then we can use the ng-class on the <tr> we are binding to in order to make even/odd rows change colors.

<tr data-ng-repeat="element in periodic.elements" data-ng-class="{tableEven: !($index%2), tableOdd: ($index%2)}">

It doesn't look pretty, and there are MUCH better ways to do this, but it gives you an example of how to think the "Angular way"

NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling
{{element.name}} {{element.atomicNumber}} {{element.atomicWeight}} {{element.phase}} {{element.ionization}} {{element.melting}} {{element.boiling}}

Last, we can also do fun things like sort by using select lists to quickly filter data. For example, we create a dropdown list for both the element phase

 <select data-ng-model="elementPhase">
            <option value="">All</option>
            <option value="Gas">Gas</option>
            <option value="Solid">Solid</option>
            <option value="Liquid">Liquid</option>
            <option value="Synthetic">Synthetic</option>
        </select>

and an ordering criteria

<select data-ng-model="elementOrder">
            <option value="name" selected>Name (ascending)</option>
            <option value="-name">Name (descending)</option>
            <option value="melting">Melting Point (asc)</option>
            <option value="-melting">Melting Point (desc)</option>
        </select>

We can then use the ng-filter and ng-order directives to filter and sort our table. Further, we can combine with our text box for searching by name and chain them together.

 <tr data-ng-repeat="element in periodic.elements | filter:{name:elementName} | filter:{phase:elementPhase} | orderBy:elementOrder">

Ultimately, our HTML looks like

<div id="periodicTable5"  data-ng-controller="chemCtrl" >
        Element Name:<input type="text" data-ng-model="elementName"/>
        &nbsp;State:
        <select data-ng-model="elementPhase">
            <option value="">All</option>
            <option value="Gas">Gas</option>
            <option value="Solid">Solid</option>
            <option value="Liquid">Liquid</option>
            <option value="Synthetic">Synthetic</option>
        </select>
        &nbsp;Order:
        <select data-ng-model="elementOrder">
            <option value="name" selected>Name (ascending)</option>
            <option value="-name">Name (descending)</option>
            <option value="melting">Melting Point (asc)</option>
            <option value="-melting">Melting Point (desc)</option>
        </select>

        <table class="table ">
            <tr>
                <th>Name</th><th>Atomic Number</th><th>Weight</th><th>Phase</th><th>Ionization Energy</th><th>Melting Point</th><th>Boiling</th>
            </tr>
            <tr data-ng-repeat="element in periodic.elements | filter:{name:elementName} | filter:{phase:elementPhase} | orderBy:elementOrder">
            <td><a href="http://en.wikipedia.org/wiki/{{element.name}}" Title="Wikipedia article on {{element.name}}" target="_top">{{element.name}}</a></td>
                <td>{{element.atomicNumber}}</td>
                <td >{{element.atomicWeight}}</td>
                <td>{{element.phase}}</td>
                <td>{{element.ionization}}</td>
                <td>{{element.melting}}</td>
                <td>{{element.boiling}}</td>
            </tr>
        </table>
    </div>
Element Name:  State:  Order:
NameAtomic NumberWeightPhaseIonization EnergyMelting PointBoiling
{{element.name}} {{element.atomicNumber}} {{element.atomicWeight}} {{element.phase}} {{element.ionization}} {{element.melting}} {{element.boiling}}

The interesting thing to note about this too is that this page is pretty heavy. It has many tables with the Periodic elements. However, by using client side code, we download all of the elements only once when we get our JavaScript file. The client side functionality does the page building, helping the page load quicker.

For those of you who have been web coding for a while, these few number of lines to have all of this client side functionality is magic. Seriously, magic.

You can either visit http://angularperiodic.azurewebsites.net/ to see the code in action and as always find the code out on GitHub


 

John Ptacek I'm John Ptacek, a software developer for Skyline Technologies. This blog is my contains my content and opinionss, which are not those of my employer.

Currently, I am reading Norse Mythology by Neil Gaiman

@jptacekGitHubLinkedInStack OverflowGoogle+