AngularJS Tutorial - 4

New version here

Refactoring

From Wikipedia, Refactoring:

Code refactoring is a "disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior",[1] undertaken in order to improve some of the nonfunctional attributes of the software. Advantages include improved code readability and reduced complexity to improve the maintainability of the source code, as well as a more expressive internal architecture or object model to improve extensibility.

Reordering files

IOC Friendly declarations

To ease the work of the AngularJS Inversion Of Control, and to allow a correct behaviour even when the files will be minified we will use a new format for the declaration of controllers, from

app.controller('ContactDetailController',function($scope, $modal, $http) {
    ...
});

to

app.controller('ContactDetailController',['$scope','$modal','$http',
    function($scope, $modal, $http) {
        ...
    }
]);

We now add square brackets, to add the "description" of the parameters, now the function call parameters could have any possible name, the important thing is that the names (the ones after the square brackets opening) remain the same.

The listController

All the logic inside the list could be easily fitted into a generic service. Services are singleton classes without any state. In general they can be considered as static functions.

We create a new file "assets/app/controllers.js" that will be included into the index.html, after app.js

We will declare the "listController".

app.controller('listController',['$scope', '$http',
    function($scope, $http){
        $http.get('api/contacts/?id='+itemId).success(function(data) {
            $scope.contacts = data.data;
        });
    }
]);

Inside the ContactListController whe could now use the new service, adding a dependency on the listService. We add the dependency on $controller. This service is needed to resolve references to other controllers.

We use it to initialize a new 'listController' so that it will have the same scope of the current controller.

app.controller('ContactsListController', ['$scope', '$controller',
    function($scope, $controller) {
        $controller('listController',{
            $scope:$scope
        });
    }
]);

The detailService

The same thing can be made for the detail. Note that the scope is NOT passed as a dependency, since it changes for every service usage. We simply moved everything from the controller to the service and adding a new detailControllerModal. This service will add to the scope the function "open".

app.controller('detailController',['$scope','$modal'
    function($scope,$modal){
        $scope.open = function (itemId) {
            $http.get('api/contacts/?id='+itemId).success(function(data) {
                $scope.contact = data.data;
                var modalInstance = $modal.open({
                    templateUrl: 'assets/contact/detail.html',
                    controller: 'detailControllerModal',
                    resolve: {
                        item: function () {return $scope.contact;}
                    }
                });
            });
        };
    }
]);

app.controller('detailControllerModal',function($scope, $modalInstance, item) {
    $scope.contact = item;
    $scope.ok = function () {
        $modalInstance.close();
    };
});

The New ContactDetailController

We should now adapt the ContactDetailController and remove the ModalContactDetailController. It will be heavily simplified:

app.controller('ContactDetailController', ['$scope','$controller',
    function($scope, $controller) {
        $controller('detailController',{
            $scope:$scope
        });
    }
]);

The Contact Repository Service

A new issue is now present, inside the generic detailService and listService we have references to the contacts. The first thing we can do is to create a new service that will handle all the calls to the contacts api. We will add it into the "assets/contact/module.js" file. Here we follow the same pattern as in detailService and listService.

Note that we use two variables: apiBase and http. This is because they will be always the same for all service calls.

app.factory('ContactsRepositoryService',['$http',
    function($http) {
        return {
            apiBase : 'api/contacts/',
            http :$http,
            
            getAll : function() {
                return this.http.get(this.apiBase);
            },
            
            getById : function(contactId) {
                return this.http.get(this.apiBase+'?id='+contactId);
            }
        }
    }
]);

We should now add a dependency on this service on the ContactDetailController, and pass it to the detailController

app.controller('ContactDetailController', ['$scope', '$controller', 'ContactsRepositoryService',
    function($scope, $controller, contactsRepositoryService) {
        $controller('detailController',{
            $scope:$scope,
            repository:contactsRepositoryService
        });
    }
]);

And we can remove the dependency on $http from the detailController and replace it with a direct call to the repository

app.controller('detailController',['$scope','$modal', 'repository',
    function($scope, $modal, repository) {      
        $scope.open = function (itemId) {
            repository.getById(itemId).success(function(data) {
                ...

The same can be done for the listController

app.controller('listController',['$scope', 'repository','identifier',
    function($scope, repository, identifier){
        repository.getAll().success(function(data) {
    ...

And on the ContactsListController

app.controller('ContactsListController', ['$scope', '$controller', 'ContactsRepositoryService',
    function($scope, $controller, contactsRepositoryService) {
        $controller('listController',{
            $scope:$scope,
            repository:contactsRepositoryService
        });
    }
]);

Last cleanup

We will remove references to the contact on the detailControllerModal, we will pass the identifier of the variable scope into the "assets/contact/detail.html". Note that for javascript "someObject.variable = value" is equivalent to "someObject['variable'] = value"

app.controller('detailControllerModal', ['$scope', '$modalInstance', 'item', 'identifier',
    function($scope, $modalInstance, item, identifier) {
        $scope[identifier] = item;
        $scope.ok = function () {
            $modalInstance.close();
        };
    }
]);

We should then pass this to the detailController that should pass it to the detailControllerModal

app.controller('detailController',['$scope','$modal', 'repository','identifier',
    function($scope, $modal, repository, identifier) {      
        $scope.open = function (itemId) {
            repository.getById(itemId).success(function(data) {
                $scope[identifier] = data.data;
                var modalInstance = $modal.open({
                    templateUrl: 'assets/'+identifier+'/detail.html',
                    controller: 'detailControllerModal',
                    resolve: {
                        item: function () {return $scope[identifier];},
                        identifier: function () {return identifier;}
                    }
                });
                ...

Then the ContactDetailController will pass the 'identifier' to the detailController

app.controller('ContactDetailController', ['$scope', '$controller', 'ContactsRepositoryService',
    function($scope, $controller, contactsRepositoryService) {
        $controller('detailController',{
            $scope:$scope,
            repository:contactsRepositoryService,
            identifier:'contact'
        });
    }
]);

The same can be made for the listController

app.controller('listController',['$scope', 'repository','identifier',
    function($scope, repository, identifier){
        repository.getAll().success(function(data) {
            $scope[identifier+"s"] = data.data;
        });
        ...

With the relative modifications on the ContactsListController

app.controller('ContactsListController', ['$scope', '$controller', 'ContactsRepositoryService',
    function($scope, $controller, contactsRepositoryService) {
        $controller('listController',{
            $scope:$scope,
            repository:contactsRepositoryService,
            identifier:'contact'
        });
    }
]);

Download the sample source


Last modified on: September 08, 2014