Angular 2 in JavaScript - lanit-tercom-school/analyzeme GitHub Wiki

#Состав Angular 2 приложения#

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

Архитектура Angular 2 приложения

Архитектурная диаграмма выделяет 8 основных составляющих Angular 2 приложения:

  1. Модуль
  2. Компонент
  3. Шаблон
  4. Мета-данные
  5. Привязка данных
  6. Сервис
  7. Директива
  8. Инъекция зависимостей

##Модуль

Модуль

По задумке, Angular 2 приложения - модульные.

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

Обычный модуль - связный блок кода, реализующий одну функциональность. Модуль экспортирует какую-либо цельную сущность (например, сервис или компонент).

app.component.js

//Пример реализации модуля 'app' на JavaScript
(function(app) {
    //Экспортируемая сущность
    app.SomeService = 
        ng.core.Class({
            constructor : function() {}
        });
})(window.app || (window.app = {}));

вверх


##Компонент##

Компонент

Мы прописываем логику приложения, поддерживающую представление(view) компонента, внутри класса. Класс взаимодействует с представлением через API свойств и методов.

HeroListComponent, например, может иметь свойство heroes, возвращающее массив героев, полученный от сервиса. Компонент может иметь метод onSelect(), который устанавливает свойство selectedHero, когда пользователь щелкает по герою из этого списка. Демонстрация на plnkr.co

hero-list.component.js

(function(app) {
  /* global ng */
  function Hero(id, name) {
    this.id = id;
    this.name = name;
  }
  app.HeroListComponent =
    ng.core.Class({
      constructor: function() {
        this.selectedHero = null;
        this.heroes = [
                { "id": 11, "name": "Mr. Nice" },
                { "id": 12, "name": "Narco" },
                { "id": 13, "name": "Bombasto" },
                { "id": 14, "name": "Celeritas" },
                { "id": 15, "name": "Magneta" },
                { "id": 16, "name": "RubberMan" },
                { "id": 17, "name": "Dynama" },
                { "id": 18, "name": "Dr IQ" },
                { "id": 19, "name": "Magma" },
                { "id": 20, "name": "Tornado" }
              ];
      },
      onSelect : function(hero) {
          this.selectedHero = hero;
      }
    });
})(window.app || (window.app = {}));

Также, есть такая вещь, как Lifecycle hooks, позволяющая обрабатывать события компонента(такие, как инициализация, изменение данных и т.п.).

вверх


##Шаблон## Шаблон

Мы описываем вид компонента с помощью шаблона. Шаблон - HTML-форма, говорящая Angular, как отрисовывать компонент. Шаблон выглядит, как обычный HTML, но с некоторыми добавлениями:

hero-list.component.html

<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<div *ngFor="#hero of heroes" (click)="selectHero(hero)">
  {{hero.name}}
</div>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>

Все, что не есть HTML теги - элементы синтаксиса шаблонов Angular 2. Обратим внимание на <hero-detail> тэг - кастомный тэг, маркирующий HeroDetailComponent.

вверх


##Мета-данные##

Мета-данные

Мета-данные говорят Angular как обрабатывать класс.

На самом деле, HeroListComponent - не Angular 2 компонент, а просто класс. И он не станет компонентом, пока мы не скажем об этом Angular, прицепив мета-данные к нему.

hero-list.component.js

app.HeroListComponent =
    ng.core.Component({
      "selector" : 'hero-list',
      "templateUrl" : "app/templates/hero-list.component.html",
      "styleUrls" : ["app/css/hero-list.component.css"],
      "directives" : [app.HeroDetailComponent],
      "providers" :   [app.HeroService]
    })
    .Class({...});

ng.core.Component - декоратор, помечающий класс как класс-компонент. Он принимает конфигурационный объект, содержащий информацию, нужную Angular для создания компонента и его представления.

Перечислим несколько возможных пунктов конфигурационного объекта:

  • selector - css селектор, говорящий Angular создать и вставить компонент везде, где будет найден тег <hero-list> в родительском HTML.
  • templateUrl - адрес файла шаблона компонента.
  • styleUrls - массив адресов файлов стилей компонента.
  • directives - массив компонент, используемых в данной компоненте.
  • providers - массив поставщиков инъекций зависимостей(dependency injection providers) для сервисов, используемых в компоненте. Это способ сказать Angular, что конструктор нашего компонента требует HeroService с помощью которого получит список героев для показа в списке.

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

вверх


##Привязка данных##

Привязка данных

Angular поддерживает привязку данных - механизм, соединяющий данные шаблона и данные компонента. Мы добавляем разметку привязки данных в HTML шаблон, чтобы указать Angular как соединять шаблон и компонент.

Есть четыре вида привязки данных, каждый имеет направление привязки - в DOM, от DOM, или в обоих направлениях - как показано стрелочками в диаграмме.

hero-list.component.html (выдержка)

<div *ngFor="#hero of heroes" (click)="selectHero(hero)">
  {{hero.name}}
</div>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
  • Интерполяция отображает значение свойства hero.name компонента внутри тегов <div>.

  • Привязка свойства [hero] передает selectedHero от родительского компонента HeroListComponent в свойство hero дочернего компонента HeroDetailComponent.

  • Привязка события (click) вызывает метод selectHero компонента, когда пользователь щелкает по имени героя.

Двусторонняя привязка данных - это важный четвертый вид, который объединяет привязку свойства и событий в одной записи, используя ngModel директиву. Демонстрация на plnkr.co

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

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

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

вверх


##Сервис##

Cервис

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

Почти все может быть сервисом. Обычно, сервис - это класс с узким, хорошо определенным назначением. Он делает что-то определенное и делает это хорошо :)

Например:

  • сервис логирования
  • сервис данных
  • калькулятор налогов
  • настройки приложения

В Angular нет никаких специальных маркеров для сервисов(как для компонента, например), но все же важно их выделять.

(function(app) {
  /* global ng */
  // Пример сервиса героев
  app.HeroService = 
    ng.core.Class({
      constructor : function() {},
      getHeroes : function() {
        return Promise.resolve(app.HEROES);
      },
      getHeroesSlowly : function() {
        return new Promise(resolve =>
          setTimeout(() => resolve(app.HEROES), 2000) // 2 seconds
        );
      }
    });
})(window.app || (window.app = {}));

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

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

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

вверх


##Директива## ##Инъекция зависимостей##

Инъекция зависимостей

Инъекция зависимостей - это способ предоставить новый инстанс класса с полностью сформированными зависимостями, которые ему требуются. Большинство зависимостей - это сервисы. Angular использует инъекцию зависимостей, чтобы предоставлять новые компоненты с сервисами, которые им требуются.

// Пример инъекции зависимости
// При каждом создании компонента будет создаваться новый инстанс сервиса.
// Если мы хотим взять сервис из родительского компонента,
// то мы просто не прописываем его в свойстве "providers" дочернего компонента.
ng.core.Component({
      ...
      "providers" : [Service]// не пишем, если хотим сервис из родительского компонента
    })
    .Class({
      constructor : [Service, function(service) {...}]
    });

Когда Angular создает компонент, сначала он запрашивает у Injector сервисы, требующиеся компоненту.

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

main.js

// Пример инъекции сервиса на самом верхнем уровне
(function(app) {
  /* global ng */
  document.addEventListener('DOMContentLoaded', function() {
    ng.platform.browser.bootstrap(app.AppComponent, [app.HeroService]);
  });
})(window.app || (window.app = {}));

Больше о dependency injection

вверх


⚠️ **GitHub.com Fallback** ⚠️