Angular Intro to Modules, Services, Factories, and Filtering

This entry is part 5 of 9 in the series: AngularJS Learning Series

The Assignment

This week’s self-imposed Angular assignment was to create a small app that would dynamically filter a list of data using an input field as search criteria. I chose to base the app on my son’s homework assignment and use United States states and capitals.

In my previous examples I used Controllers for all functions and assignments. For simple non-critical applications the configuration works well, but for larger real-world applications a more abstract approach makes sense. Enter Angular Modules.

The Basics

Modules initialize Angular applications and wire together its services, filters, directives, etc. This makes the code portable, easier to debug, and easier to unit test.

To keep things simple, I only used a service in this example. The service is a gofer, responsible for carrying out any requests made by the controller. The controller sets scope properties and behaviors and handles calls from the template.

Here’s a basic module, service, and controller in Angular:

angular.module('myApp.service',[]);
angular.module('myApp',['myApp.service']);
var StatesController = function($scope) {}

On the first line is a service named ‘myApp.service’. Note that the dot is not necessary, nor is using the app name as a prefix. I could have named it ‘gofer’ if I wanted. The important part is that I let the main application module know the name, which brings me to line 2, the main module.

The main module names the app and gives it a a comma delimited reference to services, filters, etc. that can be referenced. If I defined a filter named ‘myApp.filter’ my module definition would have been: “angular.module(‘myApp’,[‘myApp.service’,’myApp.filter’]);”. The module name, “myApp”, will be used in my ngApp directive in the template.

Line 3 is the shell of a controller definition which will be directly accessible by the template. While it wouldn’t do anything at this point, we could hook those three lines up to a template and assign myApp to the ngApp directive without error.

Let’s Get Some Data!

As with most applications, we’ll need access to data. My data source is a JSON file that looks like this:

[
    {"name":"Alabama", "capital":"Montgomery","abbreviation":"AL"},
    {"name":"Alaska", "capital":"Juneau","abbreviation":"AK"},
    {"name":"Arizona", "capital":"Phoenix","abbreviation":"AZ"},
    {"name":"Arkansas", "capital":"Little Rock","abbreviation":"AR"},
    ...
]

I need a way to create a resource object out of that data so I can interact with it. Angular’s ngResource module provides exactly what I need in its $resource service.

NOTE: The ngResource module is NOT in the base angular.js file. To use it you must load angular-resource.js in addition to angular.js. If you get an error in your debugging console that says “Uncaught Error: No module: ngResource”, chances are you forgot to load angular-resource.js. You do use a developer tools, right?

To retrieve the resource I utilized a method used to create objects in Angular called a factory. When I put the factory method inside my service, and then provide my Controller with a reference to it, I can use it in my controller to set a scoped property, like this:

angular.module('myApp.service',['ngResource']).
    factory('States', function($resource){
        return $resource('states.json', {},{
            get: {method: 'GET', isArray:true}
        });
    });

angular.module('myApp',['myApp.service']);

var StatesController = function($scope,States) {
    $scope.stateData = States.get();
};

So, what changed? First, I gave my service a reference to Angular’s ngResource module. That will give me the ability to use the $resource service for my factory allowing me to interact with my data source, “states.json”. Notice that the factory has a name, “States” which I provide to my Controller. From there I can access actions inside the factory, such as “get”, “save”, and “delete”.

Now, if I create a template referencing the controller I can display the data using my stateData property. Check out the source code here:
[iframe src=”http://embed.plnkr.co/zXGpgCkUl8cF5rzbIWYP” width=”100%” height=”300″]

Make Something Angulary Happen

We now have a working module, a service, a factory, a controller, and data. Time to do something slick with it. Building off the html above, Inside my StatesController DIV I’m going to delete the current {{stateData}} dump and replace it with something a little easier to read:

<ul class="states">
   <li>
      <div class="resultwrapper">
         <span class="title">{{state.name}}</span><br>
         <span class="bold">Abbreviation:</span> {{state.abbreviation}}<br>
         <span class="bold">Capital:</span> {{state.capital}}
      </div>
   </li>
</ul>

Using ng-repeat and an unordered list, my state information now displays in a style-able list. Unfortunately, a couple of my states are not in alphabetical order. This can be fixed easily by telling the repeater to order it using a built-in filter module named orderBy after my iterator:

<ul class="states">
   <li ng-repeat="state in stateData | filter:criteria | orderBy:'name'">
       <div class="resultwrapper">
          <span class="title">{{state.name}}</span><br>
          <span class="bold">Abbreviation:</span> {{state.abbreviation}}<br>
          <span class="bold">Capital:</span> {{state.capital}}
       </div>
   </li>
</ul>

Note that ‘name’ is a string, not a reference. I could have also defined a property for it in the controller and used it with the orderBy.

Now that they’re in alphabetical order I want to be able to search them. This was done by adding a simple input and assigning it an ng-model of ‘critera’, then giving that to the ng-repeat filter. Angular uses the value entered into the criteria field as a filter for the stateData array. Throw in a style sheet and you have a pretty nifty widget using a very minimal amount of code.

The Result

[iframe src=”http://embed.plnkr.co/BixOgwEu66Hzco6rZJqt” width=”100%” height=”500″]

Responsive Form Validation with AngularJS and HTML5

This entry is part 6 of 9 in the series: AngularJS Learning Series

This AngularJS example demonstrates how HTML5 and AngularJS can work together to provide a modern approach to form submission and validation without lengthy JavaScript validation algorithms, cumbersome CSS, or page refreshes.

Note that there are multiple ways to present and submit a form using HTML5 and AngularJS. Your implementation should depend on the needs of your application. The method I use here does not perform an actual submit. Instead, when the Submit button is clicked it calls a function that updates a visitorInfo object. Any action can be performed when the visitorInfo object is updated by modifying the update function in VisitorFormController in app.js.

The example provides immediate feedback based on the data entered into the input fields. Every input field has been flagged as “required” and you can watch the debugging info to see the status change dynamically as each input field is populated.

[iajsfiddle fiddle=”CUzbs” height=”400px” width=”100%” show=”result,html,js,css,resources” skin=”default”]

Let’s take a closer look at the email section to see how this is done:

<span class="label">Email:</span>
<input type="email" name="email" placeholder="Email" required="" />
<span class="valid">?</span>

The first line is simply the label that is show before the input field. The “label” class lets me apply some styling to keep it on the same line as the input field, make it bold, etc. No magic there.

The input field has several attributes. Name gives us a means of referring to that specific field. Type defaults to “text” but we’ve given it the HTML5 type of “email”. Doing so implements validation rules, such as requiring an & and a domain type (like .COM). It also causes responsive mobile devices to present different UI. For example, assigning a type of email will cause “.COM” to appear on keyboards of many mobile devices.

The ngModel directive links the input field to a scope property, in this case visitorInfo.email. visitorInfo is an object that holds all of the form properties. Notice that each input field model includes visitorInfo before the property name. You can watch visitorInfo populate in the debugging info as you fill in the form.

Placeholder is a new input attribute that allows you to define a short data entry hint that will appear inside the input field. The hint disappears when the user starts entering text but will reappear if the text is deleted. Placeholder text does not get submitted as form data.

Finally, there is the required attribute that defines the field as one that must be filled in for the form to be considered valid.

The span below the input field contains a checkmark that is not visible when the page first loads. The AngularJS ngShow directive has an evaluation in it that will only allow the span to be visible when the field passes email field passes validation rules.

Negative feedback is provided courtesy of CSS and AngularJS. Enter a value in the First Name field and then delete it. The field background will change to red. If you look in style.css you’ll see the following section:

input.ng-invalid.ng-dirty {
	background-color:red;
}

ng-invalid and ng-dirty automatically get assigned as classes based on the input field state. If the field contents are invalid then the input field will be dynamically assigned the ng-invalid class. We can use that class to style the field in certain circumstances as I did above. Pretty slick! See the AngularJS API for more information on these class assignments.

Finally, check out the Submit and Restore buttons. The Submit button’s job is to update the visitorInfo object. The restore button restores all of the field data to the last submitted value.

Both buttons react to the state of the form. The submit button will enable and turn green when the entire form has been filled out with valid information. The restore button enables when the form data differs from the stored visitorInfo object data.

This functionality is done using a combination of AngularJS’s ngClick and ngDisabled directives:

<button ng-click="reset()" ng-disabled="isUnchanged(visitorInfo)">RESTORE</button>
<button class="submitbtn" ng-click="update(visitorInfo)" ng-disabled="valForm.$invalid || isUnchanged(visitorInfo)">SUBMIT</button>

ngDisabled contains an evaluation. If the evaluation is true then ngDisabled will also be set to true, making the buttons un-clickable. If they evaluate to false then the buttons will become enabled. Once they are enable, they can be clicked and the clicks will be handled by the functions that are referenced in the ngClick directives.

All in all it was very easy. The code itself is MUCH shorter than this post, which is a testament to the simplicity of AngularJS and HTML5 (or it could be just an indication that I’m too wordy.) In either case, I’m using that line as an opportunity to wrap this post up.

As always, check out the code on Plunkr. Fork it, edit it, experiment with it. It’s the best way to learn.