Google+

Calculating the Cost of the 12 Days of Christmas

Every year in December, families gather around the table to celebrate Christmas. If your house is anything like my house, the good times, food and conversation can quickly escalate into a battle over what is the true cost of the classic Christmas Song, the 12 Days of Christmas! This year, I took matters into my own hand and wrote a calculator, that will calculate the cost of the 12 days of Christmas using Google's Javascript Framework, AngularJS. No more bickering at the family table over the holidays!

AngularJS is a Javascript framework from Google that focuses on building complex data applications. It has a very strong data first philosophy, which is a little different than jQuery which focuses more on DOM manipulation. You can find out more about AngularJS at the official site, or read some recent blog posts I have written up at http://www.jptacek.com/tags/angularjs/.

AngularJS is a Model View Controller (MVC) framework. The model represents our data, or an object. In our case, the model will be the cost of the items in the twelve days of Christmas; e.g., a partridge, a pear tree, a turtle dove, etc. The view is our HTML page which is responsible for displaying data. The controller is responsible for marshalling the data to the view, and helping control its state. To enable our 12 Days calculator, we will take advantage of these AngularJS features.

First, we essentially wire up our page to indicate we are going to have an Angular app by putting a ng-app attribute in the HTML body tag. Next, we set an area of our HTML markup that our controller is responsible for with a ng-controller attribute in a DIV tag.

<body ng-app="christmasApp">
<b>12 Days of Christmas Calculator</b>
<div class="container" id="12Days" ng-controller="christmasController">
...
</div>

It is worth nothing that use of data-* attributes is more "semantically" correct. If you want to save yourself keystrokes, you can just use ng-app or ng-controller, but if you get bothered by having warnings in your code, like me, you will want to use ng-app, etc.

Of course, this being Javascript and all, we need references to JavaScript files that contain this logic for implementation. Usually in structuring a project there will be an chemistryApp.js file that sets up your Angular application, controllers for interacting between your model and the view as well as services.

Our chemistryApp.js file, which we call 12DaysApp.js, is just two lines and creates the scope we reference in our body markup, christmasApp.

12DaysApp.js

'use strict';
var christmasApp = angular.module('christmasApp', []);

Our next Javascript file is the controller and this is where the interesting stuff is. Essentially, AngularJS has the concept of a $scope. This is essentially a glorified property bad or state manage for interacting with the page. Within our controller, we create variables on the scope for each of our items. We are grabbing costs from a USA Today story from last year (http://www.usatoday.com/story/money/business/2012/11/26/12-days-of-christmas-107k/1726807/).

To setup our controller, we create a controller with a callback such as

_christmasController.js

christmasApp.controller('christmasController',
    function christmasController($scope) {

);

Notice passing in the $scope variable on the parameter list. AngularJS does a VERY nice job with Dependency Injection. By passing in the $scope variable we are enabling its use within our controller and the HTML view. As you explore Angular, you will come to appreciate how quickly you can add services or logging, etc. with their Dependency Injection framework. We can then create a variable on the scope to set the cost for items we are getting for the 12 days of Christmas. For example, to set the price of a pear tree to $189.99 and a partridge, we have the following lines in our controller javascript $scope.pearTree = 189.99; $scope.partridge = 15.00; We then create a JavaScript function to calculate the cost of the first day. javascript $scope.day1 = function () { return parseFloat($scope.pearTree) + parseFloat($scope.partridge); }; Now, the magic of two way data binding can start, by including the AngularJS library, our application JavaScript file and our Controller JavaScript file, get two way databinding. In our HTML markup, curly braces ( {{ }} ) indicate our AngularJS model. So for the following markup, the ng-model="partridge" on our Input element says that we are two way binding the $scope.partidge value in our UI. And since this binding is part of a HTML Input box, whenever we make changes, it will update our UI. For example, we are displaying the Day 1 whatever value we initially set will be displayed. We then automatically update the value of function day1 via the function. This function is automatically updated and our UI as well whenever we type in a new value. This is pretty magical for developers who used to capture JavaScript key up and down events!

 

12 Days of Christmas

Partridge:
Day 1: {{day1()| currency:"$"}}
Pear Tree:
 
Two Turtle Doves:
Day 2: {{day2()| currency:"$"}}
Three French Hens:
Day 3: {{day3()| currency:"$"}}
Four Colly Birds:
Day 4: {{day4()| currency:"$"}}
Five Gold Rings:
Day 5: {{day5()| currency:"$"}}
Six Geese a-laying:
Day 6: {{day6()| currency:"$"}}
Seven Swans a swimming:
Day 7: {{day7()| currency:"$"}}
Eight Maids a Milking:
Day 8: {{day8()| currency:"$"}}
Nine Ladies Dancing:
Day 9: {{day9()| currency:"$"}}
10 Lords-a-Leaping:
Day 10: {{day10()| currency:"$"}}
Eleven Pipers piping:
Day 11: {{day11()| currency:"$"}}
Twelve Drummers Drumming:
Day 12: {{day12()| currency:"$"}}
Total Cost of the twelve days:
{{totalCostChristmas()| currency:"$"}}

 

christmasController.js Code

'use strict';

christmasApp.controller('christmasController',
    function christmasController($scope) {
        // http://www.irishmirror.ie/female/family/how-much-would-cost-presents-2851366
        // http://www.usatoday.com/story/money/business/2012/11/26/12-days-of-christmas-107k/1726807/
        //one
        $scope.pearTree = 189.99;
        $scope.partridge = 15.00;

        //two
        $scope.turtleDove = 62.50;

        // three
        $scope.frenchHen = 55;

        // four
        $scope.callingBird = 129.24;

        // five
        $scope.goldRing = 150;
        // six
        $scope.geese = 35;

        // seven swans
        $scope.swan = 1000.0;

        // eight
        $scope.milkingMaid = 7.25;

        // nine ladies dancing
        $scope.dancingLady = 700.0;

        // ten lords
        $scope.lord = 476.7;

        // eleven pipers at 232.91/hour
        $scope.piper = 232.91;

        // twelve drummers drumming
        $scope.drummer = 231.33;

        $scope.day1 =  function () {
            return parseFloat($scope.pearTree) +  parseFloat($scope.partridge);

        };

        $scope.day2 = function () {
            return parseFloat($scope.turtleDove) * 2;
        };

        $scope.day3 = function () {
            return parseFloat($scope.frenchHen) * 3;
        };

        $scope.day4 = function () {
            return parseFloat($scope.callingBird) * 4;
        };

        $scope.day5 = function () {
            return parseFloat($scope.goldRing) * 5;
        };

        $scope.day6 = function () {
            return parseFloat($scope.geese) * 6;
        };

        $scope.day7 = function () {
            return parseFloat($scope.swan) * 7;
        };

        $scope.day8 = function () {
            return parseFloat($scope.milkingMaid) * 8;
        };

        $scope.day9 = function () {
            return parseFloat($scope.dancingLady) * 9;
        };

        $scope.day10 = function () {
            return parseFloat($scope.lord) * 10;
        };

        $scope.day11 = function () {
            return parseFloat($scope.piper) * 11;
        };

        $scope.day12 = function () {
            return parseFloat($scope.drummer) * 12;
        };

        $scope.totalCostChristmas = function() {
            return  12* $scope.day1() + 11* $scope.day2() + 10*  $scope.day3() +  9 * $scope.day4()+ 8 * $scope.day5()+ 7* $scope.day6() +
                6 * $scope.day7() +  5* $scope.day8() +  4* $scope.day9() + 3* $scope.day10() +  2* $scope.day11() +  $scope.day12();
        };

    }
);

HTML Code

<!DOCTYPE html>
<html>
<head>
    <title>12 Days of Christmas</title>
    <link rel="stylesheet" href="css/bootstrap.min.css"/>
    <script src="angular.min.js"></script>
    <script src="12DaysApp.js"></script>
    <script src="christmasController.js"></script>
</head>
<body ng-app="christmasApp">
<b>12 Days of Christmas Calculator</b>
<div class="container" id="12Days" ng-controller="christmasController">
    <h2>12 Days of Christmas</h2>
    <div class="row">
        <div class="span6">Partridge: <input id="partridgeEle" ng-model="partridge" type="text"> Pear Tree: <input id="pearTreeEle" ng-model="pearTree" type="text"> </div>
        <div class="span2">Day 1: {{day1()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Two Turtle Doves: <input id="turtleEle" ng-model="turtleDove" type="text"> </div>
        <div class="span4">Day 2: {{day2()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Three French Hens: <input id="frenchHenEle" ng-model="frenchHen" type="text"> </div>
        <div class="span4">Day 3: {{day3()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Four Colly Birds: <input id="callingBirdElee" ng-model="callingBird" type="text"> </div>
        <div class="span4">Day 4: {{day4()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Five Gold Rings: <input id="goldRingEle" ng-model="goldRing" type="text"> </div>
        <div class="span4">Day 5: {{day5()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Six Geese a-laying: <input id="geeseEle" ng-model="geese" type="text"> </div>
        <div class="span4">Day 6: {{day6()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Seven Swans a swimming: <input id="swansEle" ng-model="swan" type="text"> </div>
        <div class="span4">Day 7: {{day7()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Eight Maids a Milking: <input id="milkingMaidElee" ng-model="milkingMaid" type="text"> </div>
        <div class="span4">Day 8: {{day8()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Nine Ladies Dancing: <input id="ladiesEle" ng-model="dancingLady" type="text"> </div>
        <div class="span4">Day 9: {{day9()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">10 Lords-a-Leaping: <input id="lordsEle" ng-model="lord" type="text"> </div>
        <div class="span4">Day 10: {{day10()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Eleven Pipers piping: <input id="pipersEle" ng-model="piper" type="text"> </div>
        <div class="span4">Day 11: {{day11()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Twelve Drummers Drumming: <input id="drummersEle" ng-model="drummer" type="text"> </div>
        <div class="span4">Day 12: {{day12()' currency:"$"}}</div>
    </div>
    <div class="row">
        <div class="span8">Total Cost of the twelve days:</div>
        <div class="span4">{{totalCostChristmas()' currency:"$"}}</div>
    </div>

</div>
</body>
</html>

This blog post originally appeared at Skyline Technologies


 

Fox Valley .NET User Group Presentation – Nov 2013

Thanks to everyone who came to my Fox Valley .NET User Group presentation last Wed, Nov 13, 2013 on AngularJS. There was a GREAT turnout! Close to 70 people showed up, which was great for a Wed night. See for yourself (and thus was early before the talk started up). It really shows the type of developers we have in Northeast Wisconsin, always learning more.

Crowd

The Fox Valley .NET User Group is expanding its scope and incorporating more technologies beyond just .NET, like Microsoft itself, who includes knockout and jQuery with Visual Studio for web development. There are a lot of tools and libraries for developers to look into and take advantage these days, way too many actuallyJ. As part of this expanding scope, the group will also be changing its name from the Fox Valley .NET User Group to something new. Currently, it may be the NorthEast Wisconsin Developer Users Group, though that may change.

As part of the expanding of scope, I thought I would embrace it and jump in with both feet. Instead of using Visual Studio, I did the entire presentation in WebStorm, a first for me. I also showed off the awesomeness of Microsoft's Azure Websites and their integration with GitHub by committing my changes and having them auto deploy during the talk. It worked pretty well, until I fell behind!

My slides are available on the blog at http://www.jptacek.com/2013/11/fvnug-presentation-nov-2013/AngularJS.pptx. All of the source code from my demo is available on my Github at https://github.com/jptacek/AngularJSFVNUG as is the PowerPoint slide deck. Finally, the Azure Website used during my presentation is available at http://loureed.azurewebsites.net/ . This link may or may not be available long term.

The group was a lot of fun. I tried my best to code up in real time with plenty of typos and many rescues from the audience. Great participation by the attendees. Lucky to be able to present and be part of the vibrant and engaged dev community in NorthEast Wisconsin. Hopefully they will have me back to talk again!


 

AngularJS – Introducing 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 third 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.

We have previously talked about AngularJS and controllers. However, an important part of web development is displaying data, especially repeating data. We are going to use our Periodic data and look at options for displaying that data on the page.

For this example, we have created a chemistry controller that contains information from the periodic table. An element in our JSON object looks like

 "atomicNumber": 1,
 "name": "Hydrogen",
 "atomicWeight": 1.00794,
 "phase": "Gas",
 "ionization": 13.5984,
 "melting": -259.15,
 "boiling": -252.87

Our chemistry controller essentially is responsible for setting a local scope variable.

function chemCtrl($scope) {

 $scope.periodic = {
 elements: [
 {
 "atomicNumber": 1,
 "name": "Hydrogen",
 "atomicWeight": 1.00794,
 "phase": "Gas",
 "ionization": 13.5984,
 "melting": -259.15,
 "boiling": -252.87
 },

....

 ]
 };
}

Our application now has a JavaScript variable on our Angular scope called periodic, which holds all of our JSON chemistry data. To display this on the page, we want to take advantage of the ng-repeat directive in AngularJS. The syntax for this is pretty simple. In our example, we will display all of the chemical element names from the periodic table in an unordered list.

We start our list with a ul tag. The next step is to apply the ng-repeat directive to the markup we want to repeat, in this case, theli tag, in which we will display the name of the element. The syntax is ng-repeat="element in periodic.elements". Essentially, ng-repeat is expecting an expression. In this instance, we are saying we want to loop through all items in our periodic data source, and we are going to call each item element, which will be a single item from our JSON object. Last, we need to output the data for display. In this instance then element's name, which is a property on our JSON object called name.

<ul>
  <li data-ng-repeat="element in periodic.elements">{{element.name}} </li>
</ul>
  • {{element.name}}

We could also go an easily update our markup to display the atomic weight along with the name by changing our li display to

<ul>
  <li data-ng-repeat="element in periodic.elements">{{element.name}} - {{element.atomicWeight}}</li>
</ul>

Resulting in a new look, where we are appending the atomic weight to the display of the element's name.

  • {{element.name}} - {{element.atomicWeight}}

Looking at our page though, we have a lot of data that we are displaying. Angular has the concept of filters, which can be applied to our expressions. An example of this is the limitTo filter. We can limit the number of items we display, in our scenario to 10. This is as simple as

    <li data-ng-repeat="element in periodic.elements|limitTo:10 ">

We now our displaying ten results on our page

  • {{element.name}} - {{element.atomicWeight}}

Angular can also quickly allow the data to be searched using the ng-model attribute. We create a text input box, and decorate it with the ng-model syntax to define a variable which is available on our scope, and then use that as an input to the filter.

First, we can create the input box

<input type="text" data-ng-model="elementName"/>

Next, we use that as the parameter for our filter instead of limitTo by using the filter keyword. The syntax for this is

<li data-ng-repeat="element in periodic.elements ' filter:elementName">

We can then type in an element name and the list will narrow down automatically. Give it a try!

  • {{element.name}}

You will notice that this does not REALLY work. What we want to be able to do is filter the name. For example, if you type 1, you will see results appear, for example Hydrogen. The reason for this is we are filtering on the WHOLE json object, so when we type 1 we are getting Hydrogen's Atomic number of 1 as a result and Lithium's atomic weight of 6.941, etc.

What we want to be able to do is filter on the JSON object property. I bring this up, because AngularJS, of course, has the ability to do this. This is accomplished by updating our Filter expression to identify the property of the JSON object we want to filter on. For example

<li data-ng-repeat='element in periodic.elements ' filter:{name:elementNameOnly}'>

Note, there appears to be a bug in the current version of AngularJS, 1.0.8, where you need to initialize the filter. Using the current release candidate, 1.2, resolves this issue. Thanks to my Skyline Technologies colleague Berny Zamora for helping me chase that down.

Now when we type 1, we no longer see results now.

  • {{element.name}}

I have created an Azure Website to host all of this code at http://angularperiodic.azurewebsites.net/

The code is also available 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+