Page 4 of 6

Angular Cats! Part 5 – Custom Directive

One more thing…

I took the liberty of making a custom directive out of the Twitter Bootstrap Carousel widget. You’ll see it in partials/catDeatil.html as . I’m not 100% certain I’m doing things the ‘correct angular way’, but it seems to work.

Demo | Code

The catCarousel.html partial:


<div id="myCarousel" class="carousel slide">
 
  <div class="carousel-inner">
   
    <div ng-class="{item:true, active:$first}" ng-repeat="photo in pics">
      <img ng-src="{{photo}}">
    div>
  div>
 
  <a class="carousel-control left" href="#myCarousel" data-slide="prev">&lsaquo;a>
  <a class="carousel-control right" href="#myCarousel" data-slide="next">&rsaquo;a>
div>

The directive code:

The templateURL property specifies the HTML to insert into the directive tag. I’ve restricted the type of directive to an element name. It’s just easy that way. One caveat though, it that special considerations need to be made for this to be IE7/8 compatable.

The link function will take the value of the ‘pics’ attribute in the catcarousel HTML tag and assign it to the directive scope. Whenever the ‘cat’ object changes – either by clicking Next/Prev, or selecting a cat from the grid – the directive will update the ‘pics’ and call the carousel() function on the root element of the directive template.

angular.module('myDirectives', [])
.directive('catcarousel', function(){
  return {
    templateUrl: 'partials/catCarousel.html',
    restrict: 'EAC',
    link: function (scope,element,attr) {
      scope.$watch('cat', function () {
        scope.pics = scope.$eval(attr.pics);
        $(element).carousel({
          interval: 0
        });
      });
    }
  }
});

And that just about wraps it up. There’s plenty more code in the cat apps than outlined in these blog posts, so if anyone is interested, please take a look at the project on GitHub. I tried to add as many useful comments as I could. If any Angular experts run across this and are horrified by any of the code, please let me know! Thanks. Bye.

Angular Cats! Part 4 (Refactored) – Deep Linking, More Better Services

Demo | Code

During the creation of Angular Cats!, it didn’t take me long to realize that I was straying from best practices and the intentions of Angular’s creators. I dug myself a bit of technical debt in the name of expediency, and have now come to pay the piper. Over the past few days I’ve been refactoring major portions of the app, and wound up with almost an entirely new codebase. Large sections are still intact (e.g. the translator service), and functionality is basically the same, but a few key differences are present.

The largest change by far is the ability to deep link. Every cat has its own URL, and can be bookmarked.

To get the deep-link ball rolling, the following code was added to app.js:

.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/cats/:page', {templateUrl: 'partials/catList.html', controller: CatListController});
    $routeProvider.when('/cat/:catId/:page', {templateUrl: 'partials/catDetail.html', controller: CatDetailController});
    $routeProvider.otherwise({redirectTo: '/cats/1'});
}])

The routes allow a user to go to a specific ‘page’ of cats in the cat list, or navigate to the details of any given cat – provided you know its ID. I also store the page number in the URL of the cat detail page so the user can navigate back to the page previously viewed when clicking ‘Back’ on the cat detail page.

In order to navigate around the application, the controllers now use the $location module, and will change the path according to the behavior of the user. The code below shows how the handler for clicking a cat to view details has changed.

Before (use events to change state):

// CatListController
$scope.getDetail = function() {
  eventBroadcast.broadcast('catClicked',{cat:this.cat});
};

//MainController
$scope.$on('catClicked',function() {
  switchView();
});
...
function switchView() {
  $scope.viewList = !$scope.viewList;
  $scope.viewDetail = !$scope.viewDetail;
}

After (use location to change state):

// CatListController
$scope.getDetail = function (catId) {
  $location.path('/cat/' + catId + "/" + $routeParams.page);
};

The CatsService module was completely overhauled to include a getCat() method in addition to getCats(), along with a few other useful methods. Oh, and this version actually has the ability to contact the live Petfinder.com API! Using a config constant, the app can switch between grabbing cat data from a .json file on disk, or reaching out to the live Petfinder API (via a PHP proxy on my own server). While building the app, I usually relied on a static json file for data, but it’s nice to be able to contact a live service to return fresh data.

The returned data is also stored in the CatService module. This data is basically the catCollection property that I previously stuck in $rootScope. Storing application data and state information on $rootScope just didn’t seem right, so I created some properties and accessor methods (get/set) on CatService, and used that instead. Worked out quite nicely.

Below is the new CatService module. Notice that it’s quite a bit larger that the previous module. This time around, the service had a few private variables and functions that the service uses for itself, and the CatService object is returned by the factory and injected into the controllers. The properties of the CatService object are exposed to the controllers and can be used wherever CatService is injected.

The first major feature of this updated service is the remoteSvc resource. Instead of just grabbing a .json file from disk, it will contact the proxy.php service and grab live data returned from the Petfinder API. The query object passed into the $resource function demonstrates how to override default $resource methods. This really isn’t necessary in my case, but I did it just for kicks.

The line var service = (ENDPOINT === "LOCAL") ? localSvc : remoteSvc; will look at the ‘ENDPOINT’ constant set in the application config (see app.js), and determine whether to use the local json file, or server resource to gather the cat data. It’s a quick and dirty approach, and in a larger system it would be better to abstract this into a proper config file, with two separate service modules, but it works pretty well for a small app with one or two services.

The CatService object has some getters and setters for accessing the selected cat, and the selected cat’s index value in the cat collection array. And, of course, there are the getCats and getCat methods, which gather the necessary data for the list and detail views (respectively).

angular.module('catService', ['ngResource','CatServiceHelper'])
.factory('CatsService', function($resource,$filter,$routeParams,CatServiceHelper,ENDPOINT){
    /**
     * Private vars
     */

    // Set the max number of cats to retrieve from the Petfinder API
    var numberOfCatsToGet = 200;

    // Set up the $resource injectable to use the Petfinder API. Some custom options are used in the $resource.query method
    var remoteSvc = $resource('/jsCats/ngCats/proxy.php', {count:numberOfCatsToGet,offset:0},
                { query: {
                      method:'GET',
                      params:{action:'shelter.getPets',count:numberOfCatsToGet, offset:0},
                      isArray:true
                     }
                });

    // Use the cats.json file as the $resource, if necessary
    var localSvc = $resource('cats.json',{});

    // Decide which $resource to use as the actual service
    var service = (ENDPOINT === "LOCAL") ? localSvc : remoteSvc;

    // Private properties & methods used to store data shared between controllers
    var _cats = [];
    var _activeCatIndex = -1;
    var _setActiveCat = function _setActiveCat(catId,callback) {
                          var cat = CatsService.findCatInCollection(catId);
                          if (cat) {
                            _activeCatIndex = _cats.indexOf(cat);
                            callback(cat);
                          }
                        };

    /**
     * CatService Object returned by the factory function.
     * Contains instance methods that can be used by the controllers.
     */

    var CatsService = {
     
      /**
       * ### Accessor Methods
       */
     
     
      // Controllers can read the cat collection, but cannot change it without calling a service method.
      getCatCollection : function getCatCollection() {
        return _cats;
      },

      getActiveCatIndex : function getActiveCatIndex() {
        return _activeCatIndex;
      },

      setActiveCatIndex : function setActiveCatIndex(idx) {
        if (angular.isNumber(idx) && idx <= _cats.length) {
          _activeCatIndex = idx;
        } else {
          _activeCatIndex = -1;
          throw("activeCatIndex must be a number, and cannot exceed the length of the cat collection.");
        }
      },

      /**
       * ### Service Methods
       */


      // This method retrieves all cat data from the service and stores it in _cats
      getCats : function getCats(callback){
        var self = this;
        service.query(function (data) {
          var rawCatData = [];
          if ( data && data.length > 0 && data[1].petfinder.pets.pet && data[1].petfinder.pets.pet.length > 0 ) {
            rawCatData = data[1].petfinder.pets.pet;
          }
          angular.forEach(rawCatData,function(item){
            _cats.push( CatServiceHelper.translateCat(item) );
          });
          if (angular.isFunction(callback)) callback(_cats);
        });
      },

      // This method retrieves data for one cat.  It will also call getCats if the user
      // navigates directly to a catDetail page. This is necessary for the Back and
      // Next/Prev buttons to work correctly.
      getCat : function(catId, callback) {
        var self = this;
        if( _cats.length ) {
          _setActiveCat(catId,callback);
        } else {
          this.getCats(function (results) {
              _setActiveCat(catId,callback);
          });
        }
      },

      // This uses an AngularJS filter function to extract a cat from the cat collection by the cat's id
      findCatInCollection : function findCatInCollection(catId) {
        var cat = $filter('filter')(_cats, function (item) {
                return item.id.toString() === catId;
              });
        return cat[0] || 0;
      },

      // Use the default $resource.query method
      query : service.query,
     
      // Use the default $resource.get method
      get : service.get
   
    };

    // The factory function returns CatsService, which is injected into controllers.
    return CatsService;
});

Angular Cats! Part 3 – Communicating with $broadcast

Continued from: Angular Cats! Part 2
Full Source Code: GitHub: jsCats/ngCatsHOM

In part 2 of this post, the source code in the event handlers within each controller was removed. That’s because there’s some interesting stuff going on that wouldn’t have made sense without first knowing about one last module within the application: eventBroadcast.

To communicate clicks and cats between controllers, I found a dead simple solution that cobbles together an event dispatcher-esque system. When ‘eventBroadcast’ is injected into a controller, it will have access to its member variables – in this case, an event name, and a message. The eventBroadcast object can also broadcast events from the $rootScope, which will then trickle down the scope hierarchy and can be listened for by each controllers $scope. So it goes something like this:

  1. User clicks a cat thumbnail
  2. $scope.getDetail is called.
  3. The clicked cat object is attached to eventBroadcast.message.
  4. An event is broadcast from $rootScope with the name ‘catClicked’
  5. CatDetailController’s $scope responds to ‘catClicked’
  6. eventBroadcast.message is checked for data.

Here is the ngClick handler in CatListController:

function CatListController($scope,$rootScope,$routeParams,CatsService,$location,eventBroadcast) {

  $scope.getDetail = function() {
    // When a thumbnail is clicked, a 'catClicked' event is broadcast,
    // and the data for the clicked cat is attached to eventBroadcaster
    eventBroadcast.broadcast('catClicked',{cat:this.cat});
  };

...
}

Here is the actual eventBroadcast module:

.factory('eventBroadcast', function($rootScope) {
    // eventBroadcaster is the object created by the factory method.
    var eventBroadcaster = {};

    // The message is a string or object to carry data with the event.
    eventBroadcaster.message = '';

    // The event name is a string used to define event types.
    eventBroadcaster.eventName = '';

    // This method is called from within a controller to define an event and attach data to the eventBroadcaster object.
    eventBroadcaster.broadcast = function(evName, msg) {
        this.message = msg;
        this.eventName = evName;
        this.broadcastItem();
    };

    // This method broadcasts an event with the specified name.
    eventBroadcaster.broadcastItem = function() {
        $rootScope.$broadcast(this.eventName);
    };

    return eventBroadcaster; })

And the handler for ‘catClicked’ in CatDetailController:

function CatDetailController($scope,$rootScope,eventBroadcast) {

  // When 'catClicked' is broadcast, we know that new cat data needs to be loaded from eventBroadcast
  $scope.$on('catClicked',function() {
    showCat(eventBroadcast.message.cat);
  });

  ...

  var showCat = function(cat) {
    $scope.cat = cat;
    $scope.catIndex = $rootScope.catCollection.indexOf(cat);
  }

}

And there you have it! A rudamentary technique for communicating between controllers. However, after reading , I learned that there are some flaws with this method. Thomas Burleson created a more robust pub/sub module that acts as a message queue, which is more scalable, flexible, and doesn’t rely so much on $rootScope. A demo of the Messaging Service module is on jsFiddle, and the code can be download from his site. I hope to try this out very soon.

Angular Cats! Part 2 – Views (partials) and Controllers

Continued from: Angular Cats! Part 1
Full Source Code: GitHub: jsCats/ngCatsHOM

Gathering a pile of data from an API (or in my case, a raw json file) makes for a pretty poor web application. In order to present the data in a fun and interactive interface, I created a couple HTML templates in the /partials folder, and inserted references to those templates into the MainController section of index.html.

In order to show or hide each partial view, I created two boolean values within the MainController’s scope. The values are named ‘viewList’ and ‘viewDetail’ and setting them to true or false will show or hide each one (respectively). The HTML templates themselves are inserted with the ngInclude directive. Using ngInclude probably isn’t the best option as it can lead to spaghetti code, have adverse affects on page performance, and probably some other stuff I can’t think of. Creating custom AngularJS directives are the best option for reusable components and/or widgets. I’ll eventually get to that, but to keep things simple for now, using ngInclude will work.

<div class="row" ng-controller="MainController">
    <div ng-show="viewList">
      <div ng-include src="'partials/catList.html'"></div>
    </div>
    <div ng-show="viewDetail">
      <div ng-include src="'partials/catDetail.html'"></div>
    </div>
</div>

The MainController has a simple function called switchView that flips the boolean values of viewList and viewDetail. I don’t have a situation where both should be true or false, but I could if I wanted. If you look at the MainController function, there are two event handlers that do nothing but call switchView().

function MainController($scope, $rootScope, eventBroadcast) {

  $scope.$on('catClicked',function() {
    switchView();
  });

  $scope.$on('backClicked',function() {
    switchView();
  });

  function switchView() {
    $scope.viewList = !$scope.viewList;
    $scope.viewDetail = !$scope.viewDetail;
  }

  // Starting values
  $scope.viewList = true;
  $scope.viewDetail = false;
}
// Explicitly inject stuff. This is optional unless you plan on minifying the code.
MainController.$inject = ['$scope','$rootScope','eventBroadcast'];

Upon loading the app, the user is greeted with a nice big grid full of lovely little cat pictures. This grid is actually an unordered list of thumbnails from the cat data. All of the viewable elements on the page are contained within the CatListController section – denoted by the ngController directive. There are a number of directives doing different things here: The ngClick directive handles mouseclicks and calls a click-handler function; The ngMouseover directive works similarly; ngRepeat will iterate through the contents of the ‘cats’ array and create a list-item for each cat; and ngSrc will display the thumbnail in conjunction with the img tag.

<div ng-controller='CatListController'>
  <div class="row span12 catGrid">
   
    <ul class="thumbnails">
      <li ng-repeat="cat in cats" ng-click="getDetail(cat.id)" class="span2" ng-mouseover="showName(cat.name)">
        <div class="thumbnail">
         
          <img ng-src="{{cat.thumbnail}}">
        </div>
      </li>
    </ul>
  </div>

 
  <div class="row">
    <button class='btn btn-primary span1' ng-click="changePage('prev')">Prev</button>
    <div class="catName span10">{{name}}</div>
    <button class='btn btn-primary pull-right span1' ng-click="changePage('next')">Next</button>
  </div>
</div>

That’s all fine and dandy, but it’s not going to do anything unless we actually have a CatListController. The CatListController will need to define the properties and methods within its $scope, and handle any setup or logic for the listView page. $scope methods will define the behaviors, such as responding to clicks, and will store data used by directives (e.g. catName). Also within CatListController is the call to CatService.getCats(). This retrieves all the translated cat data and stores it on the $rootScope. Storing data on the $rootScope is sort of like creating a global variable – not the best practice, but useful in a pinch. And it’s only ‘global’ within the scope of the AngularJS application, not the global Javascript namespace.

The code below is truncated. For the full source (with comments), take a look on GitHub.

function CatListController($scope,$rootScope,$routeParams,CatsService,$location,eventBroadcast) {

  $scope.getDetail = function() {
    // When a thumbnail is clicked, show the detail
  };

  $scope.goToPage = function goToPage(page) {
    // If there are more cats than can fit in the grid, more pages are needed.  
    // This function go to a specific page.
  };

  $scope.changePage = function(pagingAction) {
   // This handles the Next/Prev buttons to cycle through pages.
  };

  // When a user hovers over a cat, it's name is displayed below the grid.
  $scope.showName = function(catName) {
    $scope.name = catName;
  }

  // If the catCollection is not yet defined, fetch the data, otherwise go to page one.
  if( !$rootScope.catCollection ) {
    // This uses the CatService defined in services.js to retrieve the list of cats.
    CatsService.getCats(function (data) {
      // The cat list returned from the getCats method is loaded onto $rootScope
      // so it can be easily shared between controllers.
      $rootScope.catCollection = data
      $scope.goToPage(1);
    });
  }
CatListController.$inject = ['$scope','$rootScope', '$routeParams', 'CatsService', '$location','eventBroadcast'];

The catDetail template is pretty much the same story. There are a bunch of $scope variables to store data to display within the template, and some click handler functions for buttons. A unique element that appears on this page is ng-carousel, which is a directive that activates Twitter Bootstrap’s carousel gallery component for a series of pictures. Normally, the Bootstrap carousel is activated by the ‘carousel slide’ class. However, after building the view, I noticed that sometimes the slider buttons would just stop working. It was very intermittent, and I think Angular refreshing the DOM on $scope changes would somehow break the carousel. To fix this, I created a directive that would refresh the carousel component every time the $scope.cat variable changed.

<div ng-controller='CatDetailController' class="catDetail">


<div class="row">
    <div class="span1">
      <button class="back btn btn-primary" ng-click="goBack()">Back</button>
    </div>
    <div class="span11">
      <h2 class="pull-right">{{cat.name}}: {{cat.size}} {{cat.age}} {{cat.breed}}</h2>
    </div>
</div>     


<div class="row catDetailPane">
    <div class="span5">
   
        <div ng-carousel id="myCarousel" class="carousel slide">
         
          <div class="carousel-inner">
       
            <div ng-class="{item:true, active:$first}" ng-repeat="photo in cat.pics">
                <img ng-src="{{photo}}">
            </div>
          </div>
         
          <a class="carousel-control left" href="#myCarousel" data-slide="prev"></a>
          <a class="carousel-control right" href="#myCarousel" data-slide="next"></a>
        </div>
    </div>

 
    <div class="span7">
        <div class="well" ng-bind-html-unsafe="cat.description">
        </div>
    </div>
</div>


<div class="row">
  <button ng-click='newCat(catIndex - 1)' class="btn btn-primary span2">Prev Cat</button>
  <div class="span8 catOptions">{{cat.options}}</div>
  <button ng-click='newCat(catIndex + 1)' class="btn btn-primary pull-right span2">Next Cat</button>
</div>

</div>

Oh yeah, did you see that ng-bind-html-unsafe directive? That’s because the description text has a bunch of craptacular MS Word-to-HTML junk sprinkled throughout, and that particular directive handles it quite nicely.

The controller here is pretty straightforward – handle clicks, show a cat.

function CatDetailController($scope,$rootScope,eventBroadcast) {

  $scope.$on('catClicked',function() {
    // This is an event handler for a click on a cat thumbnail.
  });
 
  $scope.goBack = function () {
    // Back button clicked! Handle it!
  };

  // This method is called when 'Next Cat' or 'Prev Cat' is clicked.  
  // It will cycle through each cat in catCollection.
  $scope.newCat = function(idx) {
    // This method is called when 'Next Cat' or 'Prev Cat' is clicked.  
    // It will cycle through each cat in catCollection.
  }

  var showCat = function(cat) {
   // This method takes a cat object as a parameter and sets it as the active cat.
   // It also finds the index of the cat object in catCollection.
  }

}
CatDetailController.$inject = ['$scope','$rootScope','eventBroadcast'];

And here’s the directive to fix the Bootstrap carousel problem:

.directive('ngCarousel', function() {
  return function (scope, elm, attr) {
        scope.$watch('cat', function() {
          $(elm).carousel('pause');
        });
    }
});

Last but not least is a technique to give the controllers the ability to communicate with each other. But that will be a separate post, because this one is too long already.

Angular Cats! Part 1 – The Data Service

View the App | Get the Code

In order to broaden my horizons as a developer of web applications, I’ve been taking a good hard look at some of the popular Javascript MVC frameworks of the modern era.  I started by looking at backbone.js and was horribly confused, and was subsequently delighted when I investigated AngularJS.  The ‘two-way declarative bindings’ remind me a lot of Flex, and it felt very natural jumping into development with Angular.  In order to stretch my brain a bit, and really dig into the framework, I decided to start simple and convert an existing Flex project into an Angular app.  A perfect candidate for this is the House of Mews cat browser.  It’s a simple little widget that pulls pet data from the Petfinder.com API, displays a grid of thumbnails, and allows a user to browse details of adoptable pets.

Try it out!

To get started, I grabbed the angular-seed project from GitHub.  This project includes the latest version of Angular (1.0.2 as of this writing), as well as some tools to run a stand-alone server using node.js, and a unit-test runner.  All I really paid attention to was the contents of the ‘app’ folder. It has a nice directory structure, some starter files, and the library itself.

After clearing out all the cruft, I started with the app.js file in /app/js/. Using a single statement, I defined the main module for my app.

angular.module('catApp', [])

Then it’s a simple as adding the ngApp directive to the root of the index.html document. Oh yeah, I also added Twitter Bootstrap to the project as well. The grid system is just super, and I wanted to use the carousel they provide.


<html lang="en" ng-app="catApp">
<head>
  <meta charset="utf-8">
  <title>AngularJS Cats App</title>
  <link rel="stylesheet" href="css/bootstrap.min.css"/>
  <link rel="stylesheet" href="css/app.css"/>
</head>
<body>
  stuff goes here...
</body>
</html>

Before going any further with HTML and partial templates and such, I like to make sure I can grab the data first. I created a service module to gather all the cat data. Because the cat data is only updated once per week, I simply download a data file using the Petfinder API, and load the data directly from my site. This prevents hundereds (thousands?) of unnecessary calls to the Petfinder API, since the data rarely changes. I actually have a PHP script that runs every week to update the local data file.

The angular service module is really a factory that does two things: Uses Angular’s $resource object to load data from cats.json, and create a CatService object to be used throughout the application. For now, this object only has one method: getCats(). Rather than just pass raw data from cats.json around the application, getCats() will first transform all the raw data into a nice collection of sensible Cat objects. To see what I mean, observe the raw data for a single cat below:

"pet": {
    "options": {
        "option": [
            {
                "$t": "altered"
            },
            {
                "$t": "noKids"
            }
        ]
    },
    "breeds": {
        "breed": [
            {
                "$t": "Tortoiseshell"
            },
            {
                "$t": "Domestic Long Hair"
            }
        ]
    },
    "shelterPetId": {},
    "status": {
        "$t": "A"
    },
    "name": {
        "$t": "HARLOW"
    },
    "contact": {
        "email": {},
        "zip": {
            "$t": "38104"
        },
        "city": {},
        "fax": {},
        "address1": {
            "$t": "933 S. Cooper"
        },
        "phone": {},
        "state": {
            "$t": "TN"
        },
        "address2": {
            "$t": "Website:  www.houseofmews.com"
        }
    },
    "description": {
        "$t": "
I'm the House of Mews superstar in disguise, Harlow!! As you can see I am a beautiful long-haired, tortoiseshell girl and I know it! I'm actually a little shy, but I know I'm gorgeous, so I just can't help strutting my stuff everywhere I go. I guess hearing everyone tell me how beautiful I am has even made me a little more outgoing. They don't even mind my crossed eyes (not that it bothers me). In fact, most people tell me they're just part of my charm! I like to play with toys on sticks. I need a little patience to get over my shyness, but I'll be well worth it in the end. Please don't overlook me just because I am a little shy. Come on, haven't you ever dreamed of living with a supermodel?? Here's your chance! Approx. DOB is 5-20-00.
"

    },
    "sex": {
        "$t": "F"
    },
    "age": {
        "$t": "Senior"
    },
    "size": {
        "$t": "S"
    },
    "mix": {
        "$t": "yes"
    },
    "shelterId": {
        "$t": "TN198"
    },
    "lastUpdate": {
        "$t": "2011-01-12T20:07:07Z"
    },
    "media": {
        "photos": {
            "photo": [
                {
                    "@size": "x",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-x.jpg",
                    "@id": "1"
                },
                {
                    "@size": "fpm",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-fpm.jpg",
                    "@id": "1"
                },
                {
                    "@size": "pn",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-pn.jpg",
                    "@id": "1"
                },
                {
                    "@size": "pnt",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-pnt.jpg",
                    "@id": "1"
                },
                {
                    "@size": "t",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-t.jpg",
                    "@id": "1"
                },
                {
                    "@size": "x",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-2-x.jpg",
                    "@id": "2"
                },
                {
                    "@size": "fpm",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-2-fpm.jpg",
                    "@id": "2"
                },
                {
                    "@size": "pn",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-2-pn.jpg",
                    "@id": "2"
                },
                {
                    "@size": "pnt",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-2-pnt.jpg",
                    "@id": "2"
                },
                {
                    "@size": "t",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-2-t.jpg",
                    "@id": "2"
                },
                {
                    "@size": "x",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-3-x.jpg",
                    "@id": "3"
                },
                {
                    "@size": "fpm",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-3-fpm.jpg",
                    "@id": "3"
                },
                {
                    "@size": "pn",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-3-pn.jpg",
                    "@id": "3"
                },
                {
                    "@size": "pnt",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-3-pnt.jpg",
                    "@id": "3"
                },
                {
                    "@size": "t",
                    "$t": "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-3-t.jpg",
                    "@id": "3"
                }
            ]
        }
    },
    "id": {
        "$t": "4169367"
    },
    "animal": {
        "$t": "Cat"
    }
}

Now look at this:

cat: {
age: "Senior",
breed: "Tortoiseshell Domestic Long Hair ",
description: "
I'm the House of Mews superstar in disguise, Harlow!! As you can see I am a beautiful long-haired, tortoiseshell girl and I know it! I'm actually a little shy, but I know I'm gorgeous, so I just can't help strutting my stuff everywhere I go. I guess hearing everyone tell me how beautiful I am has even made me a little more outgoing. They don't even mind my crossed eyes (not that it bothers me). In fact, most people tell me they're just part of my charm! I like to play with toys on sticks. I need a little patience to get over my shyness, but I'll be well worth it in the end. Please don't overlook me just because I am a little shy. Come on, haven't you ever dreamed of living with a supermodel?? Here's your chance! Approx. DOB is 5-20-00.
"
,
id: 4169367,
name: "HARLOW",
options: "Spayed/Neutered, No Kids",
pics: [
    0: "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-x.jpg",
    1: "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-2-x.jpg",
    2: "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-3-x.jpg" ]
sex: "Female",
size: "Small",
thumbnail: "http://photos.petfinder.com/photos/US/TN/TN198/4169367/TN198.4169367-1-fpm.jpg" }

Much better, no? It’s a lot easier to work with these tidier objects in the controllers and templates, plus, some validation and manipulation makes it easier to handle cases where data is missing.

Here’s the barebones service module. Too see the full source code (including the ServiceHelper module which does all the data translation), take a look at the project on GitHub.

angular.module('catService', ['ngResource','CatServiceHelper'])
.factory('CatsService', function($resource,$filter,CatServiceHelper){

    // Define the remote service using Angular's $resource module.
    var service = $resource('cats.json',{});

    // CatsService is the object created by the CatsService factory.
    // This is a bit long-winded and could easily be shortened,
    // but I plan on adding some additional methods to CatService in the future.
    var CatsService = {
      // The getCats function calls $resource.query() to retrieve the remote data.
      // The data is then lightly validated and scrubbed using the translateCat()
      // function in the CatServiceHelper module
      getCats : function getCats(callback){
        service.query(function (data) {
          var cats = [];
          var catCollection = [];
          if ( data && data.length > 1 && data[2].pets.pet && data[2].pets.pet.length > 0 ) {
            cats = data[2].pets.pet;
          }
          angular.forEach(cats,function(item){
            catCollection.push( CatServiceHelper.translateCat(item) );
          });
          callback(catCollection);
        });
      }
    };
    return CatsService;
});

So that’s a good start. The application won’t really do anything, but it’s primed and ready to fetch lots of cat data!

AngularJS: ng-repeat, ng-hover, ng-click

So I’ve been diving into AngularJS lately, and am finding that it’s pretty awesome.  It feels more comfortable than backbone.js – most likely due to my Flex background.  I like the way ‘directives’ act similarly to Flex components, and two-way binding just works automagically.  Anyway, I’m converting my House of Mews Petfinder widget from Flex to Angular, and will post lots of code from that later.  But for now, here’s a tiny demo I threw together in jsFiddle. You can hover and click on list items, and stuff happens! (OK, so my real motivation for posting this was to test jsFiddle embedding. It works, and it’s sweet.)

profile for eterps at Stack Overflow, Q&A for professional and enthusiast programmersI’m a big fan of Stack Overflow and use it on nearly a daily basis for reference.  Once in a while I’ll post or answer a question. Recently, an answer of mine regarding responsive design in the context of Twitter Bootstrap has gotten a lot of attention.   Just for kicks, I’m cleaning up the conversation and posting the meat and potatoes here.

Here are some jsFiddles to demonstrate the difference between fluid only and fluid + responsive.

Fluid-only: http://jsfiddle.net/5JECu/

Fluid-responsive: http://jsfiddle.net/ZnEEa/

Fixed-only: http://jsfiddle.net/eterpstra/ZR4zz/3/

Fixed-responsive: http://jsfiddle.net/eterpstra/rdvaG/12/

In the fixed width layout, the columns change when the browser window reaches a width defined in a media query. So when you have your window greater than 960px wide, it will stay at it’s maximum width. Then when you shrink your browser to 959px, it will snap to a new layout based on a media query that has a maximum width of 768px. So because you are viewing a fixed-width layout, the columns will not change when your browser width is between 768 and 960. Wen you are viewing a fluid-width layout, the column sizes will always change to match your browser’s width. The layout itself will also change in accordance to the media queries, as with a fixed-width layout.

When you decide between fixed width and fluid width you need to think in terms of your ENTIRE page. Generally, you want to pick one or the other, but not both.  In other words, the Scoaffolding page is using a fixed-width layout. The  and  on the Scaffolding page are not meant to be examples, but rather the documentation for implementing fixed and fluid width layouts.  The proper fixed width example is . The proper fluid width example is .

When observing Twitter’s , you should not see the content changing sizes when your browser is greater than 960px wide. This is the maximum (fixed) width of the page. Media queries in a fixed-width design will designate the minimum widths for particular styles. You will see this in action when you shrink your browser window and see the layout snap to a different size. Conversely, the fluid-width layout will always stretch to fit your browser window, no matter how wide it gets. The media queries indicate when the styles change, but the width of containers are always a percentage of your browser window (rather than a fixed number of pixels).

The ‘responsive’ media queries are all ready to go. You just need to decide if you want to use a fixed width or fluid width layout for your page.

When creating rows, be sure to use 

row

 with a fixed width layout, and 

row-fluid

 with a fluid width. Do not mix and match (unless you have a very good reason to do this).

I can’t tell what the difference is between twitter bootstrap fluid responsive vs non-responsive though… (asked by original poster)

Open the fluid example and maximize your window. Observe how the navigation menu is a horizontal list. Now slowly reduce the width of your browser. At some point, the navigation bar will change so that a button appears, and when the button is clicked, the menu appears as vertical. So with fluid, the only thing that changes is the width of the elements. With a responsive design, you can change any css rule based on the width of the browser. See my edited answer for some jsFiddle examples.

 

Two koans and an Euler walk into a (foo) bar…

In order to suck less at Javascript, I went searching for some more practice problems in the same vein as the js-assessment project I completed a couple months ago.  Luckily, there is no shortage of instructional material on Javascript these days, and two enterprising individuals have taken it upon themselves to construct sets of Javascript koans.

The koan projects are set up as a series of failing unit tests – much like the js-assessment project.  The object of the activity is to open the test file, then fill in the blanks so all the tests pass.   I couldn’t decide which set of koans would be most beneficial, so I did both.  Practice makes perfect, right?

Javascript Koans Complete 2I tackled the set from mrdavidlaing first.  There are fewer test files, and it seemed to me at the time that the concepts were a bit more advanced.  I was right. The koans got right into fun ideas such as protypal inheritance, functional scope, and advanced array functions like map and reduce.  The inspiration for these koans are from Douglas Crockford‘s, Javscript: The Good Parts.  These koans are a nice set of exercises to complete if you are  coming to javascript from another programming language, or are a beginning javascript developer, but already know the basic programming concepts.

The koans by liammclennan were more numerous, but covered roughly the same topics as the other set, plus a few more of the basics.  This is definitely the way to go to brush up on javascript, or try out javascript as a beginner.  I didn’t get as tripped up with these exercises as I did with Mr. Laing’s, but perhaps it was because I did them second that I felt they were easier.  Either way, they were nice exercises and very worthwhile.

Finally, I really decided to test my mettle by signing into Project Euler and make an attempt at the first few problems in the ever-expanding set.  I’d heard a lot about this site, but never really looked into it until now. I thought it would be a nice, fun set of exercises intended to help beginners learn about programming concepts.

Nope!

Project Euler SolutionsProject Euler is for computer science and math geeks bent on stretching their brains to the limit.  The difficulty curve here is very nearly a vertical line placed millimeters from your nose.  Problem 1 is basically a fizzbuzz variant, and a warmup (tease, perhaps).  After that, you will be humbled.  What I thought would be a nice afternoon activity turned into a two week affair.  Basic concepts like looping and conditional statements just won’t cut it – unless you feel like waiting several hours for an answer to compute.  I had to turn to Google to learn more than I ever wanted to know about the Seive of Erastosthenes and Pathagorean triples.  But even if I didn’t figure out they mathy parts myself, I still managed to craft unique (to me) solutions in javascript based on the pseudocode or general algorithms discovered by others.  After reaching problem 10, though, I’m done. For now.

My GitHub Repos:

Fork of mrdavidliang’s koans

Fork of leammclennan’s koans

Project Euler answers (executed via Jasmine)

A simple example of the Javascript ‘Call’ function

I was asked about function.call() the other day during a discussion with a co-worker, and came up with this example off the top of my head.  There are many, many uses of call().  In addition to accepting an object as a parameter to replace the called function’s this, it also accepts an arbitrary number of arguments that will be passed into the called function.  Better examples can be found in the Mozilla Developer Network JavaScript Reference.

// A constructor function for a Dog
var Dog = function(name){
this.name = name;
};

Dog.prototype.bark = function bark(){console.log(this.name + " barks!");};

// Fred is a Dog.
// Fred can bark, because all Dogs can bark.
var fredDog = new Dog("Fred");
fredDog.bark(); // => "Fred barks!"

// A constructor function for a Cat
var Cat = function(name){ this.name = name; };

// Bill is a Cat
var billCat = new Cat("Bill");

// Normally, Cats cannot bark,
// but Bill is an exception and has learned how to bark like Fred
fredDog.bark.call(billCat); // => "Bill barks!"

When bark() is invoked with the call() function, billCat takes the place of this within the bark function of Dog, so this.name is a reference to billCat.name instead of fredDog.name.

Code HTML5 in the Cloud with StackMob, Cloud9 and GitHub

In my recent efforts to learn more about modern Javascript I’ve been looking around at different Backend-as-a-Service (BaaS) companies.  They presumably would provide a dead simple server option for whatever I’m working on so I can focus all my attention on the front-end.  I signed up for a number of different accounts to explore the feature set and documentation, and settled on StackMob due to the perceived ease of use, and the fact that their Javascript SDK is built on backbone.js (another current interest of mine).  After creating (and deleting) some dummy apps to get a feel for things, I eventually got a very simple, but usable, database put together.  What is interesting about StackMob is that they will host your front-end app, as well as provide the back-end.  Not only that, but you deploy to your front-end development server directly from GitHub.  It’s a pretty slick system.  And as soon as I realized that GitHub projects can be edited via the Cloud9 IDE, I had a nice little dev environment fully hosted in the cloud.

I’m not saying this combination is at all practical for serious use, but it could be useful in a pinch, or when monkeying around with some prototypes while using multiple computers.  Plus Cloud9 is just fun to look at, and this provided a good excuse to use it for real once in a while.  Oh, and it’s all free (as in beer).

To get started, sign up for a Stackmob account, and a GitHub account.  Both sites have amazing documentation for getting up-and-running on their respective services.  GitHub has very detailed instructions for setting up your account with a git client on Linux, OS X, and Windows.  If you haven’t already, follow those instructions, but do not yet create a repository.

Getting started in StackMob is just as easy.  Sign up for an account, then create an app.  Every app generated by StackMob starts with a Users table, which is enough for now.  Grab the Javascript SDK from the Get your SDK page, and follow the setup instructions in the Setup your JS SDK Dev Environment section.  Step 2 is optional if you just want to edit your app in the Cloud9 IDE.  Otherwise make sure you have Ruby installed and give it a try.  In Step 3, you will be instructed to create a GitHub repository. Do that.  While you are logged into GitHub, also set up automatic fetching, so your code will get deployed to your development server every time you push to GitHub.  The Hosting Your HTML5 App… page has all the necessary instructions. Make sure you have committed and pushed some files (likely the StackMob SDK) to your GitHub repository.

By this point, you can edit files locally, push the files to GitHub, and see your changes reflected online.  To start editing online, go to c9.io and log in using your GitHub account.  When you reach your dashboard, you should see a big green button that says Create New Project in the left-hand sidebar, as well as a section in the sidebar labeled Projects on GitHub.  The URL for your dashboard is http://c9.io/yourgithubusername.  If you cannot see your GitHub project, click the refresh button in the far lower left corner of the screen.  Select your StackMob GitHub project from the sidebar menu, and click Clone to Edit.  In the modal window, select Shared Development Server and click Checkout. It will take a minute, but your project should be created and listed under My Projects. Select your project and click Start Editing.  You should be taken to the editor window where you can open and edit your project files.  Take a few minutes to admire the lovely UI and its many features, and edit a file or two.

To test out your edits, open the console window (Menu: Windows > Console ) and run your usual git commands – e.g. git commit -a -m “msg” and git push. The Cloud9 git client will push your changes to your GitHub master branch, and those changes will then propogate almost immediately to your hosted StackMob application.

This is creating a web application in the cloud.  Development-in-the-cloud is not quite cut out for daily use, but if you really need to try out some code or get in a quick change from any computer (or tablet, or phone), this is a nifty way to do it.

 

« Older posts Newer posts »

© 2017 Eric Terpstra

Theme by Anders NorénUp ↑