AngularJS Tutorial: A Beginner’s Guide to AngularJS


Codever Logo

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github ⭐🙏


Introduction

Since I first started using AngularJS several years ago, it has become the most popular single-page application (SPA) framework for the JavaScript language. AngularJS makes it easy to solve simple problems fast, yet has enough features to enable development teams to build complex large-scale applications.

This tutorial aims to introduce a complete beginner to AngularJS by explaining fundamental concepts and building a sample application. The application will be a simplified admin area for a multi-author blog.

History is important

I believe a limited appreciation of history is important. Often in software, good ideas are a twist or improvement on something that has been done before, made possible by some other technology improvement(s). In the case of AngularJS, the vast performance improvements made to browser JavaScript engines has made ambitious SPA frameworks possible.

AngularJS is an adaptation of the Model-View-Controller (MVC) pattern first created in 1979. MVC helps developers to separate the concerns involved in building user interfaces. Jeff Atwood of Coding Horror wrote of an example that we’re all very familiar with, a static webpage:

  • HTML acting as the model, the content (data) for the page
  • CSS acting as the view, how that content should be presented
  • The browser acting as the controller, combining the model and the view to render the page
  • MV*

    Subtle variations to the MVC pattern have come in recent years, none more so than by JavaScript SPA frameworks. For this reason, the MV* phrase has been coined as a way of describing ‘whatever’ variation works for you.

    So although it’s definitely worth understanding the origins of MVC, keep in mind that AngularJS is MV* and does not implement MVC strictly.

    Setup

    This tutorial will use the stable version of AngularJS at the time of writing (1.4.4). It also intends to only include the bare minimum required to run an AngularJS application.

    To get up and running, download an uncompressed copy of AngularJS from angularjs.org. Create a file structure as follows with your favorite text editor:

    initial-file-layoutInitial file layout

    Edit the contents of index.html as follows:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body>
    
        <script src="angular.js"></script>
      </body>
    </html>

    Note that in the above snippet we’re serving angular.js from the local file system. This saves us from having to introduce any additional moving parts and is adequate for our purposes. Open index.html in your favorite browser and, although you’ll see nothing but a white screen, check to make sure that you don’t have any errors in your browser’s developer tools/console.

    We will add additional .js files as we progress through the tutorial.

    Directives

    Considering the fact that I discussed MVC in the introduction, it would be very tempting to structure this tutorial by the AngularJS implementation of models, views and controllers. But there is a construct so fundamental to AngularJS that to not start with it would be an error.

    That construct is directives. AngularJS describes itself as “HTML enhanced for web apps.” Directives are what facilitate this enhancement; they extend the capability of HTML elements.

    I often find analogies to be a useful tool for explaining abstract concepts, such as directives. Consider a homeowner who is having some building work completed on their house. The team of laborers required to complete any non-trivial building project is varied in role and might include:

    • a bricklayer
    • an outfitter
    • a plumber

    The types of capability directives extend HTML elements with can be categorized similarly:

    • structural
    • decorative
    • plumbing

    If we keep the analogy going a moment longer, HTML is the house and directives are the team of laborers.

    Previously I introduced a sample application that we will build during this tutorial. Let’s build upon that by displaying an article title contained within a plain JavaScript object:

    var article = {
      title: "Learn AngularJS"
    };

    Edit index.html to contain the ng-app, ng-controller and ng-bind directives as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
        <div ng-bind="title"></div>
        <div ng-bind="getTitle()"></div>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    You’ll notice that two .js files have appeared. There is a small amount of JavaScript required to make the article title appear in the <div>s and app.js and controllers.js is where it happens. Before we create them, I want to briefly discuss the three directives introduced in the above snippet.

    ng-app

    ng-app is a plumbing directive. AngularJS looks for which HTML element has the ng-app directive attached to it and uses it to bootstrap an application. ng-app can be placed on any HTML element, not just <html>, and connects a module (in our case, ‘udemyAdmin’) to it. Modules will be covered shortly.

    ng-controller

    ng-controller is a plumbing directive. It connects a model and any related behaviors to a branch of the HTML tree made available by the usage of ng-app.

    ng-bind

    ng-bind is a plumbing directive. It connects a model property or the result of a model behavior to the text content of an HTML element. In our example, the title property and getTitle behaviour is available because ng-bind has been used on a <div> which is a child of <body> (where ng-controller has been used).

    Edit the file structure to match the following:

    second-file-layoutNew file layout

    Edit the contents of app.js as follows:

    angular.module('udemyAdmin', []);

    In the above snippet, we first create a module called ‘udemyAdmin’. This module is created with no dependencies (denoted by the empty array). Modules allow us to partition different parts of an application anytime we wish; this is useful in larger applications. For example, a full Udemy blog application might be partitioned as follows:

    angular.module('udemy', ['udemy.courses', 'udemy.authors', 'udemy.students']);
    angular.module('udemy.courses', ['udemy.courses.archived', 'udemy.courses.current']);
    // and so on

    Edit the contents of controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
      var title = "Learn AngularJS";
    
      $scope.title = title;
      $scope.getTitle = function() {
        return title;
      };
    });

    In the above snippet, we first retrieve the ‘udemyAdmin’ module (angular.module is dual operation based on the number of arguments) and then register a controller within it. $scope is a core AngularJS component available for injection to controllers (we cover injection in a later section of this tutorial). $scope is the model exposed by articleCtrl; we add properties and behaviors we want to be available for use in our HTML.

    Although app.js and controllers.js are very small at the moment, they will grow as this tutorial progresses and it is useful to refer to snippets of code by file name.

    Bindings

    In the previous section of this tutorial, we introduced ng-bind as a way to connect controller models to the text content of HTML elements. There is an alternative, non-directive way of achieving this, namely double curly bracket notation or . To use it in index.html we would edit the <div>s as follows:

    <div></div>
    <div></div>

     

    Typically `` is used over ng-bind as it is less verbose (4 characters versus 10).

    ng-bind does have one distinct advantage, though. It prevents any temporary flashing of curly braces, which can happen if there is a gap between the browser rendering HTML and AngularJS executing. The ng-cloak directive is an alternative solution, but we won’t go into details in this tutorial.

    Expressions

    Both and ng-bind require an AngularJS expression. AngularsJS expressions are similar in concept to JavaScript expressions but with subtle differences that I’ll highlight with examples.

    We could change the expressions used in index.html (currently title and getTitle()) to any of the following:

    • title + 1 – expressions can have operators. Here we are operating on a string, so 1 is converted to “1” (as per normal JavaScript) resulting in “Learn AngularJS1”.
    • title + ': ' + description – expressions are forgiving. description doesn’t exist, but the result is “Learn AngularJS: ” rather than “Learn AngularJS: undefined” as you might expect.
    • title + (description ? ': ' + description : '') – conditionals are allowed but only in ternary form, so the result here is just “Learn AngularJS”.

    Curly brackets are not allowed in AngularJS expressions, which means that blocks (e.g., if () { }) and function declarations (e.g., function() { }) are ruled out.

    The recommendation is to have a model behavior instead of complex expressions. In general, it’s a good idea to keep your expressions looking attractive.

    Two-way bindings

    The connections made between controller models and HTML elements are more commonly known as ‘bindings’. All bindings in AngularJS are two-way. This means that any updates to bound model properties are automatically reflected in the HTML. Even at this early stage, our sample application has two-way bindings. It just doesn’t look like it because there is no binding that will update the model. Let’s change that now so that we can properly highlight two-way binding.

    Edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
        <div ng-bind="title"></div>
        <div></div>
        <input type="text" ng-model="title" />
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    In the above snippet, we’ve introduced an HTML text input element and attached an ng-model directive to it. ng-model listens for DOM events (e.g., keyup) raised by the element, reads its value and writes it to the bound model property specified in the expression (i.e., title). The text content of the <div>s updates thanks to the binding created by ng-bind and . Try this out and witness the magic of two-way binding.

    Directives again

    In the first directives section, I introduced an analogy (a team of laborers completing some building works) and used it to begin categorizing directives as one of three types:

    • structural
    • decorative
    • plumbing

    Unfortunately, I then proceeded to only introduce plumbing directives (ng-app, ng-controller and ng-bind). Let’s fix that now by returning to directives and discussing ng-repeat.

    ng-repeat

    Most applications built with AngularJS are likely to be data-driven; reading, creating, updating and deleting data. Real-world data is often plural in nature – for example:

    • walking into your local foreign exchange provider to buy currency for your next holiday. You’ll likely be presented a board of rates for the most popular currency pairings.
    • visiting your local bookstore and browsing your favorite niche. You’ll likely be presented with a range of choices for a book to leaf through.

    ng-repeat is a structural directive because it modifies the “bricks and mortar” of our HTML. At a simple level, it creates the HTML element it is attached to multiple times based on the contents of a model collection property, e.g., an array.

    Lets see how ng-repeat can help us display an array of articles. Edit index.html to the following:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        <div ng-repeat="article in articles" ng-bind="article.title"></div>
    
        <ul>
          <li ng-repeat="article in articles">
            <p></p>
          </li>
        </ul>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    In the snippet above, article in articles is much like a traditional JavaScript for…in loop, with article being the name of the variable assigned to on each iteration of articles. article is then available for use in other directives and bindings, either on that HTML element or a descendant (the snippet shows both).

    Edit controllers.js so that an appropriate model property is available as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
      $scope.articles = [
        { title: "Learn AngularJS" },
        { title: "JavaScript closures explained!" }
      ];
    });

    All being well, you’ll see the same two article titles displayed twice – inside <div> and <p> elements, respectively.

    ng-repeat is a very powerful directive. We’ve used it to simply create an HTML element per item in a static array. It is more likely that you’ll use ng-repeat with dynamic collections – for example, live feeds of buy and sell prices for foreign exchange currency pairings. ng-repeat will track changes in dynamic collections and, in most cases, behave as you would expect, creating, editing and destroying HTML elements appropriately.

    limitTo filter

    We can further demonstrate the power of ng-repeat by combining it with a filter. Filters could demand a section of this tutorial to themselves, but in general they are applied to expressions, using the | operator, and format the result.

    AngularJS comes with a handful of built-in filters, including limitTo. limitTo will format the result of the expression we have used with ng-repeat by returning a specified number of articles from an optional starting index.

    Edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        <div ng-repeat="article in articles | limitTo:1 " ng-bind="article.title"></div>
    
        <ul>
          <li ng-repeat="article in articles | limitTo:1:1">
            <p></p>
          </li>
        </ul>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    In the above snippet, both ng-repeat expressions include a contrived usage of the limitTo filter (we only have two articles thus far, so limiting to one seems pretty pointless).

    The first expression is limited to one article, but we haven’t specified a starting index, so 0 is used. This results in HTML of <div>Learn AngularJS</div>.

    The second expression is also limited to one article, but this time we have specified a starting index. This results in HTML of <p>JavaScript closures explained!</p>.

    Custom directives

    AngularJS comes with a handful of structural directives, such as ng-include for fetching and displaying external HTML fragments, but ng-repeat is the most significant.

    This is beyond the scope of this tutorial, but it is possible to create your own custom directives of any type (plumbing, structural or decorative). To whet your appetite for what might lie ahead in your own AngularJS journey, check out the directives in the excellent Angular Material library or my own angular-charts library.

    You will probably note that we still haven’t discussed decorative directives. That will come later in one final directives section.

    Injection

    In the first directives section, we mentioned injection, stating that we would cover it in a later section. Injection might get a little complicated, but I’ll do my best to keep it concise and understandable.

    Minimal theory

    Dependency injection is a common software design pattern that is frequently used in any non-trivial application. By writing an AngularJS application, you will use dependency injection, even if you’re not aware of it. As such, it makes sense to learn a little of the theory behind the pattern.

    The essence of the pattern is to separate the responsibilities of construction and usage of dependencies. In this tutorial, we have already seen an example of this, as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
      $scope.title = "Learn AngularJS";
    });

    In this snippet, articleCtrl is dependent on $scope in order to make model properties and behaviors available for binding. We do not know how $scope is constructed by AngularJS (nor do we really care), as long as we can use it within articleCtrl.

    If we were to construct $scope ourselves, in a very simplified form it might look something like the following:

    angular.module('udemyAdmin').controller('articleCtrl', function() {
      var $scope = new angular.Scope();
      $scope.title = "Learn AngularJS";
    });

    In this snippet, the responsibility for the construction and use of the ``<div id="toc_container" class="no_bullets">

    Contents

    </div>

    Introduction

    Since I first started using AngularJS several years ago, it has become the most popular single-page application (SPA) framework for the JavaScript language. AngularJS makes it easy to solve simple problems fast, yet has enough features to enable development teams to build complex large-scale applications.

    This tutorial aims to introduce a complete beginner to AngularJS by explaining fundamental concepts and building a sample application. The application will be a simplified admin area for a multi-author blog.

    History is important

    I believe a limited appreciation of history is important. Often in software, good ideas are a twist or improvement on something that has been done before, made possible by some other technology improvement(s). In the case of AngularJS, the vast performance improvements made to browser JavaScript engines has made ambitious SPA frameworks possible.

    AngularJS is an adaptation of the Model-View-Controller (MVC) pattern first created in 1979. MVC helps developers to separate the concerns involved in building user interfaces. Jeff Atwood of Coding Horror wrote of an example that we’re all very familiar with, a static webpage:

  • HTML acting as the model, the content (data) for the page
  • CSS acting as the view, how that content should be presented
  • The browser acting as the controller, combining the model and the view to render the page
  • MV*

    Subtle variations to the MVC pattern have come in recent years, none more so than by JavaScript SPA frameworks. For this reason, the MV* phrase has been coined as a way of describing ‘whatever’ variation works for you.

    So although it’s definitely worth understanding the origins of MVC, keep in mind that AngularJS is MV* and does not implement MVC strictly.

    Setup

    This tutorial will use the stable version of AngularJS at the time of writing (1.4.4). It also intends to only include the bare minimum required to run an AngularJS application.

    To get up and running, download an uncompressed copy of AngularJS from angularjs.org. Create a file structure as follows with your favorite text editor:

    initial-file-layoutInitial file layout

    Edit the contents of index.html as follows:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body>
    
        <script src="angular.js"></script>
      </body>
    </html>

    Note that in the above snippet we’re serving angular.js from the local file system. This saves us from having to introduce any additional moving parts and is adequate for our purposes. Open index.html in your favorite browser and, although you’ll see nothing but a white screen, check to make sure that you don’t have any errors in your browser’s developer tools/console.

    We will add additional .js files as we progress through the tutorial.

    Directives

    Considering the fact that I discussed MVC in the introduction, it would be very tempting to structure this tutorial by the AngularJS implementation of models, views and controllers. But there is a construct so fundamental to AngularJS that to not start with it would be an error.

    That construct is directives. AngularJS describes itself as “HTML enhanced for web apps.” Directives are what facilitate this enhancement; they extend the capability of HTML elements.

    I often find analogies to be a useful tool for explaining abstract concepts, such as directives. Consider a homeowner who is having some building work completed on their house. The team of laborers required to complete any non-trivial building project is varied in role and might include:

    • a bricklayer
    • an outfitter
    • a plumber

    The types of capability directives extend HTML elements with can be categorized similarly:

    • structural
    • decorative
    • plumbing

    If we keep the analogy going a moment longer, HTML is the house and directives are the team of laborers.

    Previously I introduced a sample application that we will build during this tutorial. Let’s build upon that by displaying an article title contained within a plain JavaScript object:

    var article = {
      title: "Learn AngularJS"
    };

    Edit index.html to contain the ng-app, ng-controller and ng-bind directives as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
        <div ng-bind="title"></div>
        <div ng-bind="getTitle()"></div>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    You’ll notice that two .js files have appeared. There is a small amount of JavaScript required to make the article title appear in the <div>s and app.js and controllers.js is where it happens. Before we create them, I want to briefly discuss the three directives introduced in the above snippet.

    ng-app

    ng-app is a plumbing directive. AngularJS looks for which HTML element has the ng-app directive attached to it and uses it to bootstrap an application. ng-app can be placed on any HTML element, not just <html>, and connects a module (in our case, ‘udemyAdmin’) to it. Modules will be covered shortly.

    ng-controller

    ng-controller is a plumbing directive. It connects a model and any related behaviors to a branch of the HTML tree made available by the usage of ng-app.

    ng-bind

    ng-bind is a plumbing directive. It connects a model property or the result of a model behavior to the text content of an HTML element. In our example, the title property and getTitle behaviour is available because ng-bind has been used on a <div> which is a child of <body> (where ng-controller has been used).

    Edit the file structure to match the following:

    second-file-layoutNew file layout

    Edit the contents of app.js as follows:

    angular.module('udemyAdmin', []);

    In the above snippet, we first create a module called ‘udemyAdmin’. This module is created with no dependencies (denoted by the empty array). Modules allow us to partition different parts of an application anytime we wish; this is useful in larger applications. For example, a full Udemy blog application might be partitioned as follows:

    angular.module('udemy', ['udemy.courses', 'udemy.authors', 'udemy.students']);
    angular.module('udemy.courses', ['udemy.courses.archived', 'udemy.courses.current']);
    // and so on

    Edit the contents of controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
      var title = "Learn AngularJS";
    
      $scope.title = title;
      $scope.getTitle = function() {
        return title;
      };
    });

    In the above snippet, we first retrieve the ‘udemyAdmin’ module (angular.module is dual operation based on the number of arguments) and then register a controller within it. $scope is a core AngularJS component available for injection to controllers (we cover injection in a later section of this tutorial). $scope is the model exposed by articleCtrl; we add properties and behaviors we want to be available for use in our HTML.

    Although app.js and controllers.js are very small at the moment, they will grow as this tutorial progresses and it is useful to refer to snippets of code by file name.

    Bindings

    In the previous section of this tutorial, we introduced ng-bind as a way to connect controller models to the text content of HTML elements. There is an alternative, non-directive way of achieving this, namely double curly bracket notation or . To use it in index.html we would edit the <div>s as follows:

    <div></div>
    <div></div>

     

    Typically `` is used over ng-bind as it is less verbose (4 characters versus 10).

    ng-bind does have one distinct advantage, though. It prevents any temporary flashing of curly braces, which can happen if there is a gap between the browser rendering HTML and AngularJS executing. The ng-cloak directive is an alternative solution, but we won’t go into details in this tutorial.

    Expressions

    Both and ng-bind require an AngularJS expression. AngularsJS expressions are similar in concept to JavaScript expressions but with subtle differences that I’ll highlight with examples.

    We could change the expressions used in index.html (currently title and getTitle()) to any of the following:

    • title + 1 – expressions can have operators. Here we are operating on a string, so 1 is converted to “1” (as per normal JavaScript) resulting in “Learn AngularJS1”.
    • title + ': ' + description – expressions are forgiving. description doesn’t exist, but the result is “Learn AngularJS: ” rather than “Learn AngularJS: undefined” as you might expect.
    • title + (description ? ': ' + description : '') – conditionals are allowed but only in ternary form, so the result here is just “Learn AngularJS”.

    Curly brackets are not allowed in AngularJS expressions, which means that blocks (e.g., if () { }) and function declarations (e.g., function() { }) are ruled out.

    The recommendation is to have a model behavior instead of complex expressions. In general, it’s a good idea to keep your expressions looking attractive.

    Two-way bindings

    The connections made between controller models and HTML elements are more commonly known as ‘bindings’. All bindings in AngularJS are two-way. This means that any updates to bound model properties are automatically reflected in the HTML. Even at this early stage, our sample application has two-way bindings. It just doesn’t look like it because there is no binding that will update the model. Let’s change that now so that we can properly highlight two-way binding.

    Edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
        <div ng-bind="title"></div>
        <div></div>
        <input type="text" ng-model="title" />
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    In the above snippet, we’ve introduced an HTML text input element and attached an ng-model directive to it. ng-model listens for DOM events (e.g., keyup) raised by the element, reads its value and writes it to the bound model property specified in the expression (i.e., title). The text content of the <div>s updates thanks to the binding created by ng-bind and . Try this out and witness the magic of two-way binding.

    Directives again

    In the first directives section, I introduced an analogy (a team of laborers completing some building works) and used it to begin categorizing directives as one of three types:

    • structural
    • decorative
    • plumbing

    Unfortunately, I then proceeded to only introduce plumbing directives (ng-app, ng-controller and ng-bind). Let’s fix that now by returning to directives and discussing ng-repeat.

    ng-repeat

    Most applications built with AngularJS are likely to be data-driven; reading, creating, updating and deleting data. Real-world data is often plural in nature – for example:

    • walking into your local foreign exchange provider to buy currency for your next holiday. You’ll likely be presented a board of rates for the most popular currency pairings.
    • visiting your local bookstore and browsing your favorite niche. You’ll likely be presented with a range of choices for a book to leaf through.

    ng-repeat is a structural directive because it modifies the “bricks and mortar” of our HTML. At a simple level, it creates the HTML element it is attached to multiple times based on the contents of a model collection property, e.g., an array.

    Lets see how ng-repeat can help us display an array of articles. Edit index.html to the following:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        <div ng-repeat="article in articles" ng-bind="article.title"></div>
    
        <ul>
          <li ng-repeat="article in articles">
            <p></p>
          </li>
        </ul>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    In the snippet above, article in articles is much like a traditional JavaScript for…in loop, with article being the name of the variable assigned to on each iteration of articles. article is then available for use in other directives and bindings, either on that HTML element or a descendant (the snippet shows both).

    Edit controllers.js so that an appropriate model property is available as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
      $scope.articles = [
        { title: "Learn AngularJS" },
        { title: "JavaScript closures explained!" }
      ];
    });

    All being well, you’ll see the same two article titles displayed twice – inside <div> and <p> elements, respectively.

    ng-repeat is a very powerful directive. We’ve used it to simply create an HTML element per item in a static array. It is more likely that you’ll use ng-repeat with dynamic collections – for example, live feeds of buy and sell prices for foreign exchange currency pairings. ng-repeat will track changes in dynamic collections and, in most cases, behave as you would expect, creating, editing and destroying HTML elements appropriately.

    limitTo filter

    We can further demonstrate the power of ng-repeat by combining it with a filter. Filters could demand a section of this tutorial to themselves, but in general they are applied to expressions, using the | operator, and format the result.

    AngularJS comes with a handful of built-in filters, including limitTo. limitTo will format the result of the expression we have used with ng-repeat by returning a specified number of articles from an optional starting index.

    Edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        <div ng-repeat="article in articles | limitTo:1 " ng-bind="article.title"></div>
    
        <ul>
          <li ng-repeat="article in articles | limitTo:1:1">
            <p></p>
          </li>
        </ul>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
      </body>
    </html>

    In the above snippet, both ng-repeat expressions include a contrived usage of the limitTo filter (we only have two articles thus far, so limiting to one seems pretty pointless).

    The first expression is limited to one article, but we haven’t specified a starting index, so 0 is used. This results in HTML of <div>Learn AngularJS</div>.

    The second expression is also limited to one article, but this time we have specified a starting index. This results in HTML of <p>JavaScript closures explained!</p>.

    Custom directives

    AngularJS comes with a handful of structural directives, such as ng-include for fetching and displaying external HTML fragments, but ng-repeat is the most significant.

    This is beyond the scope of this tutorial, but it is possible to create your own custom directives of any type (plumbing, structural or decorative). To whet your appetite for what might lie ahead in your own AngularJS journey, check out the directives in the excellent Angular Material library or my own angular-charts library.

    You will probably note that we still haven’t discussed decorative directives. That will come later in one final directives section.

    Injection

    In the first directives section, we mentioned injection, stating that we would cover it in a later section. Injection might get a little complicated, but I’ll do my best to keep it concise and understandable.

    Minimal theory

    Dependency injection is a common software design pattern that is frequently used in any non-trivial application. By writing an AngularJS application, you will use dependency injection, even if you’re not aware of it. As such, it makes sense to learn a little of the theory behind the pattern.

    The essence of the pattern is to separate the responsibilities of construction and usage of dependencies. In this tutorial, we have already seen an example of this, as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
      $scope.title = "Learn AngularJS";
    });

    In this snippet, articleCtrl is dependent on $scope in order to make model properties and behaviors available for binding. We do not know how $scope is constructed by AngularJS (nor do we really care), as long as we can use it within articleCtrl.

    If we were to construct $scope ourselves, in a very simplified form it might look something like the following:

    angular.module('udemyAdmin').controller('articleCtrl', function() {
      var $scope = new angular.Scope();
      $scope.title = "Learn AngularJS";
    });

    In this snippet, the responsibility for the construction and use of the`` dependency is solely with articleCtrl. A principle of good software design is for a component, such as articleCtrl, to do one thing and to do it well.

    Dependency annotations

    AngularJS needs assistance to know what to inject into the callbacks we provide to certain functions, such as controller(). In the examples we’ve seen so far, AngularJS uses the argument name, so if we were to make a typo such as £scope AngularJS wouldn’t know what to inject.

    Although typos are possible, they’re fairly quick to spot (via errors in your browser’s developer tools/console) and fix. A more significant problem is associated with the minification of scripts comprising our AngularJS applications. We’ve all seen the output of most minifers: unintelligble single-character variable names everywhere! Minifers will rename $scope, for example, and AngularJS won’t get any injection assistance.

    To be able to use minifers, we can use dependency annotations as follows:

    angular.module('udemyAdmin').controller('articleCtrl', ['$scope', function($scope) {
      $scope.title = "Learn AngularJS";
    }]);

    In the above snippet, we’re now passing an array as the second parameter to controller(). The last item in the array needs to be our original callback, while the first items are the string names of the dependencies to be injected. A rudimentary minifier might output the above snippet as follows:

    a.m('udemyAdmin').c('articleCtrl', ['$scope', function(s) {
      s.t = "Learn AngularJS";
    }]);

    Strings can’t be renamed, so AngularJS can use dependency annotations to get the injection assistance it needs. There’s a small caveat to this approach: the array item order and callback argument order must be in sync. Additionally, we’re still prone to errors from typos, but overall the benefit of minification far outweighs these caveats in any larger application.

    However, we won’t use dependency annotations in this tutorial, preferring instead to keep snippets lean and focused.

    Custom injectables

    AngularJS offers four techniques for registering our own injectable components, much like $scope, as follows:

    • constant
    • value
    • factory
    • service

    Injectable components are known as services in AngularJS speak, so it’s a shame that they decided to call one of the techniques the same.

    We will discuss the more common techniques that I have used myself and have seen used by others, namely value and factory. Constant and service are less common and more complicated in use; as such, I consider them beyond the scope of this tutorial.

    Value services

    A value service allows us to register injectable values (be it strings, numbers, objects, arrays or functions). They allow us to define a value that can’t be changed, much like a constant in other programming languauges. The fact that there is also a constant service is another unfortunate naming choice by AngularJS. The difference between the two is associated with the configuration life cycle of an AngularJS application, rather than anything to do with the value registered.

    A value service allows us to register a value once and use it many times (via injection). Consider the limitTo filter from the “Directives again” section of this tutorial. We could introduce a pageSize value service, inject it into articleCtrl and use it as the default number of articles to display. pageSize could also be reused in other areas of the application, such as administering a subset of a large number of categories.

    Edit the file structure to match the following:

    third-file-layoutNew file layout

    Edit the contents of services.js as follows:

    angular.module('udemyAdmin').value('pageSize', 2);

    In the above snippet, we create the most basic of value services, registering the value 2 for the pageSize injectable component.

    Edit the contents of controller.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, pageSize) {
      $scope.articles = [
        { title: "Arduino Tutorial" },
        { title: "After Effects Tutorial" },
        { title: "Django Tutorial" }
      ];
    
      $scope.numArticles = pageSize;
    });

    In the above snippet, articleCtrl now has a dependency on pageSize, so the value 2 is injected in. We then store it as $scope.numArticles for use in our HTML.

    Finally, edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        Number of articles to display: <input type="text" ng-model="numArticles" />
    
        <ul>
          <li ng-repeat="article in articles | limitTo:numArticles">
            
          </li>
        </ul>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
        <script src="services.js"></script>
      </body>
    </html>

    In the snippet above, limitTo is part of an expression and can therefore include$scope properties. We’ve replaced the hard-coded number of articles to limit to with numArticles. Additionally, we’ve attached ng-model to a new HTML text input. This is to allow a user to override the default provided by pageSize, should they wish to do so.

    Factory services

    A factory service also allows us to register injectable values. The key difference is that the injectable value is the return value of a function that can itself be injected with dependencies. This is definitely best explained with an example.

    Edit services.js as follows:

    angular.module('udemyAdmin')
    
      .value('pageSize', 2)
    
      .value('calculateCategoryPercentage', function(articles) {
        var availableCategories = ['tutorial', 'graphics', 'hardware'];
        var uniqueCategories = [];
    
        articles.forEach(function(article) {
          article.categories.forEach(function(category) {
            if (uniqueCategories.indexOf(category) == -1) {
              uniqueCategories.push(category);
            }
          });
        });
    
        return Math.floor(100 * (uniqueCategories.length / availableCategories.length));
      });

    In the above snippet, we have registered calculateCategoryPercentage as a value service that will calculate a percentage of used categories from an array from articles. The details of how (i.e., the nested looping) isn’t important, but note how we have hard-coded the available categories in a local variable.

    Now edit services.js as follows:

    angular.module('udemyAdmin')
    
      .value('pageSize', 1)
    
      .value('availableCategories', ['tutorial', 'graphics', 'hardware'])
    
      .factory('calculateCategoryPercentage', function(availableCategories) {
    
        return function(articles) {
          var uniqueCategories = [];
    
          articles.forEach(function(article) {
            article.categories.forEach(function(category) {
              if (uniqueCategories.indexOf(category) == -1) {
                uniqueCategories.push(category);
              }
            });
          });
    
          return Math.floor(100 * (uniqueCategories.length / availableCategories.length));
        };
      });

    In the snippet above, we have registered the previously hard-coded available categories as a value service, enabling them to be used elsewhere in the application via injection. We have then registered calculateCategoryPercentage as a factory service, a function that gets called once with its dependency (availableCategories) injected and then returns the same function as the value service version.

    To see how to use calculateCategoryPercentage, edit controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, pageSize, calculateCategoryPercentage) {
      $scope.articles = [
        { title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
        { title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
        { title: "Django Tutorial", categories: ['tutorial'] }
      ];
    
      $scope.numArticles = pageSize;
    
      $scope.categoryPercentage = calculateCategoryPercentage($scope.articles);
    });

    In the snippet above, calculateCategoryPercentage is injected into the controller and then invoked with our hard-coded list of articles. It’s important to note that availableCategories could also be injected into the controller, and in a later section we will do so.

    As with other examples we’ve seen, $scope.categoryPercentage can be used in index.html with a simple binding, such as:

    <span>Percentage of categories used: </span>

    Value and factory services are a more advanced technique, but their use allows us to achieve a level of decoupling in our AngularJS applications that will greatly improve the speed of implementing any future requirements.

    Directives continued

    In this last directives section, we again return to our analogy (a team of laborers completing some building works), as we have yet to cover any decorative directives.

    Rather than strictly being about styling and CSS, by decorative I mean in the sense of HTML elements being decorated with additional behaviour. This is like having a new kitchen fitted, which isn’t stuctural “bricks and mortar”, nor is it plumbing as the pipes have already been laid and are waiting to be used. But a new kitchen definitely gives a house additional behavior, especially if it has one of those fancy taps producing instant boiling water.

    Lets now look at a couple of example decorative directives, namely ng-click andng-checked.

    ng-click

    ng-click decorates HTML elements with the ability to invoke a model behaviour when they are clicked, which I suspect is fairly self-explanatory.

    In the previous section, we introduced a factory servicecalculateCategoryPercentage and used it with a hard-coded list of articles. Let’s make our application a little more interesting by allowing new articles to be created.

    Edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        <input type="text" ng-model="newTitle" placeholder="Enter article name..." />
        <button name="Add" ng-click="addArticle()">Add</button>
    
        <hr />
    
        Number of articles to display: <input type="text" ng-model="numArticles" />
    
        <ul>
          <li ng-repeat="article in articles | limitTo:numArticles">
            
          </li>
        </ul>
    
        <span>Percentage of categories used: </span>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
        <script src="services.js"></script>
      </body>
    </html>

    In the snippet above, we’ve added two new HTML elements. The text input element will update a model property newTitle via the ng-model directive (which has been discussed in a previous section on “Bindings”). The button element will invoke a model behaviour addArticle when clicked via ng-click.

    Edit controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, pageSize, calculateCategoryPercentage) {
      $scope.articles = [
        { title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
        { title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
        { title: "Django Tutorial", categories: ['tutorial'] }
      ];
    
      $scope.newTitle = '';
    
      $scope.addArticle = function() {
        $scope.articles.push({ title: $scope.newTitle, categories: [] });
      };
    
      $scope.numArticles = pageSize;
    
      $scope.categoryPercentage = calculateCategoryPercentage($scope.articles);
    });

    In the above snippet, addArticle uses $scope.newTitle when invoked to push a new article onto $scope.articles. Note that the new article has an empty categories array.

    ng-checked

    Our blog admin application needs to allow articles, new and old alike, to be re-categorized. Wouldn’t it be great if when this happens $scope.categoryPercentage is re-calculated? This can be accomplished with another usage of ng-click and a new directive ng-checked. Let’s explore how now.

    Edit controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, calculateCategoryPercentage, pageSize, availableCategories) {
    
      $scope.categories = availableCategories;
    
      $scope.articles = [
        { title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
        { title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
        { title: "Django Tutorial", categories: ['tutorial'] }
      ];
    
      $scope.$watch('articles', function(articles) {
        $scope.categoryPercentage = calculateCategoryPercentage(articles);
      }, true);
    
      $scope.containsCategory = function(article, category) {
        return article.categories.indexOf(category) >= 0;
      };
    
      $scope.toggleCategory = function(article, category) {
        var index = article.categories.indexOf(category);
    
        if (index == -1) {
          article.categories.push(category);
        } else {
          article.categories.splice(index, 1);
        }
      };
    
      $scope.newTitle = '';
    
      $scope.addArticle = function() {
        $scope.articles.push({ title: $scope.newTitle, categories: [] });
      };
    
      $scope.numArticles = pageSize;
    });

    We’ve introduced quite a lot in the above snippet. Let’s discuss each in turn.

    We previously mentioned that we would find another use foravailableCategories. It is now also injected into articleCtrl and set as$scope.categories.

    When we create bindings in our HTML (e.g., via ng-bind, , ng-model and many others), we are implicitly creating watches. At a simplified level, whenever AngularJS detects something that might affect an application (be it a browser event, HTTP response, and many others), it checks all of its watches against their previous values and updates bindings if there is a difference. We use $scope.$watch to create a manual watch of the articles array and re-calculate $scope.categoryPercentage if there is a difference. The third parameter to $scope.$watch indicates that we want a “deep” watch. For more information, please have a look at this excellent StackOverflow answer.

    Finally, we introduce two new model behaviors for interacting with an article’s categories. $scope.containsCategory simply saves us from having an ugly expression in our HTML. $scope.toggleCategory either adds or removes a category based on whether an article is already categorized as such.

    To see all of this in action, edit index.html as follows:

    <!DOCTYPE html>
    <html ng-app="udemyAdmin">
      <head>
        <title>Udemy tutorials - admin area</title>
      </head>
      <body ng-controller="articleCtrl">
    
        <input type="text" ng-model="newTitle" placeholder="Enter article name..." />
        <button name="Add" ng-click="addArticle()">Add</button>
    
        <hr />
    
        Number of articles to display: <input type="text" ng-model="numArticles" />
    
        <div ng-repeat="article in articles | limitTo:numArticles">
          <p></p>
          <label ng-repeat="category in categories">
            <input type="checkbox"
              ng-checked="containsCategory(article, category)"
              ng-click="toggleCategory(article, category)" />
            
          </label>
        </div>
    
        <span>Percentage of categories used: </span>
    
        <script src="angular.js"></script>
        <script src="app.js"></script>
        <script src="controllers.js"></script>
        <script src="services.js"></script>
      </body>
    </html>

    In the above snippet, we come across the usage of ng-click and ng-checked mentioned a little while ago. We’ve introduced a second ng-repeat loop to create a checkbox for each category. This ng-repeat loop is nested and, as such, creates the set of checkboxes for each article. ng-checked is used to ensure that the underlying state of the HTML checkbox element is kept in sync with the model (this is very similar to ng-model). ng-click is used to invoke toggleCategory with the appropriate article and category.

    If you try this out, you should see the percentage of categories used being updated as you categorize articles.

    Server integration

    The aim of this section is to discuss two services AngularJS provides for integrating with a server, namely $http and $resource. We’ll look at $http first and $resource second. $resource is essentially a RESTful extension of $http.

    Previously we introduced a feature to our blog admin application that calculated the percentage of used categories. We originally used this feature to look at how we can create our own injectable components. We’ll now use it again to discuss $http.

    $http

    The requirements for our blog admin application have changed, and we now need to fetch the categories from a server.

    The simplest way to achieve this is to use $http directly in articleCtrl.

    The contents of articleCtrl became quite lengthy in the previous section, so the following two snippets are intended to be illustrative.

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, $http, calculateCategoryPercentage) {
    
        $scope.articles = [
          { title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
          { title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
          { title: "Django Tutorial", categories: ['tutorial'] }
        ];
    
        $scope.categoryPercentage = 0;
    
        $http.get('/categories').then(function(response) {
          $scope.categoryPercentage = calculateCategoryPercentage($scope.articles, response.data);
        });
      });

    In the above snippet, we issue an HTTP request to the URL ‘/categories’ and, if it is successful, we pass the response data to calculateCategoryPercentage. There are two key things to note here.

    First, there will be a delay while the server responds. During this delay, the $scope property is not set and therefore a percentage is not displayed in the browser. We can lessen the impact of this by setting an initial value.

    Second, we have returned calculateCategoryPercentage to its simple value service version. But rather than hard-coding the available categories, calculateCategoryPercentage is passed as follows:

    angular.module('udemyAdmin')
    
      .value('calculateCategoryPercentage', function(articles, availableCategories) {
        var uniqueCategories = [];
    
        articles.forEach(function(article) {
          article.categories.forEach(function(category) {
            if (uniqueCategories.indexOf(category) == -1) {
              uniqueCategories.push(category);
            }
          });
        });
    
        return Math.floor(100 * (uniqueCategories.length / availableCategories.length));
      });

    $httpBackend

    For the purposes of a tutorial, it is desirable to avoid introducing any tangential complexity. In our case, that would be a server to respond to the URL ‘/categories’.

    AngularJS has a very useful additional module for backend-less development, namely $httpBackend. This is perfect for tutorials, but I have also used it in a team of four AngularJS developers building an application against a partially built server. We didn’t want the road map of server features to impact us, so for a period of time, we developed backend-less.

    Download angular-mocks.js and add another script reference in index.html in the following order:

    <script src="angular.js"></script>
    <script src="angular-mocks.js"></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
    <script src="services.js"></script>

    There are two variants of $httpBackend, one for unit testing and one for backend-less development. We need to tell our application to use the backend-less variant; this is achieved by adding a dependency to ngMockE2E to our udemyAdmin module. We then to configure how $httpBackend should respond to URL and HTTP verb combinations. Currently we only need one combination, HTTP GET and ‘/categories’.

    Edit app.js as follows:

    angular.module('udemyAdmin', ['ngMockE2E']).run(function($httpBackend) {
      $httpBackend.whenGET('/categories').respond(['tutorial', 'hardware', 'graphics']);
    });

    In the above snippet, the run method of our ‘udemyAdmin’ module is used for one-off configuration code that needs to execute as our application starts up.

    Promises

    We first looked at fetching categories from a server by using $http directly inarticleCtrl. For this to work, we would have to undo some of the decoupling we previously achieved by having calculateCategoryPercentage as a factory service with a dependency on availableCategories.

    Let’s now return to that and see if we can extract $http from articleCtrl and use it in availableCategories. If we can achieve that, articleCtrl and calculateCategoryPercentage can become oblivious to how the available categories are obtained. This is a more advanced technique that will help keep code flexible as our applications grow.

    We glossed over it in our simple illustration of $http, but the get method made the HTTP request (which is inherently asynchronous) and immediately returned a Promise object. A Promise object represents the future value of an asynchronous operation. If the operation succeeds, then the Promise is ‘resolved’; if it fails, then the Promise is ‘rejected’. A Promise object exposes several methods, one of which then allows us to register a resolution and/or rejection handler. In our simple usage, we just registered a resolution handler as follows:

    $http.get('/categories').then(function(response) {
      $scope.categoryPercentage = calculateCategoryPercentage($scope.articles, response.data);
    });

    Promises can, without doubt, get reasonably complicated. If you want to learn more, I suggest studying the readme for the Q library. AngularJS implements a lightweight version of the Q library.

    Now that we’ve briefly introduced Promises, let’s look at how we can use them to get our decoupling back. In the following snippets, I’ve added numbered comments, which we’ll discuss in detail shortly.

    Edit controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, calculateCategoryPercentage, pageSize, availableCategories) {
    
      $scope.categories = [];
    
      availableCategories.then(function(categories) {
        Array.prototype.splice.apply(
          $scope.categories,
          [$scope.categories.length, 0].concat(categories)
        );
      });
    
      $scope.articles = [
        { title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
        { title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
        { title: "Django Tutorial", categories: ['tutorial'] }
      ];
    
      $scope.$watch('articles', function(articles) {
        // 3
        calculateCategoryPercentage(articles).then(function(percentage) {
          // 6
          $scope.categoryPercentage = percentage;
        });
      }, true);
    
      $scope.containsCategory = function(article, category) {
        return article.categories.indexOf(category) >= 0;
      };
    
      $scope.toggleCategory = function(article, category) {
        var index = article.categories.indexOf(category);
    
        if (index == -1) {
          article.categories.push(category);
        } else {
          article.categories.splice(index, 1);
        }
      };
    
      $scope.newTitle = '';
    
      $scope.addArticle = function() {
        $scope.articles.push({ title: $scope.newTitle, categories: [] });
      };
    
      $scope.numArticles = pageSize;
    });

    Edit services.js as follows:

    angular.module('udemyAdmin')
    
      .value('pageSize', 1)
    
      .factory('availableCategories', function($http) {
        // 1
        return $http.get('/categories').then(function(response) {
          return response.data;
        });
      })
    
      .factory('calculateCategoryPercentage', function(availableCategories) {
        // 2
        return function calculate(articles) {
          var uniqueCategories = [];
    
          articles.forEach(function(article) {
            article.categories.forEach(function(category) {
              if (uniqueCategories.indexOf(category) == -1) {
                uniqueCategories.push(category);
              }
            });
          });
    
          // 4
          return availableCategories.then(function(categories) {
            // 5
            return Math.floor(100 * (uniqueCategories.length / categories.length));
          });
        };
      });

    In the above snippets, the numbered comments relate to the following points:

    1. articleCtrl depends on calculateCategoryPercentage which depends onavailableCategories. The Promise object returned from $http.get('/categpries').then is registered as availableCategories. Note that availableCategories has been made a factory service so that it, too, can have a dependency injected (namely, $http).
    2. calculateCategoryPercentage is next in the dependency chain, so the function calculate is registered.
    3. articleCtrl runs as the last step in the dependency chain. It calls calculateCategoryPercentage each time its articles change (via $scope.$watch). A Promise object is returned, and articleCtrl assigns a resolution handler.
    4. A resolution handler is assigned to the availableCategories Promise object. Assigning resolution handlers via then returns another Promise object, which allows for chained resolution.
    5. availableCategories is resolved (i.e., a response is received from the server), and the category percentage is calculated and returned.
    6. The chained resolution set in step 4 allows articleCtrl to set the category percentage as a $scope property.

    You may wonder about the benefit of this approach over the simpler use of $http direct in articleCtrl we had previously. In both approaches, we have had to change how calculateCategoryPercentage is used in articleCtrl. In this approach, the change has been to work with Promises. Promises are a very general API. For example, in the future our application could first look in the browser’s local storage for categories before resorting to an HTTP server call. The Promise API that articleCtrl works with wouldn’t change one bit, but behind the scenes, obtaining the categories would be more involved. With Promises, articleCtrl has no insight into how the categories are obtained for the calculation, just that somehow they are.

    $resource

    Until now, the initial articles in our blog admin application have been hard-coded in articleCtrl. This clearly isn’t the most flexible application around; as such, the requirements have changed yet again.

    We’re now asked to provide a means of retrieving and creating articles stored on a server. The server has provided us a RESTful API for interacting with articles. Sending an HTTP GET request to the URL ‘/articles’ will return an array of articles, while sending an HTTP POST request will create and return a new article. $resource is another additional module and is perfect for working with this type of API.

    Download angular-resource.js and add another script reference in index.html in the following order:

    <script src="angular.js"></script>
    <script src="angular-resource.js"></script>
    <script src="angular-mocks.js"></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
    <script src="services.js"></script>

    As with $http previously, the simplest way to get up and running with $resource is to use it directly in articleCtrl. We could encapsulate $resource in another factory service so that articleCtrl isn’t aware of how articles are retrieved and created. For our purposes, the first approach allows us to focus on the detail of using $resource, but in a larger real-world application, I would certainly consider the latter approach.

    Edit app.js as follows:

    angular.module('udemyAdmin', ['ngResource', 'ngMockE2E']).run(function($httpBackend) {
      $httpBackend.whenGET('/categories').respond(['tutorial', 'hardware', 'graphics']);
    });

    The change in the above snippet is a simple one: we’ve just added an additional dependency for the ‘udemyAdmin’ module, namely ‘ngResource’.

    Edit controllers.js as follows:

    angular.module('udemyAdmin').controller('articleCtrl', function($scope, calculateCategoryPercentage, pageSize, availableCategories, $resource) {
    
      var Article = $resource('/articles');
    
      $scope.categories = [];
    
      availableCategories.then(function(categories) {
        Array.prototype.splice.apply(
          $scope.categories,
          [$scope.categories.length, 0].concat(categories)
        );
      });
    
      $scope.articles = Article.query();
    
      $scope.$watch('articles', function(articles) {
        calculateCategoryPercentage(articles).then(function(percentage) {
          $scope.categoryPercentage = percentage;
        });
      }, true);
    
      $scope.containsCategory = function(article, category) {
        return article.categories.indexOf(category) >= 0;
      };
    
      $scope.toggleCategory = function(article, category) {
        var index = article.categories.indexOf(category);
    
        if (index == -1) {
          article.categories.push(category);
        } else {
          article.categories.splice(index, 1);
        }
      };
    
      $scope.newTitle = '';
    
      $scope.addArticle = function() {
        var newArticle = new Article({ title: $scope.newTitle });
        newArticle.$save().then(function(article) {
          $scope.articles.push(article);
        });
      };
    
      $scope.numArticles = pageSize;
    });

    In the above snippet, $resource is declared as a dependency of articleCtrl and subsequently injected in. We create a resource object Article by invoking $resource with our URL.

    We then invoke Article.query to retrieve an array of articles from the server. This results in an HTTP GET request to ‘/articles’. As this is an asynchronous operation, it does not block the application waiting for the server to respond, but what value would be useful to return in the meantime? $resource immediately returns an empty array, which will be filled when the server responds. This is a neat trick, as combined with setting the array as a property on $scope, any binding we make in our HTML will automatically update.

    We haven’t looked at the contents of index.html for a while, but the usage of ng-repeat doesn’t need to change, even though we’ve switched to $resource and $scope.articles is, at first, an empty array.

    Returning to the snippet of articleCtrl above, when $scope. addArticle is invoked we now create a new instance of Article with $scope.newTitle as its title. We then invoke $save resulting in an HTTP POST request to ‘/articles’. A Promise object is returned, which is resolved by the server responding with an HTTP 200 status code and body. A instance of Article is created from the server response for us, and we simply push it onto $scope.articles. The usage of $scope.addArticle and $scope.newTitle in index.html does not need to change.

    $httpBackend again

    As $resource is an extension of $http, we can also use $httpBackend to configure how requests to the URL ‘/articles’ should be handled.

    Edit app.js as follows:

    angular.module('udemyAdmin', ['ngResource', 'ngMockE2E']).run(function($httpBackend) {
      $httpBackend.whenGET('/categories').respond(['tutorial', 'hardware', 'graphics']);
    
      var articles = [
        { title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
        { title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
        { title: "Django Tutorial", categories: ['tutorial'] }
      ];
    
      $httpBackend.whenGET('/articles').respond(articles);
      $httpBackend.whenPOST('/articles').respond(function(method, url, data) {
        var article = angular.fromJson(data);
        article.categories = article.categories || [];
        articles.push(article);
        return [200, article, {}];
      });
    });

    In the above snippet, we’ve simply moved the hard-coded array of articles from articleCtrl and returned it whenever an HTTP GET request is made to ‘/articles’. An HTTP POST request to ‘/articles’ pushes another article onto the array, meaning that any subsequent GET requests would include it.

    Summary

    If you’ve made this far, after all that copy and pasting of snippets, then I salute you for sticking with me.

    You should have a working sample application that demonstrates AngularJS as a great framework that’s both powerful and fun to use.

    This tutorial certainly doesn’t cover everything. My hope is that it has covered enough of the basics that, along with a couple of intermediate concepts, it will give you the knowledge to continue the journey you have started with AngularJS.

    Thanks for reading!

    Published on Codepedia.org with the permission of Udemy – source AngularJS Tutorial: A Beginner’s Guide to AngularJS from https://blog.udemy.com/

    Udemy blog

    Udemy

    Udemy.com is a platform or marketplace for online learning. Unlike academic MOOC programs driven by traditional collegiate coursework, Udemy provides a platform for experts of any kind to create courses which can be offered to the public, either at no charge or for a tuition fee. Udemy provides tools which enable users to create a course, promote it and earn money from student tuition charges.

    Subscribe to our newsletter for more code resources and news

    routerLink with query params in Angular html template

    routerLink with query params in Angular html template code snippet Continue reading