Kontynuując cykl o AngularJS, dziś pragnę omówić routing , serwisy i co się z tym wiąże –  dependency injection(DI).

Routing

Angular JS, jak i wiele innych frameworków MVW (Model – view – whatever) posiada funkcjonalność parsowania adresu podanego w przeglądarce i na podstawie niego wyświetla odpowiednią treść.

angular.module('MySuperbApp', ['MySuperbApp.Services']).
config(['$routeProvider', function ($routeProvider) {
    $routeProvider.when('/home', { templateUrl: 'Partials/home.html', controller: 'HomeController' });
    $routeProvider.when('/employee/:id', { templateUrl: 'Partials/employee.html', controller: 'EmployeeController' });
    $routeProvider.when('/controllerless', { templateUrl: 'Partials/controllerless.html' });
    $routeProvider.otherwise({ redirectTo: '/home' });
}]);

Już wyjaśniam, co tu się dzieje.

W linii #1 definiujemy główny moduł naszej aplikacji o nazwie MySuperbApp i mówimy, że aplikacja jest zależna od MySuperbApp.Services, który powinien być zarejestrowany pojemniku DI (za chwilę więcej o tym).

W następnej linii konfigurujemy jego odpowiednie elementy. W tym przypadku żądamy $routeProvider (moduły zaczynające się na $ to zazwyczaj moduły zawarte w AngularJS) i ustawiamy go w podane funkcji, która przyjmuje go na argumencie. W tym miejscu ustawiamy przy pomocy metody when() schemat url, który załadować odpowiedni widok z odpowiednimi operacjami.

Drugim argumentem when() jest obiekt z odpowiednimi polami, które mówią, skąd ma załadować kod HTML odpowiedzialny za widok, który kontroler ma zostać zbindowany do tego widoku.

W kolejnej linii możemy znaleźć :id, które mówi, że jest to zmienna i będzie ona dostarczona do kontrolera w $routeParams. Dalej widzimy ścieżkę do widoku bez kontrolera.
W

arto zawsze na końcu powiedzieć providerowi, co ma się stać, jeśli wprowadzono ścieżkę, której nie zdefiniowaliśmy. Do tego służy metoda otherwise – podajemy jej obiekt z polem redirectTo, w którym podajemy route wcześniej zdefiniowany.

Serwisy

Aby zachować strukturę i ład każdej aplikacji, musimy pomyśleć o tym, jak chcemy rozmieścić odpowiednie funkcjonalności. Wkładanie logiki do kontrolera może być łatwe i szybkie, ale powoduje to koszmar w testowaniu i na dłuższą metę bałagan.

Aby to rozwiązać, Angular posiada mechanikę serwisów i wstrzykiwania ich w odpowiednie miejsca na żądanie(DI).
Powiedzmy, że gdy wchodzimy z na stronę /employee to chcemy mieć dostęp do danych, z którego załadujemy odpowiedniego pracownika.
Założmy, że dane będziemy pobierać przez AwesomeService

angular.module("MySuperbApp.Services", []).
factory("AwesomeService", function ($http) {
    return {
        address: '/api/employees',
        getEmployeeById: function (id, success) {
            $http.get({url:this.address, data:{id:id}}).success(function(response){
                success(response.data);
            });
        }
    }
});

Tak wygląda definicja tworzenia przykładowego serwisu.
Tworzymy nowy moduł MySuperbApp.Services, który posiadać będzie fabrykę obiektów, przez które będziemy robić zapytania.
$http.get() zwraca obietnicę (Promises – o nich kiedy indziej). Taka obietnica posiada zazwyczaj trzy metody, które przyjmują funkcję (callback) na operacje, które się udało, nie udało i postęp. Zanim pracownika z serwisu, rozpakowujemy go z obiektu odpowiedzi i wyłojemy callback z nim jako argument.

function EmployeeController($scope, $routeparams, AwesomeService){
    AwesomeService.getEmployeeById($routeparams.id, function(employee){
         $scope.employee = employee;
    });
}

W naszym kontrolerze, żądamy $routeparams, aby dowiedzieć się jakie ID przekazano, AwesomeService, który jest tworzony i podany do kontrolera. Wywołujemy odpowiednią metodę i podajemy funkcję, która przypisze pracownika do $scope, który efektywnie zbinduje go do widoku.

Dzięki temu podejściu możemy łatwo ten serwis podmienić w celu przetestowania kontrolera. W samym serwisie możemy podmienić moduł $http, aby móc poprawnie przetestować serwis.