11.25.2016

Коротко о логике авторизации и роутинге используя Angular 2

Для решения данного вопроса вполне достаточно документации на официальном сайте Angular 2

Однако!

В разделе описано много больше информации и если английский текст вы воспринимаете медленно, то данная небольшая статья поможет быстро вникнуть в суть происходящего.

А,  я лишь хочу обрисовать общую логику процесса и некоторые моменты авторизации так, чтобы вы (и я) могли максимально быстро настроить роутинг для своего проекта.

Простая навигация по ссылкам не самый интересный вариант с точки зрения реального применения. В реальности мы имеем приватную и публичную часть приложения. А иногда, только приватную, которая доступна только после авторизации. Весь роутинг после авторизации будет представлять собой самый простой вид роутинга т.е. написал путь и привязал к нему компонент который будет отображаться.

Мне удобно представлять "картину целиком" прежде чем приступать к работе.

Итак! У нас будет главный роутинг приложения, и модуль роутинга для приватной части. В роутинге приватной части используются дочерние пути. Дочерние пути будут защищаться при помощи сервисов авторизации. Авторизироваться можно будет при помощи компонета входа (login). Сервисы авторизации авторизируют пользователя и запоминают состояние.

Итого:
  • Главный роутинг
  • Дочерний роутинг приватной части
  • Сервисы авторизации
  • Компонент входа (форма входа)

Теперь логика процесса авторизации. Пользователь заходит на главную страницу сайта, главный роутнг отправляет его на приватную часть сайта. Пути приватной части сайта обрабатывает роутинг приватной части. Для проверки нужно ли отображать дочерние пути он использует сервис авторизации. Если сервис подтвердит, что у пользователя есть права то роутинг обработает запрос к дочерним путям. Если нет, то будет вызван роут для несуществующего пути (404 страница). Если роутинг приватной части не обрабатывает 404 ошибку то, это попытается сделать главный роутинг. Если и он не справится то выпадет ошибка:
Unhandled promise rejection
Error: Permission denied to access property "rejection"
Поэтому сервис авторизации в случае если пользователь не авторизован должен сам направить его на нужный маршрут по которому запрос обработает модуль входа. А так же не забывайте ставить обработку несуществующего пути:
{
    path: "**",
    component: NotFoundComponent
}

Теперь по сути и с кусками кода.
Пользователь заходи на главную страницу и это обрабатывает главный роутинг:
const routes: Routes = [
    {
        path: '',
        redirectTo: '/panel',
        pathMatch: 'full'    },
    {
        path: 'login',
        component: LoginComponent
    },
    {
        path: "**",
        component: NotFoundComponent
    }
];
Так же не забываейте, что для роутинга приватной части нужен свой тег
<router-outlet></router-outlet>который будет находиться в главном компоненте приватной части, так же как и для всего приложения он находится в app.component.

Пользователь перенаправляется на приватную часть сайта, это обрабатывает роутинг приватной части:
const routes: Routes = [
    {
        path: 'panel',
        canActivate: [AuthGuard],
        component: AdminComponent,
        children: [
            {
                path: '',
                canActivateChild: [AuthGuard],
                children: [
                    {
                        path: '',
                        component: DashboardComponent,
                    },
                    {
                        path: 'dashboard',
                        component: DashboardComponent
                    },
                    {
                        path: 'logs',
                        component: LogsComponent
                    },
                    {
                        path: 'configs',
                        component: ConfigsComponent
                    }
                ]
            }
        ]
    }
];
Для проверки авторизации роутинг использует AuthGuard подключенный в роуты такой строчкой:
canActivate: [AuthGuard],
Сам сервис авторизации должен реальзовывать два метода canActivate и canActivateChild. Выглядит следующим образом:
import { Injectable }       from '@angular/core';
import {
    CanActivate, Router,
    ActivatedRouteSnapshot,
    RouterStateSnapshot,
    CanActivateChild
}                           from '@angular/router';
import { AuthService }      from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {

    canActivate(): boolean {
        return this.checkLogin();
    }

    canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.canActivate();
    }
    checkLogin(url: string): boolean {
        // используем сервис this.authService 
        // чтобы проверить авторизован ли пользователь 
        // выполнить редирект на компонент входа можно тут
    }
}
Для выполнения редиректа нужно подключать модуль Router
import { Router } from '@angular/router';
Добавлять его в класс который будет выполнять редирект
constructor(
    private authService: AuthService,
    private router: Router
) {}
И вызывать редирект следующим образом:
this.router.navigate(['/login']);
Компонент входа (/login) использует сервис авторизации authService, который может иметь любую логику на ваше усмотрение, например в таком виде:
import { Injectable } from '@angular/core';

@Injectable()
export class AuthService {
    isLoggedIn: boolean = false;

    // store the URL so we can redirect after logging in    redirectUrl: string;

    login(): Observable<boolean> {
        return this.isLoggedIn = true;
    }

    logout(): void {
        this.isLoggedIn = false;
    }
}

Остальное и более подробно читайте в официальной документации:
https://angular.io/docs/ts/latest/guide/router.html
Angular 2 Router:
https://vsavkin.com/angular-2-router-d9e30599f9ea

11.18.2016

Angular 2 Архитектура

Как только вышла стабильная версия Angular 2 я решил, что пришло время уделить время переводу статьи по архитектуре данного фреймворка. Но, что-то я отвлекся от перевода на некоторе время и вот уже версия 2.2 вышла в свет. Конечно, "некоторое" время было потрачено в том числе и на эксперименты, прочтение и осмысление базового гайда и тура героев, но оно того стоило. Так что получился перевод с моими дополнениями или пояснениями.

В конце статьи смотрите видео по всему фрейворку. Очень познавательное!

Все кто заглядывал на официальный сайт фреймворка в разделе архитектура видели ее схематическое изображение, а как говорится повторение - мать учения!



Можно выделить 8 основных архитектурных блоков в приложениях на Angular 2:

    Модули (Modules)


    Приложения на Angular являются модульными, а сам фрейм-ворк предоставляет свою систему модульности - NgModules. В терминах JavaScript NgModule, именно без окончания "s" это обычная функция, функция декоратор. Так как Angular 2 использует TypeScript то правильно будет назвать NgModule - функцией декоратором.

    Каждое приложение написанное на Angular имеет как минимум один модуль. Внутри модуля находятся компоненты приложения. 

    Например, список товаров или героев может быть одним компонентом, а детальная информация о герое, которая открывается по нажатию на конкретного героя будет вторым компонентом.
      Ситуация с одним модулем характерна для простых приложений тогда как в больших приложениях имеется много модулей объединяющих в себе определенный набор возможностей.

      Модули в Angular, это class обернутый в декоратор @NgModule.

      Декораторы модифицируют поведение пользовательских классов. Функция-декоратор, используя переданный ей класс и метаданные создает его экземпляр, а так же устанавливает дополнительные связи требуемые самому фрейм-ворку. В итоге, написав свой класс обернув его в декоратор и импортировав его для дальнейшего использования мы импортируем не тот класс который мы написали, а функцию которую вернул декоратор Angular.

      NgModule - это функция декоратор, которая принимает один объект метаданных. Наиболее важными свойствами являются:
      • declarations - Принимает массив компонентов приложения. По мимо компонентов можно передавать свои директивы и трубы (под трубами можно понимать потоки событий реализованных с помощью EventEmitter) .
      • exports - С помощью этого свойства мы можем сделать текущий модуль доступным (видимым) для использования снаружи другими модулями. Т.е. через exports сделали видимым, а через imports подключили текущий модуль к другому.
      • imports - Массив модулей которые используются в текущем модуле. Например, при работе с формами мы подключаем соответствующий модуль самого фрейм-ворка и подключаем его к своему модулю через указание его в массиве imports.
      • providers - Массив сервисов или услуг, которые предоставляют компонентам расширенную функциональность. Типичным примером сервисов может быть функция логирования или функции выполняющие запросы данных по сети (httpRequest).
      • bootstrap - Главный компонент приложения (корневой). Можно сказать, что это точка входа в приложение. Только для корневого модуля устанавливается свойство само загрузки.
      import { NgModule }      from '@angular/core';
      import { BrowserModule } from '@angular/platform-browser';
      @NgModule({
        imports:      [ BrowserModule ],
        providers:    [ Logger ],
        declarations: [ AppComponent ],
        exports:      [ AppComponent ],
        bootstrap:    [ AppComponent ]
      })
      export class AppModule { }
       

      Angular модули vs JavaScript модули

      Как уже было сказано ранее. Модули в Angular, - это декорированные классы. В свою очередь JavaScript имеет свою модульную структуру, которая не как не связанна с Angular.

      В JavaScript каждый файл представляет собой модуль. В модуле может содержаться разное количество объектов, классов или функций, каждый из которых может быть помечен специальным словом export, а другие модули используют операторы import для доступа к ранее экспортированным объектам.


      import { NgModule }     from '@angular/core';
      import { AppComponent } from './app.component';
      export class AppModule { }

      Библиотеки Angular (Angular Libraries)

      Angular представляет собой набор модулей JavaScript. Официальная документация предлагает читателю думать о них как о библиотеке модулей. Если перейти на репозиторий Angular то можно найти все модули которые входят в состав фрейм-ворка.
      https://github.com/angular/angular/tree/master/modules/%40angular


      Модулей довольно много для того, чтоб запомнить их все сразу. Первые модули с которыми сталкивается разработчик, это модуль ядра core, модуль для платформы platform-browse
      r и модуль для работы с формами forms.

      Каждая библиотека начинается с префикса @angular

      Вы устанавливаете их с помощью npm менеджера пакетов и импортируете нужные вам модули в свое приложение с помощью оператора import.


      Например:

      import { Component } from '@angular/core';
      import { BrowserModule } from '@angular/platform-browser';


      После того как вы импортировали нужный вам функционал остается привязать его к классам которые вы будете писать в своем приложении. Иначе как ваши классы смогут пользоваться магией Angular? Конечно при помощи декораторов, которые расширят ваши классы и добавят им функции из импортированных вами модулей. Для этого достаточно написать следующий код:


      imports:      [ BrowserModule ],

      Надеюсь вы еще помните где нужно писать данную строчку? Конечно в метаданных декоратора класса. 

      import { NgModule }      from '@angular/core';
      import { BrowserModule } from '@angular/platform-browser';
      @NgModule({
        imports:      [ BrowserModule ],
        providers:    [ Logger ],
        declarations: [ AppComponent ],
        exports:      [ AppComponent ],
        bootstrap:    [ AppComponent ]
      })
      export class AppModule { }

      Компоненты (Components)

      Компонент представляет собой какую-либо визуальную часть интерфейса.

      Например:
      • Корень приложения с навигационными ссылками - appComponent
      • Список героев - HeroListComponent
      • Редактор героев - HeroEditorComponent
      В классе компонента вы определяете его логику, а взаимодействие с интерфейсом (view) происходит по средствам API свойств и методов.

      Например, HtroListComponent имеет свойство heroes, которое содержит массив героев полученный от службы (Служба или сервис - это функция возвращающая массив героев, обычно внутри функции происходит ajax запрос на получение списка героев). HeroListComponent так же имеет метод selectHero(), который устанавливает свойство selectedHero, в момент нажатия пользователем на кнопку, для того, чтобы выбрать героя из списка.

      export class HeroListComponent implements OnInit {
        heroes: Hero[];
        selectedHero: Hero;
      
        constructor(private service: HeroService) { }
      
        ngOnInit() {
          this.heroes = this.service.getHeroes();
        }
      
        selectHero(hero: Hero) { this.selectedHero = hero; }
      }

      У каждой директивы, компонента есть жизненный цикл т.е. время от начала взаимодействия с компонентом до окончания этого взаимодействия. Предположим, пользователь открывает список героев с этого начинается жизненный цикл компонента HeroListComponent. Дальше пользователь хочет отредактировать определенного героя и на этом закончить. Когда пользователь выберет героя и отправиться редактировать его, то это можно считать окончанием жизненного цикла для компонента HeroListComponent. 

      Но как пользователь сможет выбрать требуемого героя если не произошла загрузка списка героем? Одним из решений может быть создание кнопки Load, которую должен будет нажать сам пользователь. Но есть и другой способ!

      Angular предоставляет lifecycle hook, это события которые происходят в течении жизненного цикла компонента. Для того, чтобы ими воспользоваться нужно лишь добавить в свой класс метод со специальным именем, дальше Angular сам вызовет данный метод, когда придет время. 

      В примере выше таким хуком жизненного цикла (lifecycle hook) был метод ngOnInit.


      Шаблоны (Templates)

      Описывая компонент интерфейса вы добавляете к нему шаблон для отображения в интерфейсе. Шаблон представляет собой html который определяет как компонент будет выглядеть в интерфейсе.

      Шаблон выглядит как обычный HTML, за исключением нескольких отличий. Вот шаблон для нашего списка героя компонента:
       
       
      <h2>Hero List</h2>
       
      <p><i>Pick a hero from the list</i></p>
      <ul>
      <li *ngFor="let hero of heroes" (click)="selectHero(hero)">
          {{hero.name}}
        </li>
      </ul>
       
      <hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
      
      
      Хотя этот шаблон использует типичные HTML-элементы, такие как <h2> и <р>, он также имеет некоторые отличия. Код, как *ngFor, {{hero.name}}, (click), [hero], а <hero-detail> использует синтаксис шаблонов Angular.

      В последней строке шаблона тег <hero-detail> является пользовательским элементом, который представляет собой новый компонент, HeroDetailComponent.

      HeroDetailsComponent  является еще одним компонентом по мимо HeroListComponent и представляет факты о конкретном герое выбранном из списка героев. Так же HeroDetailsComponent является потомком HeroListComponent.

      Обратите внимание на то, как органично <hero-detail> компонент вписывается в обычные html элементы. Пользовательские шаблоны легко смешиваются с обычным html.


      Мета данные (Metadata)

      Мета данные рассказывают Angular как обрабатывать class.

      Оглядываясь назад на код HeroListComponent, вы можете увидеть, что это просто класс. Внутри класса нет ничего чтобы относилось к самому Angular.

      На самом деле, HeroListComponent это просто класс, и он не является компонентом, пока вы не скажете Angular об этом.

      Сказать, что HeroListComponent является компонентом Angular, можно прикрепив метаданные к классу.

      В TypeScript, прикреплении метаданные можно с помощью декоратора. Вот некоторые метаданные для HeroListComponent:


      @Component({
        moduleId: module.id,
        selector:    'hero-list',
        templateUrl: 'hero-list.component.html',
        providers:  [ HeroService ]
      })
      export class HeroListComponent implements OnInit {
      /* . . . */
      }

      Вот декоратор @Component, который идентифицирует класс непосредственно под ним как класс компонента.

      @Component Декоратор принимает необходимые объекты конфигурации с информацией, Angular необходимо создать и представить компонент и его вид.

      Вот некоторые из возможных вариантов конфигурации @Component:

      • moduleId - устанавливает источник базового адреса (module.id) для относительно модуля URL-адресов, таких как templateUrl.
      • selector - CSS селектор который определяет в какой элемент будет вставлен содержимое компонента т.е. шаблон компонента.
      • templateUrl - относительный адрес для получения шаблона компонента
      • providers - массив провайдеров зависимости, инъекций услуг, которые нужны компоненту. Это один из способов сказать, что Angular конструктор компонента требует HeroService, для того чтобы получить список и отобразить список героев.
      Метаданные в @Component определяют, где получить основные строительные блоки, заданные для компонента.

      Шаблон, метаданных и компонент вместе описывают вид.

      Так же существуют другие декораторы.
      @Injectable, @Input и @output - это наиболее используемые виды декораторов.

      Архитектурный идея Angular2 состоит в том, что вы должны добавить метаданные в ваш код используя декораторы, таким образом Angular2 поймет, что нужно делать. 



      Связывание данных (Data binding)

      Без фреймворка вам бы пришлось вручную обновлять данные в HTML и так же вручную обрабатывать действия пользователей. Написание такой логики вручную утомляет, итоговый код трудно читать и он подвержен ошибкам, любой опытный программист на jQuery может это подтвердить ;)


      Связывание данных в Angular, это механизм координации данных между JavaScript (Компонентом) кодом и Html (Шаблоном) кодом. Добавляя в html разметку связывания мы определяем как соединить компонент и шаблон.

      Как показано на схеме, существует четыре формы данных синтаксиса связывания. Каждая форма имеет направление - к DOM, из DOM, или в обоих направлениях.



      Пример, HeroListComponent шаблон имеет три формы:

      <li>{{hero.name}}</li>
      <hero-detail [hero]="selectedHero"></hero-detail>
      <li (click)="selectHero(hero)"></li>

      1. {{hero.name}} значит, что значение свойства hero.name будет взято из компонента HeroListComponent и отображено внутри тегов <li>. Это одностороннее связывание при котором изменение значения свойства hero.name приведет к изменению отображаемого значения.
      2. [hero] Со вторым случаем все немного сложнее, так было сказано: каждая форма имеет направление - к DOM, из DOM, или в обоих направлениях. А о направлении от компонента к компоненту нечего сказано не было. Поэтому, нужно сказать, что коммуникация происходит между компонентами. Почему именно так? Потому что свойство hero компонента HeroDetailsComponent связывается со значением selectedHero компонента HeroListComponent. Таким образом происходит коммуникация между дочерним и родительским компонентом. Как только изменится значение свойства selectedHero, это значение попадет в свойство hero компонента HeroDetailsComponent и последний сможет отобразить детальную информацию о герое. Отмечу, что такой код сам по себе работать не будет так как свойство hero внутри компонента HeroDetailsComponent должно быть помечено специальным декоратором @Input.
      3. (click) обработчик события onClick.
      Четвертым видом связывания является двухстороннее связывание, совмещающее в себе привязку по событию и привязку к свойству. Для двухстороннего связывания используется директива ngModel. Вот пример из шаблона HeroDetailComponent.

      <input [(ngModel)]="hero.name">

      При изменении значения пользователем в поле ввода изменится значение свойства hero.name, так же и при изменении значения свойства результат сразу отобразится в html.

      Один раз за цикл событий JavaScript Angular обрабатывает все привязки данных, от корня дерева компонентов приложения до всех дочерних компонентов.



      Привязка данных играет важную роль в общении между шаблоном и ее компонентом.


       





      Привязка данных также имеет важное значение для связи между родительскими и дочерними компонентами.




       

      Директивы (Directives)

      Шаблоны в Angular являются динамическими. Angular обрабатывает их и превращает в DOM в соответсвии с инструкциями указанными в директивах.



      Директива - это класс и ее метаданные. В TypeScript, используется декоратор @Directive для класса директивы.







      Компонент является директивой с шаблоном, @Component декоратор, это фактически @Directive декоратор расширеный функциями ориентированными на работу с шаблонами.

      На самом деле компонент технически является директивой, но компоненты настолько важны в приложениях построенных на Angular, что в данном обзоре архитектуры мы отделяем компоненты от директив.

      Существует два вида директив:
      • Структурные
      • И директивы атрибутов
      Они, как правило, появляются в пределах элемента тега так же как атрибуты, иногда по имени, но чаще в качестве цели присвоения или привязки.

      Структурные директивы изменяют макет путем добавления, удаления и замены элементов в DOM.

      Пример шаблона использующего две встроенные структурные директивы:


      <li *ngFor="let hero of heroes"></li>
      <hero-detail *ngIf="selectedHero"></hero-detail>
       
      • *ngFor указывает Angular, что для каждого элемента массива heroes нужно создать отдельный тег <li>. 
      • *ngIf указывает Angular, что добовлять элемент HeroDetail нужно только в том случае если свойство selectedHero возвращает правду.

      Директивы атрибутов изменяют внешний вид или поведение существующего элемента. В шаблонах они выглядят как обычные HTML атрибуты, отсюда и название.

      Директива ngModel, которая реализует двустороннюю привязку данных, является примером директивы атрибута. Директива ngModel изменяет поведение существующего элемента, устанавливает для свойства input.value значение из hero.name, а так реагирует на изменения пользователем значения input.value используя события.

      <input [(ngModel)]="hero.name">

      Angular имеет несколько директив, которые либо изменяют структуру макета (например, ngSwitch) или модифицируют аспекты элементов и компонентов DOM (например, ngStyle и ngClass).

      Конечно, вы можете также написать свои собственные директивы. Такие компоненты, как HeroListComponent один вид пользовательской директивы.


      Сервисы (Services) 

      Сервис, это очень широкое понятие, но по сути сервис является функцией, которая удовлетворяет некоторые потребности. 

      Обычно сервис представляет собой класс с узко и четко обозначенной целю, например получать данные с сервера или вычислять координаты фигуры svg. Сервис должен делать, что-то конкретное и делать это хорошо!

      Примеры сервисов:

      •      сервис логирования
      •      сервис получения данных с удаленного сервера
      •      сервис сообщение о багах
      •      калькулятор
      •      конфигурация приложения
      Angular не содержит внутри себя сервисов как  таковых т.е. в них нет нечего специфического с точки зрения фреймворка. Для сервисов не существует специального декоратора и их не нужно регистрировать как например компоненты. Однако, чтобы использовать один и тот же сервис в разных компонентах используется декоратор @Injectable(). Об этом будет сказано в следующем разделе статьи.

      Тем не менее, сервисы имеют большое значение для любого Angular приложения. Компоненты являются основными потребителями сервисов.

      Вот пример класса
      сервиса логирования, который регистрируется в консоли браузера .
      export class Logger {
        log(msg: any)   { console.log(msg); }
        error(msg: any) { console.error(msg); }
        warn(msg: any)  { console.warn(msg); }
      }
      Еще пример сервиса который завист от двух других сервисов Logger и BackendService.
      export class HeroService {
        private heroes: Hero[] = [];

        constructor(
          private backend: BackendService,
          private logger: Logger) { }

        getHeroes() {
          this.backend.getAll(Hero).then( (heroes: Hero[]) => {
            this.logger.log(`Fetched ${heroes.length} heroes.`);
            this.heroes.push(...heroes); // fill cache
          });
          return this.heroes;
        }
      }
      Сервисы просто повсюду!

      Цель сервисов вынести часть кода из компонентов. Компоненты не должны сами получать данные с сервера, проверять пользовательский ввод или выводить сообщения в консоль. Такие задачи они делегируют сервисам.

      Компонент является связующим звеном между шаблоном (видом) и логикой приложения. Хороший компонент представляет собой свойства и методы для связывания данных. Все остальное он делегирует сервисам.


      Angular не будет жаловаться если вы напишите компонент в  3000 строк без использования сервисов. Но, Angular действительно помогает нам правильно организовать код отделяя логику приложения в отдельные сервисы, с целью получить к ним доступ из любого компонента используя внедрения зависимостей (dependency injection). 


      Внедрение зависимостей (Dependency injection) 

      Внедрение зависимостей, это способ создания класса с уже готовыми зависимостями, которые ему нужны. Большинство зависимостей, - это сервисы. Angular использует внедрение зависимостей, чтобы отделить создание класса сервиса от кода компонента, таким образом новые компоненты сразу получают те сервисы которые им необходимы. Забегая вперед, скажу, что такой подход призван упростить тестирование.

      Angular может определить какие сервисы нужны компоненты глядя на типы его параметров в конструкторе. На самом деле, это делает транспилятор TypeScript. В этом легко убедиться если открыть данные код в разделе playground на сайте TypeScript

      class HeroService { }

      class HeroesComponent {
          constructor(
              private heroService: HeroService
          ) {}
      }


      Перед тем как создать компонет Angular спрашивает инжектор какие сервисы требуются компоненту. 

      Инжектор имеет контейнер с экземплярами класса сервисов созданых ранее. Если запрошенные экзепляр сервиса не найден в контейнере, то инжектор сделает данный экземпляр сервиса и отправит его в контейнер перед тем как вернуть экзепляр сервиса запросившему его компоненту. Когда все запрошенные сервисы были помещены в контейнер и отправлены в нужные компоненты, Angular помещает экземпляры сервисов в качестве аргументов компонента и вызывает конструктор. Это и есть внедрение зависимостей.

      Процесс внедрения зависимости HeroService выглядит так:


      Если инжектор не имеет экземпляра HeroService в контейнер, как он должен сделать его экземпляр?
      Нужно зарегистрировать сервис у поставщика (provider). Поставщик может создать или вернуть экземпляр услуги, часто он позвращает сам класс сервиса.

      Можно зарегистрировать поставщиков в модулях или в компонентах.

      В общем случае, добавьте поставщиков в корневой модуль таким образом, экземпляр службы станет доступен везде.

      providers: [
        BackendService,
        HeroService,
        Logger
      ],
       
      Если же вам нужен новый экземпляр сервиса в каком-то компоненте то можно зарегистрировать его именно в декораторе @Component нужного комонента.
       @Component({
        moduleId: module.id,
        selector:    'hero-list',
        templateUrl: 'hero-list.component.html',
        providers:  [ HeroService ]
      })
      Помните, что регистрация на уровне компонентов означает, что вы получите новый экземпляр сервиса с каждым новым экземпляром этого компонента.
      Внедрение зависимостей подключается в Angular и используются повсеместно.

      И в заключении всего хочу поделиться видео лекцией по Angular 2: