Chapter 2. Building an Amazing Store Frontend with AngularJS

The tagline for AngularJS is: HTML enhanced for web apps!, and it does exactly that. It extends the HTML code with 'directives' like attributes, new tags, classes, and comments. These directives bind the HTML elements to AngularJS code and change the default behavior of the element. AngularJS differs from unobtrusive frameworks such as jQuery and BackboneJS in that the bindings are through HTML extensions (directives) rather than ID and CSS selectors. This approach alleviates the code complexity and readability that comes with the more traditional JS frameworks. The AngularJS framework first appeared in 2009 and quickly became one of the most popular JavaScript frameworks. Finally, AngularJS has many built-in features that make it an excellent candidate for developing Single Page Applications (SPA) and Data-Driven Applications.

A couple of decades ago, JavaScript was mainly used for form validations, some animations, and visual effects. Back then, most of the JS testing was done manually. The JS code was tightly coupled to the DOM, which made it hard to test. Over the years, in order to minimize the response time and network delays, more of the business logic started migrating from the backend to the client. JS needed more than ever to be testable. AngularJS excels in testability. Tests and test runners were created for it since its architectural conception. The Karma test runner and the end-to-end tests with Protractor are good examples of this.

AngularJS is a very capable Model-View-Controller (MVC) framework. MVC is a UI architectural pattern which separates the view from the data models and binds them through controllers. Angular 1.x implements a two-way data binding that always keeps the view (HTML) and models (JavaScript) in sync. Thus, AngularJS models are the single source of truth. Another nice feature is that AngularJS provides XHR services, which make it very easy to integrate with the RESTful APIs.

This chapter will focus only on AngularJS. In later chapters, we will show you the ways to connect AngularJS to the rest of the MEAN components.

Without further ado, let's dive in and discuss the following topics in this chapter:

  • Understanding AngularJS and the client directory structure
  • Laying out the e-commerce site
  • CRUD-ing products

Understanding AngularJS and the client directory structure

This section will help you get started with AngularJS. We are going to focus on the client part of our project and the AngularJS concepts will be explained as we walk through our app.

Let's first get familiarized with the functionality of our application out-of-the-box:

  • Go to the project root, and run grunt serve in the command line (remember, you need mongod running as well).
  • Interact with the app for a few minutes (http://localhost:9000). Open another window with the application. Add a new item and notice that this too gets updated in all the browser windows in real-time. Pretty cool, huh? We are going to use that feature (SocketIO) to make our e-commerce site real-time as well.

We are going to build a single page application (SPA), so we only need a single HTML file: index.html. AngularJS will take control of it and manage the transitions and the different states in such a way that the user will feel like he/she is navigating through different pages. This HTML file contains all the necessary boilerplate to render well in most browsers and mobile devices.

Client-side structure

We have three main directories in our meanshop project: client, server, and e2e. In this chapter we are going to focus solely on the client folder. It contains our AngularJS code and assets such as images and fonts. This is an overview of the client directory:

client
├── app        - All of our app specific components go in here
├── assets     - Custom assets: fonts, images, and so on
└── components - Reusable components, non-specific to our app

Mostly, we are going to be using the app folder to write our AngularJS code. Let's take a look at an example to see how each of our components will be organized:

meanshop/client/app/main
├── main.js                 - Routes
├── main.controller.js      - Controller
├── main.controller.spec.js - Test
├── main.html               - View
└── main.scss               - Styles

As you can see, we are going to have routes, controllers, views and styles properly named in each app subfolder.

Now we are going to examine the code in the order of execution.

Open the meanshop folder and inside that, open the client folder. This is where we are going to set all the client code such as AngularJS files, HTML, CSS, and images. Open index.html. The following screenshot displays the project file structure and the AngularJS directives in the index.html file:

Client-side structure

Directives

Directives are HTML extensions in the form of attributes, tags, CSS classes, and even HTML comments. Directives are a key component in AngularJS, and the way in which it extends the HTML functionality. We can also define our own directives, as we are going to see in further chapters.

Take a look at index.html. The first thing to notice are all the non-standard HTML attributes. These are the directive attributes. In the body tag, we have ng-app defining the name of the module.

<!-- client/index.html -->
<body ng-app="meanshopApp">

The ng-app directive is necessary for bootstrapping AngularJS automatically. It defines the root module of the application:

Note

Directive attributes are not part of the HTML standard; they are extensions to AngularJS. So, they will fail when the HTML validators are run against them. On the other hand, HTML5 does allow custom attribute names using the prefix data-*. All that we need to do to make the AngularJS directive's HTML valid is prefixing it with data. For instance: data-ng-app="" and data-ui-view="".

There's another directive to be aware of called ui-view:

<!-- client/index.html -->
<div ui-view=""></div>

This div element is where a module called ui-router pushes the HTML code. More on modules and routing can be learnt in the following sections.

Moving back to the index.html file, notice some JS files being referenced at the bottom of the screen: Angular-resource, angular-ui-router to mention a few, as well as third party JS libraries, such as jQuery, SocketIO, AngularJS, and so on. Below that, we can see our application files starting with app/app.js. Let's now get into the client/app folder. The app.js file is the entry point of the application.

Modules

Modules are the preferred way for organizing code in AngularJS. Even though we could get away without them, it makes the code much easier to maintain. AngularJS provides a global variable that contains many functions, and one of them is module. We are going to use module extensively through our application to set and get modules and to manage dependencies. The angular.module can work both as a getter and a setter. The getter is just the name of the module without any other parameter. It will load that module if it exists:

angular.module('meanshopApp');

On the other hand, the setter has two parameters: one to define the name of the module as defined in the ng-app directive, and the other to load the dependencies if any. In case there are no dependencies, an empty array must be passed.

In the following code, angular.module is being used as a setter. It creates the module and has a second parameter for dependencies:

/* client/app/app.js */

angular.module('meanshopApp', [
  'ngCookies',
  'ngResource',
  'ngSanitize',
  'btford.socket-io',
  'ui.router',
  'ui.bootstrap'
]);

We are creating a new module called meanshopApp in the preceding code. The second argument is an array of third-party AngularJS modules such as routes (ui.router), cookies (ngCookies), and so on. Furthermore, notice that we have chained the methods to the module right after creating it.

When AngularJS sees np-app="meanshopApp" in index.html, it knows it has to execute the module with the matching name and make all the listed dependencies available.

The angular.module getter has a number of other methods that can be chained to it. For instance, in the app.js, we can see the config, factory, and run methods.

  • Config: This executes at the time of loading of a module.
  • Factory: This returns an object or a function closure
  • Run: This executes when all the modules are done loading

At a higher level, we use the config function to bootstrap the routes, which we are going to explain next.

Routing with AngularUI router

Routing allows the user to have a URL that reflects the current state of the application. Single Page Applications (SPAs) could have all the different pages with just one unchangeable URL. Conversely, the user will not have the functionality to go back in history or to access a page/state by typing the URL directly. To fix that, we are going to use a module called ui-router, which is going to pair URLs and states with HTML views and controllers.

Back in index.html, remember the ui-view directive; this is where ui-router pushes the content of the page. Furthermore, besides loading app/app.js, there are other files being loaded such as main.js. Let's take a look at that one.

/* client/app/main/main.js */

angular.module('meanshopApp')
  .config(function ($stateProvider) {
    $stateProvider
      .state('main', {
        url: '/',
        templateUrl: 'app/main/main.html',
        controller: 'MainCtrl'
      });
  });

The AngularJS UI router allows us to define views and states. The way we wire up routes, controllers, and states to HTML templates is through $stateProvider. Main.js sets up the root URL ('/') to a template (main.html) and a controller (MainCtrl). A similar pattern is found in the following files:

  • client/app/admin/admin.js
  • client/app/account/account.js

We can list multiple routes at once, like in account.js. Now, let's talk more about the controllers that these routes are using.

Tip

Single Page Applications (SPA), Routing, and SEO

In an SPA, every time an internal link is clicked, it does not pull it from the server. Instead, it renders the predefined HTML template and does API calls in case it needs some data. This is a performance boost and a smarter utilization of the bandwidth. However, there is a caveat: since a single URL could have a lot of different pages and states, the history of the browser cannot detect it. To fix that, SPA adds dynamic routes through JS using hash like this: /#products or /#/orders. In terms of SEO, some web crawlers still don't render AJAX pages. They need the hash-bang URL convention to recognize AJAX pages, for example, /#!products or /#!/orders. Modern browsers have HTML5 history.pushstate, which allows us to use a URL without the hash or hash-bang, and still make it work like the conventional pages. For more info, refer to https://developers.google.com/webmasters/ajax-crawling/docs/learn-more.

Controllers and scopes

As in any MVC framework, a controller interacts with Views and Modules. Controllers are the ones that are responsible for loading the data and representing it in the HTML templates (Views).

Going back to the main.js file, we see that the root URL ('/') is managed by MainCtrl. Let's open main.controller.js:

/* meanshop/client/app/main/main.controller.js */

angular.module('meanshopApp')
  .controller('MainCtrl', function ($scope, $http, socket) {
    $scope.awesomeThings = [];

    $http.get('/api/things').success(function(awesomeThings) {
      $scope.awesomeThings = awesomeThings;
      socket.syncUpdates('thing', $scope.awesomeThings);
    });

    $scope.addThing = function() {
      if($scope.newThing === '') {
        return;
      }
      $http.post('/api/things', { name: $scope.newThing });
      $scope.newThing = '';
    };

    $scope.deleteThing = function(thing) {
      $http.delete('/api/things/' + thing._id);
    };

    $scope.$on('$destroy', function () {
      socket.unsyncUpdates('thing');
    });
  });

In AngularJS, a controller is the glue between the models/services and the views. The data is retrieved using $http, and is made accessible to the view through $scope.

What is $http? It is a core AngularJS service that facilitates the communication with remote servers. Behind the scenes, it handles the XMLHttpRequest or jsonp JSONP requests. We are going to use it in the next few chapters to communicate with the ExpressJS server.

What is $scope? It is an object that glues the controller to the views. It provides two-way data binding. Every time we update a variable in $scope, it automatically rerenders the HTML view. Similarly, every time a change is made in the HTML, its value representation gets updated. In the preceding code, notice that we cannot just set variables like awesomeThings, but we can also make functions available and listen for events.

Note

All AngularJS core identifiers and built-in services are distinguished by the dollar sign prefix with the name. For example, $scope, $http.

Next we are going to see how we can use these scoped variables and functions in the templates.

Templates

Templates are the HTML files mixed with AngularJS enhancements. They execute the JS expressions, reference variables, and functions in the $scope.

Let's see an example. Open main.html:

<!-- client/main/main.html *excerpt -->

<div class="container">
  <div class="row">
    <div class="col-lg-12">
      <h1 class="page-header">Features:</h1>
      <ul class="nav nav-tabs nav-stacked col-md-4 col-lg-4 col-sm-6" ng-repeat="thing in awesomeThings">
        <li><a href="#" tooltip="{{thing.info}}">{{thing.name}}<button type="button" class="close" ng-click="deleteThing(thing)"> &times;</button></a></li>
      </ul>
    </div>
  </div>

In this small fragment of the main.html file, we can see new directives such as:

  • ng-click: This is a directive that allows specifying a custom behavior when it is clicked. For example, in the code snippet, when the button is clicked, it executes 'deleteThing()', which is a function defined in MainCtrl's $scope.
  • ng-repeat: This is an iterator for a collection. For instance, 'awesomeThings' is an array set in the controller, and thing represents each of the elements in turn.

Note

There are many other directives available for use in templates. Take a look at the entire list at https://docs.angularjs.org/api/ng/directive.

It has been a great walk-through so far. Now, it's coding time!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.54.149