AngularJS Tutorial in a Hot Towel – Part 2

In the second part of the recently released AngularJS-1 tutorial, the fundamental elements of the first part, such as modules, directives, controllers, templates etc., will be expanded by Angular to include more advanced concepts. These concepts include services, the communication with the server, promises, two-way data binding and event handling. The structure of this article is oriented on Part 1; in addition to fundamental explanations of individual concepts, the example application has been extended with the adding of a new view. This view includes a further module, a controller and a new route. In the finish it will be possible to manipulate entries within the database via the graphic interface.

Current Status

The precondition for advancing with the tutorial is having completed the first part. Alternatively, the following branch can be cloned and used as a starting point. The basic structure for this tutorial is as follows:

src/
 |__ client/
        |__ app/
               |__ blocks/
               |__ core/
               |__ layout/
               |__ widgets/
               |__ welcome/
               |__ app.module.js
 |__ server/*

Expanding with a new view

The first thing that is done is to create a mask in which the superheroes can be illustrated in a table. There are also fields which allow the addition of new heroes and the deletion of existing heroes from the table. It is for this reason that the current status of the web app is expanded with the addition of a new folder /src/client/app/watchmen/. Four new files are created within this new folder. One template, one controller, one module and one route. The route and modules are hardly any different to the already defined files welcome.module.js and welcome.route.js. They have the following content:

// /src/client/app/watchmen/watchmen.module.js
angular.module('app.watchmen', [
    'app.core',
    'app.widgets'
]);

The module needs to be entered as a dependency into the file /src/client/app/app.module.js.

// app.module.js
angular.module('app', [
    'app.core',
    'app.widgets',
    'app.layout',
    'app.watchmen
]);

The route is next:

// /src/client/app/watchmen/watchmen.route.js  

  angular
    .module('app.watchmen')
    .run(appRun);

  appRun.$inject = ['routerHelper'];
  /* @ngInject */
  function appRun(routerHelper) {
    routerHelper.configureStates(getStates());
  }

  function getStates() {
    return [
      {
        state: 'watchmen',
        config: {
          url: '/watchmen',
          templateUrl: 'app/watchmen/watchmen.html',
          controller: 'WatchmenController',
          controllerAs: 'vm',
          title: 'Watchmen',
          settings: {
            nav: 2,
            content: '<i class="fa fa-lock"></i> Watchmen'
          }
        }
      }
    ];
  }

In order for the routing onto the template watchmen.html to be possible, this must obviously already exist. The HTML file has the following form:

<!-- /src/client/app/watchmen/watchmen.html -->
<section id="dashboard-view" class="mainbar">
  <section class="matter">
    <div class="container">
      <div class="row">
        <div class="col-md-6">
          <div class="widget wviolet">
            <div ht-widget-header title="Watchmen" allow-collapse="true"></div>
            <div class="widget-content text-center text-info">
              <table class="table table-condensed table-striped">
                <thead>
                  <tr>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>Alias</th>
                    <th>Id</th>
                  </tr>
                </thead>
                <tbody>
                  <tr ng-repeat="w in vm.watchmen">
                    <td>{{w.firstName}}</td>
                    <td>{{w.lastName}}</td>
                    <td>{{w.alias}}</td>
                    <td>{{w._id}}
                  </tr>
                </tbody>
              </table>
              </div>
            </div>
            <div class="widget-foot">
              <div class="clearfix"></div>
            </div>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-3">
          <span><h3>Add new Hero</h3></span>
          <form ng-submit="vm.sendData()">
              <div class="form-group row">
                <label for="firstNameInput" class="col-xs-2 col-form-label">First Name</label>
                <input class="form-control" type="text" placeholder="firstname" id="firstNameInput" ng-model="vm.firstName" required>
              </div>
              <div class="form-group row">  
                <label for="lastNameInput" class="col-xs-2 col-form-label">Last Name</label>
                <input class="form-control" type="text" placeholder="lastname" id="lastNameInput" ng-model="vm.lastName" required>
              </div>  
              <div class="form-group row">  
                <label for="aliasInput" class="col-xs-2 col-form-label">Alias</label>
                <input class="form-control" type="text" placeholder="alias" id="aliasInput" ng-model="vm.alias" required>
              </div>
              <button type="submit" class="btn btn-primary">Save new user</button>
          </form>
        </div>
      <div class="col-md-3">
        <span><h3>User deletion by Id</h3></span>
        <form ng-submit="vm.deleteData()">
          <div class="form-group row">
            <label for="idDeleteInput" class="col-xs-2 col-form-label">Id to delete</label>
            <input class="form-control" type="text" placeholder="id" id="idDeleteInput" ng-model="vm.idToDel" required>
          </div>
          <button type="submit" class="btn btn-primary">Delete user by Id</button>
        </form>
      </div>
    </div>
    </div>
  </section>
</section>

At first glance, the template appears to be very complex and chaotic, which is, however, in particular due to the tag attributes for bootstrap and for the HotTowel widgets. When broken down into its fundamental elements, the HTML template is made up of three components: a table of superheroes, an input form for entering new heroes into the table and an input form for deleting individual entries.

<!-- table excerpt of watchmen.html -->
<table class="...">
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Alias</th>
      <th>Id</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="w in vm.watchmen">
      <td>{{w.firstName}}</td>
      <td>{{w.lastName}}</td>
      <td>{{w.alias}}</td>
      <td>{{w._id}}
    </tr>
  </tbody>
</table>

The table is filled up using the ngRepeat directive by iterating all entries of the array vm.watchmen (this is defined a little further on in the controller). A prerequisite for this is obviously that the array contains the corresponding key-value entries for firstName, lastName, alias and _id.

Note on the side: Watchmen is a superhero film based on the comic from the author Alan Moore and the comic book artist Dave Gibbons.

<!-- form input to add new heroes -->
<span><h3>Add new Hero</h3></span>
<form ng-submit="vm.sendData()">
  <label for="firstNameInput">First Name</label>
  <input type="text" placeholder="firstname" id="firstNameInput" ng-model="vm.firstName" required>
  <label for="lastNameInput">Last Name</label>
  <input type="text" placeholder="lastname" id="lastNameInput" ng-model="vm.lastName" required>
  <label for="aliasInput">Alias</label>
  <input type="text" placeholder="alias" id="aliasInput" ng-model="vm.alias" required>
  <button type="submit" class="...">Save new user</button>
</form>

The input form for entering new superheroes contains three text entry fields and a submit button. The directives ngModel and ngSubmit, in particular, are relevant for Angular. The directive ngSubmit is located within the form tag and contains a string value “vm.sendData()”. vm.sendData() is a function that needs to be defined and triggered in the Watchmen controller as soon as the form is transmitted. The value of ngSubmit must correspond to a valid expression in the process.
NgModel integrates the entry of the input field via the scope directly into an attribute in the controller. In order to emphasise this, the template can be expanded with an additional line underneath the form block <div>{{vm.firstName}}</div>. Entries into the first-name text field are displayed synchronously in the template.

<!-- input form for user deletion -->
<span><h3>User deletion by Id</h3></span>
<form ng-submit="vm.deleteData()">
  <label for="idDeleteInput">Id to delete</label>
  <input type="text" placeholder="id" id="idDeleteInput" ng-model="vm.idToDel" required>
  <button type="submit">Delete user by Id</button>
</form>

The second form works in accordance with the same principle. By clicking on the submit button, the function vm.deleteData() is executed. This is not yet declared because the corresponding Watchmen controller has not yet been defined. However, you need to take a quick look at the services and providers prior to creating the controller.

Services from Constant to Provider

Each web application consists of a wide range of objects with various tasks. The majority of these objects are automatically instantiated by Angular by means of the injector service and connected with other components. It can be generally differentiated between two types of objects, which are generated by the injector. There are services and specialised objects. Services are objects, whose API are determined by the developer who writes the service, whereas specialised objects are controllers, directives, filters or animations. Services contain logic and functions, for example, which should be spread across the entire application. Instead of separately defining complex calculations or repeating tasks by the same function in each controller, it is sufficient to define this in a service just once and to subsequently inject this into any controller.
In order to be able to tell the injector how service objects are created, the so-called recipes need to registered on the injector. There are five different types of recipes: constant, value, service, factory and provider. Note: A recipe-type service is not the same as service objects, it is just a special subtype of this.

A constant can be injected everywhere and its value can never be changed.

var app = angular.module('app');
  
app.constant('MAX_INT', 2147483647);
  
app.controller('MyCtrl', function (MAX_INT) {
  // useage of constant here
});

A value is not more than a value that is injected. A value can be a string, a number or a function.

var app = angular.module('app');
  
app.value('movieTitle', 'Watchmen');
  
app.controller('MyCtrl', function (movieTitle) {
  // useage of value here
});

The configuration of a service entails the registration of a constructor function, which creates a service instance. Angular retrieves this function when the associated service is requested. The created instance is then injected. Services contain objects or functions, which should be spread across the entire application. Services are singletons and dependencies in the form of services or values can be injected.

var app = angular.module('app');
   
app.service('movie', function () {
  this.title = 'Watchmen';
});
  
app.controller('MyCtrl', function(movie) {
  // useage of service here
});

A factory is a function that demonstrates strong similarities with a service. If, for example, a controller uses a factory function, the result of the factory is injected into the controller. A factory is also a singleton and the same dependencies as for a service can be injected into the factory. In contrast to factory, the function handed over is constructed with new in a service.

var app = angular.module('app');
   
app.factory('movie', function () {
  return {
    title : 'Watchmen';
  }
});
  
app.controller('MyCtrl', function(movie) {
  // usage of factory here
});

A provider can be understood to be a factory that can be configured. Providers accept objects or a constructor. Providers allow the configuration of a service object at the start of the application.

var app = angular.module('app');
  
app.provider('movie', function () {
  var version;
  return {
    setVersion: function (value) {
      version = value;
    },
    $get: function () {
      return {
          title: 'Watchmen' + ' ' + version
      }
    }
  }
});

app.config(function (movieProvider) {
  movieProvider.setVersion('Reloaded');
});
  
app.controller('MyCtrl', function (movie) {
  titleWithVersion = movie.title; 
});

Using app.config, the provider is injected at the start of the application and its method setVersion is retrieved. This sets the variable version to the value “Reloaded”. The provider service is then subsequently injected into a controller and can be retrieved there via the movie.title. The variable in the controller titleWithVersion has the value “Watchmen Reloaded” as a result.

Data Services and Promises

The factory dataservice in the file /src/client/app/core/dataservice.js contains a wide variety of functions that make XHR calls on the defined REST server. The functions return promises, which in turn transmit to a controller and which can then be dissolved there (more about this shortly). The functions include getWatchmen(), a function, which returns all Watchmen entered into the database, the function postWatchmen(), in order to add new Watchmen to the database, and deleteWatchmenById(), in order that individual heroes can be deleted from the database. The file consists of the following content:

// dataservice.js
angular
    .module('app.core')
    .factory('dataservice', dataservice);

  dataservice.$inject = ['$http', 'exception', 'logger'];
  /* @ngInject */
  function dataservice($http, exception, logger) {
    var service = {
      getWatchmen: getWatchmen,
      postWatchmen: postWatchmen,
      deleteWatchmenById: deleteWatchmenById
    };

    return service;

    function getWatchmen() {
      return $http.get('/api/watchmen')
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for getWatchmen')(e);
      }
    }

    function postWatchmen(data) {
      return $http.post('/api/watchmen', data)
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for postWatchmen')(e);
      }
    }

    function deleteWatchmenById(data) {
      return $http.delete('/api/watchmen/' + data, data)
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for deleteWatchmenById')(e);
      }
    }
  }

Firstly, a short digression to promises: Promises are given back through asynchronous functions and give the promise that the program part of this function is executed. This is either successful (resolve) or it fails (reject). This will allow successful or unsuccessful execution to be reacted to from the outset.

The Angular service $http within the factory dataservice.js. serves as an example for promises. XHR calls on distant HTTP servers are possible via $http, which are in turn returned as promise objects.

// excerpt dataservice.js
function getWatchmen() {
      return $http.get('/api/watchmen')
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for getWatchmen')(e);
      }
    } 

The service $http within the function getWatchmen uses the get-method to access the server’s REST-API, and gives back a promise in the process. If the accessing is successful, the function success is executed and the response data are returned. In this case, a promise object that contains an array of superheroes from the database. If the attempt to access fails, for example because the connection to the database cannot be established, the malfunction is intercepted and the function fail is executed.
The exact definition of the function exception.catcher is located in the file /src/client/app/blocks/exception/exception.js.

The total of three functions contained in the factory dataservice are returned in the form of a service object that can be subsequently injected into other components of the example application, e.g. into the WatchmenController, through which the functions at this point can be retrieved.

var service = {
      getWatchmen: getWatchmen,
      postWatchmen: postWatchmen,
      deleteWatchmenById: deleteWatchmenById
};

return service;

The Watchmen Controller

The next step is to create the controller, whose authority is defined and laid down in /src/client/app/watchmen/watchmen.route.js. The WatchmenController is registered in the module app.watchmen and implements the logic of the functions from the template watchmen.html (vm.sendData() and vm.deleteData()).

// watchmen.controller.js
  angular
    .module('app.watchmen')
    .controller('WatchmenController', WatchmenController);

  WatchmenController.$inject = ['$q', 'dataservice', 'logger'];
  /* @ngInject */
  function WatchmenController($q, dataservice, logger) {
    var vm = this;

    vm.watchmen = [];
    vm.title = 'Watchmen';

    
    vm.sendData = function () {
      vm.data = {
        firstName: vm.firstName,
        lastName: vm.lastName,
        alias: vm.alias
      };
      return postWatchmen(vm.data);
    };

    
    vm.deleteData = function () {
      return deleteWatchmenById(vm.idToDel);
    };

    activate();

    // controller initialization function
    function activate() {
      var promises = [getWatchmen()];
      return $q.all(promises).then(function () {
        logger.info('Activated Watchmen View');
      });
    }

    function getWatchmen() {
      // returns and manipulates promise
      return dataservice.getWatchmen().then(function (data) {
        vm.watchmen = data;
        return vm.watchmen;
      });
    }

    function postWatchmen(data) {
      // send data via service and refresh table
      return dataservice.postWatchmen(data).then(getWatchmen);
    }

    function deleteWatchmenById(data) {
      // send data via service and refresh table
      return dataservice.deleteWatchmenById(data).then(getWatchmen);
    }
  }

The following services are injected into the controller: $q, dataservice and logger. The service dataservice has just been defined, $q serves as a promise constructor, but also offers other general functions in order to handle promises, and logger is, as the name already suggests, a self-written logger (please see /src/client/app/blocks/logger/logger.js).

// excerpt of watchmen.controller.js

function getWatchmen() {
  // returns and manipulates promise
  // Step 2
  return dataservice.getWatchmen().then(function (data) {
    // Step 3
    vm.watchmen = data;
    return vm.watchmen;
  });
}
activate();

// controller initialization function
function activate() {
  // Step 1 
  var promises = [getWatchmen()];
    // Step 4
    return $q.all(promises).then(function () {
      logger.info('Activated Watchmen View');
    });
} 

While the controller is being initialised, it retrieves the function activate(). Subsequently, var promises is defined as the array of all promises to be dissolved (in this case, that is only one). The getWatchmen function defined in the controller uses the function dataservice.getWatchmen() from dataservice.js. This function returns a promise object that receives data from the database when successful. The function then is provided with a callback, whose parameter data is the return value of the previous promise object that is then subsequently bound to the array vm.watchmen (please see watchmen.html) in the controller. This means that the array promises itself receives a promise. Back to activate() function. The function $q.all(promises) retrieves the function then, if all promises in the array are dissolved; in this case, it is only (var promises = [getWatchmen()];).

That all sounds terribly complicated, but put simply, it essentially means the following:

1. Ask getWatchmen function for data for the superheroes and wait for promise.
2. Execute the dataservice.getWatchmen function with access to the database.
3. In the event of success, set the return data from the DB to vm.watchmen and dissolve the promise.
4. Execute logger.info because all promises for $q.all are dissolved.

Enter new heroes or delete them

With the creation of the controller, all necessary components for second view are now available. It is now possible to use Watchmen view to create superheroes via the first input form (vm.sendData()). All created superheroes are displayed on a table, the function responsible for this is getWatchmen(). These can be deleted from the database via their generated ID using the second form and are then no longer displayed on the table. The updating of the table takes place within the function declaration of postWatchmen(data) and deleteWatchmenById(data). The decisive line of code is return dataservice.postWatchmen(data).then(getWatchmen);.

For example, Edward Blake, alias The Comedian, can be entered into the database. The REST-API can be subsequently used in order to check that it exists as a new entry in the database (localhost:3000/api/watchmen). By copying his generated ID, the entry can be removed once again from the database, and consequently from the table.

Event Handling

AngularJS makes it possible to handle occurring events using the function $on. The $on function has the form $on(name, listener), with name being the name of an event and listener being a function, which is executed when the event occurs. $on can be bound to a specific $scope or also to a $rootScope, for cross-application event handling. In the file /src/client/app/blocks/router/router-helper.provider.js, the $on function is used in order to react to routing failures.

function handleRoutingErrors() {
  // Route cancellation:
  // On routing error, go to the welcome view.
  // Provide an exit clause if it tries to do it twice.
  $rootScope.$on('$stateChangeError',
    function(event, toState, toParams, fromState, fromParams, error) {
      if (handlingStateChangeError) {
        return;
      }
      stateCounts.errors++;
      handlingStateChangeError = true;
      var destination = (toState &&
        (toState.title || toState.name || toState.loadedTemplateUrl)) ||
        'unknown target';
      var msg = 'Error routing to ' + destination + '. ' +
        (error.data || '') + '. <br/>' + (error.statusText || '') +
        ': ' + (error.status || '');
      logger.warning(msg, [toState]);
      $location.path('/');
      }
  );
}

The code excerpt illustrates the event handling when a failure when changing the status occurs ('$stateChangeError'). The listener function with the event object event and certain predefined arguments are subsequently executed. In this case, the welcome view is switched to when a routing failure occurs and a failure notification is issued by the logger.

Recapitulation of the tutorial

This marks the end of the AngularJS Introductory Tutorial. The concluding status of the application is located, just like the two previous ones, on their own branch of the repository. The new elements of the second part of the tutorial include:

  • Template, controller, route, and modules for Watchmen view
  • Services from constant to provider
  • Data services and promises
  • Event handling

There are of course a wide variety of further components for the creation of own web applications that are made available by Angular, but that should be it for the introduction. The AngularJS tutorial should create a general overview of the Framework AngularJS and serve as an introduction for larger self-created web applications.

I hope that I have been able to rouse your interest for Angular, and that you will soon be able to start writing your own applications.

Share this article

Kommentare

  1. Visitors to school can find many questionnaires massage therapist of any age and nationality performing body to body massage in the city Bronx.

    Girls are able not only to give pleasure in this way, but also to demonstrate their other abilities to men of the stronger sex. Girls perform tantric a massage that will produce a gentleman a vivid impression.
    Prices for thai massage depends on qualification Beauties and the skills that she possesses. Before making a choice, carefully study the prices for services and customer feedback about the work of one or another masseur specialist. We are sure that the search for a real professional masseur will be crowned with success and you will be satisfied with the quality of our services. Specialists are skilled workers in their field and they will help you relax after a hard day.

    We have a massage parlour NJ. : nude massage

  2. Монтаж рамных строительных лесов осуществляют согласно правил по установки и подразделяют на несколько операций:
    • разметка основания, на котором будут размещена та или иная конструкция и ее планировка;
    • также строительные леса в минске
    • ознакомление рабочих с конструкцией, проведение инструктажа по ее сборке, креплению и по технике безопасности;
    • раскладка элементов конструкции по периметру установки;
    • размещение в необходимых местах подъемных механизмов, если они будут использоваться при сборке конструкции;
    • проверка каждого элемента, как и щитов настила на предмет выявления повреждений;
    • установка первого яруса;
    • сборка оставшихся ярусов с их креплением;
    • окончание работ с установкой молниеотводов и их заземлением.

  3. Добро пожаловать в колыбель истории – радушный Израиль. Вашему вниманию индивидуальная программа для продуктивного вояжа по стране на автомобиле экскурсовода.
    Ваш индивидуальный гид в Израиле Евгений Скальт позаботится о: впечетляющей программе экскурсий, крыше над головой и удобном ночлеге; вкусной и сытной пище; безопасности, легком трансфере.
    Скальт Евгений – личный гид в Израиле с большим опытом и копилкой знаний о жемчужене Ближнего Востока.
    Вашему вниманию также экскурсии в Палестину по официальной лицензии, все об оздоровлении организма в Израиле, возможностях лечения на Мертвом море и не только. Частный русский гид в Иерусалиме обеспечит продуктивное и незабываемое времяпровождение для тех, кто ищет знания и пищу для разума.
    Чай кошерный, настоящий израильский завтрак, сладкий цимес, аутентичные сувениры и колорит. Вы получите все от этой поездки!
    Ваш гид в Израиле индивидуальный Скальт Евгений ждет заявок на нужные даты, спешите забронировать время для экскурсий.
    Гид в Израиле – [url=http://www.исрагид.рф]сувениры из израиля что привезти[/url]

  4. • разметка основания, на котором будут размещена та или иная конструкция и ее планировка;
    • также строительные леса
    • ознакомление рабочих с конструкцией, проведение инструктажа по ее сборке, креплению и по технике безопасности;
    • раскладка элементов конструкции по периметру установки;
    • размещение в необходимых местах подъемных механизмов, если они будут использоваться при сборке конструкции;
    • проверка каждого элемента, как и щитов настила на предмет выявления повреждений;
    • установка первого яруса;

  5. Монтаж рамных строительных лесов осуществляют согласно правил по установки и подразделяют на несколько операций:
    • разметка основания, на котором будут размещена та или иная конструкция и ее планировка;
    • также строительные леса
    • ознакомление рабочих с конструкцией, проведение инструктажа по ее сборке, креплению и по технике безопасности;
    • раскладка элементов конструкции по периметру установки;
    • размещение в необходимых местах подъемных механизмов, если они будут использоваться при сборке конструкции;
    • проверка каждого элемента, как и щитов настила на предмет выявления повреждений;
    • установка первого яруса;

    • сборка оставшихся ярусов с их креплением;
    • окончание работ с установкой молниеотводов и их заземлением.

  6. • разметка основания, на котором будут размещена та или иная конструкция и ее планировка;
    • также леса строительные купить в минске цена
    • ознакомление рабочих с конструкцией, проведение инструктажа по ее сборке, креплению и по технике безопасности;
    • раскладка элементов конструкции по периметру установки;
    • размещение в необходимых местах подъемных механизмов, если они будут использоваться при сборке конструкции;
    • проверка каждого элемента, как и щитов настила на предмет выявления повреждений;
    • установка первого яруса;

  7. Ограждения ради лестниц, которые предлагает наша компания, отличаются надежностью, устойчивостью к различным агрессивным воздействиям и безупречным внешним видом. Кроме этого, присутствие их изготовлении учитываются всевозможные нормы и требования, актуальные чтобы данной группы изделий. Сложно представить постройка, в котором будут нет лестничные ограждения, наличие которых способствует увеличению удобства, безопасности близ передвижении. Отметим, что ныне ради изготовления конструкции предлагается достойный избрание элементов, с через которых удается скоро, выполнить монтаж конструкции, которая некоторый годы прослужит без потери первоначальных качеств. Осуществление, проектирование и монтаж лестниц и защита и стоят приемлемо, согласно сравнению с конструкциями из других материалов замечаться равных качественных характеристиках. Вконец нержавейка намного более популярный вещество – его безданнобеспошлинно возделывать, соединять с другими материалами, устанавливать и ухаживать после уже готовым изделием

Malte Kreutzfeldt
Software Development
Always with open eyes for new open source tools, frameworks and best practice development methods, he is allowing his IT horizons to expand with virtually no limits.