Мы в команде Web Development компании Itransition уже имеем хороший опыт разработки на Angular 2 и хотим им поделиться.
Эта статья ориентирована на тех крутых ребят, которые разрабатывают на Angular и подумывают перейти на вторую его версию.
Стоит ли начинать новый проект на Angular 2?
Однозначно – да! И вот почему:
- Это грамотно и тщательно спроектированный, высокопроизводительный фреймворк;
- С более низким порогом вхождения по сравнению с первой его версией;
- С хорошей документацией с большим количеством практических примеров.
Начало нового проекта
Наконец-то глоток свежего воздуха, полная свобода, можно начать всё с чистого листа. Хорошенько проработать архитектуру, структуру данных, компоненты, различные абстракции, составить некий словарь терминов приложения и так далее. Чтобы всё было красиво.
Свобода действий
Но с первой версией Angular не всё так просто. Нужно чтобы всё придуманное ложилось на реалии фреймворка, его модули и сервисы строго определенного типа. Нельзя просто так взять и аккуратно создать некий класс или компонент, который будет делать что-то важное. Нужно решить, чем этот компонент будет с точки зрения фреймворка? Каким типом сервиса: «value», «constant» или всё же «factory»? А может быть сервис типа «service»? Ведь он создаёт объект с оператором new, вроде бы это то, что нужно. А вдруг синглтона будет недостаточно? И эти вопросы возникают практически всегда, работая с Angular, в подобных ситуациях, и нет на них однозначного ответа.
Отсутствие подобного рода ограничений со стороны фреймворка, на мой взгляд, сильное преимуществом Angular 2. Можно использовать любую удобную модульную систему, как угодно называть и подключать произвольный код.
Генератор кода
Далее для начала работы над проектом необходимо:
- создать файловую структуру приложения,
- наладить работу с шаблонами,
- наладить работу со стилями, препроцессором,
- настроить сборку для разработки, отладки, продакшена,
- настроить процесс тестирования,
- …
Со второй версией фреймворка мы получаем инструмент командной строки, с которым можно генерировать приложения, модули, компоненты, директивы, сервисы, фильтры (pipe – новое их название), запускать тесты, проверку кода и т.д. И для выполнения описанного выше необходимо выполнить одну команду:
ng new app-name
Будет создана вся необходимая инфраструктура в лучшем на данный момент исполнении. Сразу можно приступать к работе. Ничего лишнего.
Команда может принимать дополнительные аргументы. Например, если планируется использовать CSS препроцессор Stylus:
ng new app-name --style=styl
Будет автоматически настроена компиляция и сборка стилей с учётом выбранного препроцессора.
TypeScript
Сгенерированный код приложения будет использовать TypeScript, который пугает многих, скорее всего попросту из-за ошибочных представлений о нём. На самом деле это тот же JavaScript (ECMAScript 6), но с некоторыми приятными и полезными примочками:
- интерфейсы,
- типизация,
- перечисления (Enum),
- модификаторы (public, private, static),
- декораторы (@).
Всё это позволяет писать более стабильный и красивый код, избавляет от надобности повсеместно использовать скверный JSDoc.
Начав использовать TypeScript, хочется писать только на нём, и уже не понимаешь, как можно было быть таким грешным – не использовать его раньше?
Компоненты
В Angular 2 нет контроллеров, только компоненты. Создать новый можно таким образом:
ng generate component playground/player
Эта команда создаст директорию player в playground с минимально необходимым кодом компонента:
- файл реализации,
- файл шаблона,
- файл стилей с расширением используемого CSS препроцессора,
- файл юнит-тестов.
Никаких копипастов — источника зла и ошибок! Сгенерированный код реализации компонента будет такой:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-player',
templateUrl: './player.component.html',
styleUrls: ['./player.component.styl']
})
export class PlayerComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
@ + Component здесь – и есть пример декоратора. В конечном счёте, это простая функция, которая получает в качестве аргумента конструктор, определённый ниже, и изменяет его с учётом описанной конфигурации, в данном случае это:
- selector – определяет, как будет называться элемент компонента в шаблонах приложения (<app-player></app-player>),
- templateUrl – путь к файлу шаблона текущего компонента,
- styleUrls – массив путей к файлам стилей компонента.
Помимо перечисленных параметров существуют и другие, которые позволяют писать код шаблона и стили в этом же файле, конфигурировать инкапсуляцию стилей этого компонента и т.д.
Two-way binding
Ключевая фишка Angular — two-way binding. Как она реализована в Angular 2?
<app-player [(position)]="playerPosition"></app-player>
Такая запись в шаблоне передаст значение свойства playerPosition текущего компонента и будет изменять его при изменении свойства position внутри компонента player. Это и есть two-way binding.
Но почему именно такая довольна странная запись?
В Angular 2 появился новый синтаксис, который позволяет передавать значения свойств дочерним компонентам (one-way binding). Использует он квадратные скобки:
<app-player [position]="playerPosition"></app-player>
И можно подписываться на события, возникающие в дочерних компонентах. Используются круглые скобки:
<app-player (positionChange)="onPositionChange($event)"></app-player>
Такая запись подразумевает, что в компоненте player есть свойство positionChange, которое является экземпляром класса EventEmitter. И когда в компоненте player вызывается this.positionChange.emit(newValue), выполняется код onPositionChange($event), указанный в шаблоне. $event будет содержать значение newValue.
Собственно, так в Angular 2 и реализуется two-way binding:
- передача исходного значения свойства,
- подписка на событие с названием «название свойства внутри дочернего компонента» + «Change»,
- изменение свойства в родительском компоненте при появлении события.
А запись [(position)]=«playerPosition» является лишь синтаксическим сахаром, которая делает всё описанное автоматически и экономит время на постройку дома, посадку дерева и выращивание сына.
Благодаря этой реализации в Angular 2 нет вотчеров, которые ранее являлись источников многих проблем с производительностью. Теперь всё более естественно.
Принципы SOLID
В нашей команде мы активно используем принципы SOLID, которые делают поддержку и дальнейшую разработку приложения более эффективным и приятным процессом.
Angular 2 решает множество проблем, связанных с высоким уровнем связанности кода, новой мощной реализацией Dependency Injection и возможностью абстрагироваться от реализаций различных взаимосвязанных компонентов, используя интерфейсы (TypeScript).
Например, можно написать такую конструкцию:
class SomeComponent {
constructor(public someService: SomeService) {}
}
И при создании экземпляра этого компонента автоматически будет создан экземпляр сервиса SomeService и передан в конструктор SomeComponent. Это очень сильно снижает уровень связанности и позволяет тестировать их в отдельности друг от друга.
Так же, запись public someService (TypeScript) делает этот сервис доступным внутри экземпляра класса с помощью ключевого слова this (this.someService).
Валидация форм
Валидация на основе шаблонов, которая была раньше, осталась, но появилась новая дополнительная реализация. Её конфигурация полностью происходит в JavaScript, что позволяет формировать набор правил динамически, создавать реюзабельные валидаторы и полностью управлять процессом, в том числе фильтровать пользовательский ввод.
Пример реализации формы ввода нового пароля с проверкой его сложности и подтверждением:
let passwordControl = new FormControl('', [
Validators.required,
CustomValidators.complexPassword,
]);
let passwordConfirmControl = new FormControl('', [
Validators.required,
]);
this.formGroup = new FormGroup({
password: passwordControl,
passwordConfirm: passwordConfirmControl,
}, CustomValidators.match(passwordControl, passwordConfirmControl));
Валидатор (Validators.required и подобные) – простая функция, в которую передаётся значение и которая возвращает null или объект с описанием ошибки.
В шаблоне формы нужно указать formGroup:
<form [formGroup]="formGroup">
Полям ввода нужно указать соответствующие названия контролов formGroup:
<input type="password" formControlName="password">
<input type="password" formControlName="passwordConfirm">
И всё. Валидна ли форма, все ошибки и состояния можно получать через объект formGroup, который будет обновляться при каждом взаимодействии пользователя с ней.
Роутинг
Роутинг похож на прежний, но с некоторыми приятными улучшениями. Например, если какой-нибудь громоздкий модуль приложения используется редко, можно подгружать его динамически:
{
path: 'profile',
loadChildren: 'app/profile/prodile.module#ProfileModule'
}
Обработка всех запросов, начинающихся с /profile (/profile/photo, /profile/orders, /profile/orders/:id), будет передана ProfileModule, который будет загружен раз при первой необходимости.
Низкий порог вхождения
В начале статьи говорилось о низком пороге вхождения. Несмотря на всю мощь Angular 2, это действительно так.
На мой взгляд, это из-за того, что:
- максимально используются возможности JavaScript,
- многие вещи реализованы более логичным и ожидаемым образом,
- качество подсказок и авто-завершения на высшем уровне (благодаря TypeScript),
- имеется командная строка для генерации всего необходимого,
- хорошая документация.
Но это не говорит о лёгкости его усвоения человеком не знающего JavaScript.
Заключение
Ещё много можно написать про новую версию фреймворка, сравнить основные сервисы, разобраться, как происходит работа с асинхронными операциями (Zone.js), упомянуть о реактивном программировании (Rx.js) и так далее. Но это уже будет не статья, а целая книга.
Хочется сказать, что Angular 2 – очень профессиональный и качественный фреймворк. Работая с ним, чувствуешь, что его писали люди, которые имеют большой опыт практической разработки и понимаешь, что больше не хочется писать на первой его версии.
Успехов!
Автор: chuev