AngularJS Part 4 - The Factory

Yngve Bakken Nilsen

This post is part of a series of posts on AngularJS, and here are the parts so far:


When working with AngularJS, creating controllers and using DI we are forced into a modular state of mind. It should trigger something in you wanting to write short and managable pieces of code. The more lines of code in your controller, the less maintainable it will become.

In our app, we've so far implemented a simple search that returns nothing, but simply outputs the search-term on the screen. I'd like to get some dummy data in our application, so that our search actually searches for something. So, before we get started with the factory, let's briefly look at another concept of AngularJS - the constant.

Constant

A constant in AngularJS is declared like this: angular.module('app').constant('myName', 'Yngve B. Nilsen').
Coming from other programming languages, the behaviour of the AngularJS-constant can be a bit confusing - you can actually change the value of an injected constant. Importantly, this change will only be valid within the scope you injected the constant, so all other services, controllers etc will still receive the original value when the constant is injected. So a constant is more of a descriptive name (except for a minor difference from .value() which we'll look at in a later post :))

Let's move on.
I need some data in my application, so I create a folder called TestData, in which I create a single file called People:

/
    /app/app.js
    /app/app.routes.js
    /app/Home/Home.js
    /app/Navbar/Navbar.html
    /app/TestData/People.js  * New
    /scripts/angular.js
    /views/main/index.cshtml

In this file I simply create an array with people that I've pulled from the brilliant site http://randomuser.me. I could put this array directly in my factory or controller, but that's dirty and wouldn't really help to make my code clean and neat. We'll use this as a pretend-API in a minute.

People.js

  
  
angular.module('app').constant('People', [  
   {
      "SSN": "907-96-4231",
      "cell": "(490)-273-7638",
      "dob": "66291076",
      "email": "tyler.rhodes83@example.com",
      "gender": "male",
      "location": {
          "city": "greensboro",
          "state": "kentucky",
          "street": "7928 crockett st",
          "zip": "53225"
      },
      "md5": "14eb34047098011d8cbcf805e93962c5",
      "name": {
          "first": "tyler",
          "last": "rhodes",
          "title": "mr"
      },
      "password": "hustler",
      "phone": "(394)-225-5666",
      "picture": "http://api.randomuser.me/0.3.2/portraits/men/23.jpg",
      "registered": "1164310881",
      "salt": "SmafJe]$",
      "sha1": "9d4011843335f6fab925a4478a56bf60448f9cbc",
      "sha256": "f410b5eb0e742823c6dc6926f1586e8d9d7ada6587218a051e607ba50f93daaf",
      "username": "organicmouse985"
  }, 
  { /* More users */ },
  ]);
  

The factory

We want to serve our controller with data from somewhere - somewhere being anything from an internal API, an external datasource or - like in this example - a static JSON-object. AngularJS actually provides us with three concepts we could use to solve this: provider, service and factory. In reality they're just abstractions of each other, so in the source of AngularJS the service is really just a wrapper for a provider, and the factory is a wrapper for a service. So in the end we're creating a provider whether we like it or not.

We'll skip the technical implementation-details in this post, but instead concentrate on the highest level of abstraction, namely the factory.

A factory is declared in a way that should be familiar by now:

angular.module('app').factory('MyFactory', [function(){ }]);

The above code would actually result in an error if we tried to inject MyFactory and do something with it. The reason is simply how a factory works. When injected, the factory will return whatever is returned by the function() { }. So basically we're talking about an Immediately-invoked function expression. Expanding on our code above we could do something like this:

  
  
angular.module('app').factory('MyFactory', ['$window', function($window){

    function sayHello(){
        $window.alert('Hello');
    }

    return {
        sayHello: sayHello
    }
}]);
  

Now, when MyFactory is injected, we could call myFactory.sayHello(), and it would display the alert on-screen.

Side-note: I'm injecting $window here instead of using window.alert directly. This is due to testability, which we'll cover in an upcoming post. Basically we want to be able to mock the alert-function, as it blocks the browser when running tests

Let's utilize this, and make a PeopleSearch factory that we can use to perform an actual search based on the term from our previous post. In order for our factory to have data, we can now pull a dependency on our People constant. We'll save the file as PeopleSearch.js in the Search-folder

angular.module('app').factory('PeopleSearch', ['People', function(people){  
    function search(term) {
        return _.filter(people, function (person) {
            return person.name.first.indexOf(term) > -1 ||
             person.name.last.indexOf(term) > -1
        });
    }

    return {
        search: search
    }
}]);

I've added a reference to Underscore.jsin order to perform the list-filtering. I've also decided to keep the _ on the Global Scope, and not pull it as a dependency, since chances are slim that I'll ever mock out Underscore in tests (which we'll start looking at soon)

We can now utilize the Underscore filter method to search for stuff in our People-array. In this case we simply look for a match either in the persons first or last name, and return that array.

Back in our Results-controller, we can now extend the logic a bit by doing an actual search:

angular.module('app').controller('Results',  
    ['$scope','$location','PeopleSearch', function($scope, $location, peopleSearch) {
        $scope.searchTerm = $location.search()['q'];
        $scope.results = peopleSearch.search($scope.searchTerm);
    }]
);
As you see, we inject the `PeopleSearch` factory, and simply pass the searchTerm to the search-function. The results are set to a value on our `$scope`. Let's implement a simple list in our markup to verify that we actually get some results:
<h4>{{results.length}} match(es)</h4>  
<ul>
    <li ng-repeat="person in results">
        {{person.name.last}}, {{person.name.first}}
    </li>
</ul>

Remember to add both people.js and PeopleSearch.js as references after app.js in the HTML for this to work!

Run the site, and - lo and behold - we have searchresults!

That should do it for this part of the series. This post is already pretty longwinded, but thanks for reading! :)

I'll be back with more AngularJS magic soon!


Complete code for this post on Github.com

Getting Started, javascript, Angularjs, services, constants, factory">
comments powered by Disqus