Хочешь выучить Angular?
Введение
По итога опроса разработчиков на StackOverflow за 2018 год, Angular — один из самых популярных фреймворков среди профессионалов. Изучите его, и ваши шансы получить работу в качестве высокооплачиваемого веб-разработчика повысятся.
Дэн Валин (Dan Wahlin) — Google Developer Expert, занимается проведением тренингов по архитектурой и разработке сервисов в крупнейших компаниях IT-индустрии, также создал огромное количество обучающих курсов на Udemy и Pluralsight. Он регулярно выступает по всему миру на конференциях, посвященных веб-разработке.
В этом курсе Дэн поможет вам создать ваше первое Angular-приложение с использованием TypeScript. Пройдите этот курс и пополните свою копилку знаний. Но для начала давайте разберемся из чего состоит наш курс.
1. Краткий экскурс
Во введении Дэн рассказывает о ключевых аспектах Angular, а также о своем прошлом опыте. Тут мы немного поближе знакомимся с автором перед тем, как приступим к написанию кода нашего приложения.
2. Обзор приложения
В этом видео мы получим некоторое представление о том, какое приложение мы напишем в будущем. Это нужно для того, чтобы мы могли сфокусироваться на ключевых блоках при использовании Angular. Нам необходимо будет создать приложение в виде личного кабинета, где будут отображаться данные клиента и его заказы, опираясь на такие понятия как компонент, модуль, сервис и роутинг. Также нам необходимо будет научиться использовать функционал, присущий каждом приложению, использованию сортировки и фильтрации.
3. Angular CLI
Эта часть посвящена изучению основ использования Angular CLI (command-line interface), основываясь на базовых командах:
Например, ng -new my-app-name создаст для нас новое пустое приложение, а используя ng -generate вы создадите отдельные его части (компоненты, сервисы, и тд.), ng build создаст что угодно для вас, а ng serve -o даже запустит сервер разработки.
4. Обзор структуры проекта
В этом блоке мы освоим базовые принципы работы с CLI и разберемся с конфигурированием таких файлов как tslint.json, tsconfig.json и protractor.conf.js.
5. Общая картина
В этом разделе мы разберемся с абстрактными аспектами разработки на основе компонентного подхода. Как наши приложения состоят из компонентов и какие механизмы используются для их взаимодействия. Также мы мы узнаем для чего нам TypeScript.
Мы составим некоторую ментальную модель о нашем коде при работе с Angular, чтобы в будущем нам было проще ориентироваться.
6. Компоненты и модули
Тут более детально, мы познакомимся с механизмом работы Angular.
Компоненты состоят из HTML-шаблонов и имеют селектор, поэтому вы можете использовать их таким образом:
<app-component></app-component>
Каждый файл компонента состоит:
- импорт(ы) — импорт ссылок на значения, экспортированные из внешнего модуля
- декоратор(ы) — функции-обёртки
- описание класса — шаблон для создания объекта (компонента)
Как только вы поймете как создать свой первый компонент, в дальнейшем вам станет намного проще разрабатывать свои приложения, поняв базовые механизмы.
7. AppComponent — главный компонент
Здесь мы создадим свой первый компонент HelloWorld. В этом разделе мы разберем каждый аспект работы Angular, поймем как наш код работает и что нужно, чтобы это работало в браузере.
Мы узнаем для чего нам нужно свойство selector, которое позволяет вызывать по имени в <app-root></app-root> наши компоненты в HTML коде.
Дэн разбирает по частям каждый аспект компонентов от рутового компонента до дочерних, и объясняет как их использовать, и как наши компоненты обрабатываются Angular, как это добавляется в app-module и наконец выводится на экран. И немного узнаем о привязке данных компонентов в шаблонах (data-binding), далее эта тема будет развита в следующих главах.
8. AppModule — главный модуль
Этот раздел посвящен обзору внутренней работе модулей в Angular. Узнаем для чего они нужны, познакомившись с главным модулем AppModule, а также что такое NgModule и BrowserModule.
9. Добавление Customers-компонента
Здесь мы научимся создавать компоненты с использованием CLI, также рассмотрим процесс создания компонентов и ручным способом. Рассмотрим примеры структуризации компонентов.
В качестве примера мы создадим тестовый компонентов для вывода списка покупателей. Этот компонент будет имитировать взаимодействие с API. Также на примере мы убедимся как модули помогают нам сохранить код организованным и структурированным.
10. Создание компонента списка клиентов
Этот раздел посвящается созданию компонента customers-list.component.ts. Компонент будет обычной HTML-таблицей, в которой будут отображены наши клиенты. Для этого мы просто создадим его и зарегистрируем в customers.module.ts, а далее мы легко можем использовать его где угодно в шаблонах:
<app-customers-list></app-customers-list>
Следующий шаг — заполнение данными таблицу.
11. Создание фильтра
Фильтры встречаются на практике чаще всего, поэтому здесь мы будем создавать с вами компонент filter-textbox.component.ts, который будет представлять из себя поле, по которому будут фильтроваться данные в таблице.
12. Создание общего модуля и интерфейсов
В этой главе мы поговорим для чего нам нужен shared.module.ts — модуль, в который мы помещаем общие компоненты или даже общие модули, которые мы хотим переиспользовать во всем приложении, а не только в области видимости модуля customers.
Также мы рассмотрим зачем нам нужны интерфейсы (они определяют свойства и методы, которые объект должен реализовать), и как они могут нам помочь для обеспечения качественной поддержки кода.
export interface CustomerInterface {
id: number;
name: string;
city: string;
orderTotal: number;
customerSince: any;
}
13. Data-binding
Связывание данных [(data-binding)] — это процесс, который устанавливает соединение между UI приложения и бизнес-логикой. Если свойства и всплывающие события установлены правильно, данные будут отражать изменения, когда это будет происходить. Это может также значить, что когда UI изменяется, лежащие в его основе данные тоже будут отражать эти изменения.
14. Погружение в Data-binding
Angular поддерживает механизм привязки, благодаря которому различные части шаблона могут быть связаны по значению.
Привязка свойств [property-binding]:
<input
type="text"
[value]="name"
/>
name — модель данных (объект или примитив)
@Component({
selector: 'my-app',
template: '<input type="text" [value]="name" />'
})
export class AppComponent {
name = 'Angular';
}
Привязка DOM-событий (event binding):
<button (click)="countClicked()">...</button>
15. Директивы и интерполяция
Интерполяцией называется процесс вычисления выражения, помещенного непосредственно в HTML-код шаблона, с целью получить его результат мгновенно.
Привязка данных в шаблоне (интерполяция):
<h1>Добро пожаловать {{name}}!</h1>
Но, что если мы хотим не просто выводить данные статично в шаблоне, а например, итерировать массивы или выполнять некоторые условия? Для этого применяются так называемые директивы. В нашем случае, по умолчанию существуют две встроенные директивы *ngFor, *ngIf, они контролируют рендеринг ваших шаблонов и позволяют вам писать, например, такой код:
И таким образом мы получаем такую таблицу данных:
16. Data Binding — Event Binding
Event Binding (привязка событий) является ключевым моментом, для того как мы будем обрабатывать наши события, будь это клик мыши или ее наведение. Здесь мы разберем пример с добавлением сортировки в таблице.
Давайте создадим в компоненте customer-list.component.ts метод:
sort(key: string) {
// Выполняем сортировку
}
В шаблоне customers-list.component.html нам достаточно указать:
<tr>
<th (click)="sort('name')">Name</th>
<th (click)="sort('city')">City</th>
<th (click)="sort('orderTotal')">Order Total</th>
</tr>
17. Data Binding — входные параметры компонента
Итак, мы знаем, что у нас есть некоторый массив данных people в нашем компоненте customers.component.ts, теперь нам необходимо передать отфильтрованный массив filteredCustomers из родительского в дочерний. Для этого мы используем декоратор Input, который помечает наше поле класса как входной параметр компонента.
@Input()
public get customers(): Customer[] {
return this._customers;
}public set customers(value: Customer[]) {
if (value) {
this.filteredCustomers = this._customers = value;
this.calculateOrders();
}
}
Теперь мы привязываем в шаблоне родительского компонента модель, тем самым передавая данные от родительского к дочернему.
<app-customers-list [customers]="people"></app-customers-list>
18. Работа с Pipes
Осталось несколько деталей, выглядящих странно, например “john” начинается с маленькой буквы, также отсутствует символ “$”, отображающего тип валюты в стоимости заказа.
С одной стороны это нормально, так как данные с API поступают к нам именно так, с другой стороны пользователям такие моменты необходимо интерфейсы делать удобными и максимально доступными. Мы можем пойти двумя путями:
- изменять данные напрямую (маппить) при запросе к API
- использовать встроенные функции Angular (pipe)
Простой пример:
{{ customer.name | uppercase }} // renders JOHN
{{ customer.name | titlecase }} // renders John
Иногда вам будет недостаточно встроенных фукнций Angular и вы захотите сделать свои pipe-ы. Тут мы также научимся писать свой собственный pipe на примере capitalize pipe.
19. Data Binding — добавление фильтрации
В этом разделе мы научимся делать фильтрацию на основе декоратора Output и EventEmitter, для создания всплывающих событий. Для этого нам нужно создать обработчик событий и привязываем его к нашему текстовому блоку:
<filter-textbox (changed)="filter($event)"></filter-textbox>
20. Сервисы
В этой главе мы рассмотрим сервисы и как с ними работать. Одной из сильных сторон Angular является то, что это фреймворк с полноценной расширяемой структурой для приложения, при этом данная структура будет обеспечивать вам не только встроенную поддержку управления состоянием всего приложения, но и добавляет возможность общаться разными объектам, компонентам общаться и взаимодействовать. Поскольку мы не хотим, чтобы компоненты знали слишком много о других компонентах и не слишком много обязанностей на себя брали (клиентская валидация, перерасчеты, маппинг) мы будем использовать сервисы.
Компоненты фокусируются на представлении данных и обработке пользовательских событий. Выполнение сложной бизнес логики следует делегировать это сервисам для обеспечения лучшей поддержки кода и его использования в дальнейшем (масштабирование и покрытие тестами).
21. Работа с HTTP
Итак, для того, чтобы создать сервис нам достаточно повесить декоратор Injectible, который является декоратором, позволяющим использовать ваш инстанс класса где угодно, благодаря Dependency Injection (внедрение зависимости) или DI для краткости. Это еще одна мощная фича Angular “из коробки”.
В дальнейшем мы будем использовать встроенный сервис HttpClient с RESTful API.
Для того, чтобы DI заработал, вам нужно всего лишь написать:
constructor(private http: HttpClient) { }
22. Встроенные HTTP методы
В этой части мы познакомимся с Observables (поток данных) из RxJS. RxJS — это реактивное расширение JavaScript, которое является отдельной библиотекой поставляемой с Angular.
Мы будем использовать Observables для работы с асинхронным кодом. Что дают Observables? В двух словах, они возвращают вычисления на которые можно подписаться, в последствии вы получаете данные из подписки. Когда данные возвращаются с сервера, происходит отписка.
23. Внедрение сервисов в компоненты
Теперь, когда мы знаем как и откуда получать данные, нам необходимо внедрить (заинжектить) наш сервис в один из компонентов. Теперь мы можем изменить получение this.people
в customers.component, убрав сложный код и вызывая вместо этого сервис.
Нам достаточно проинициализировать наш data.service
в app.module
и затем в customers.component мы можем:
import { DataService } from '../core/data.service';
Внедрить DataService прямо в конструктор компонента:
constructor(private dataService: DataService) {}
Часть 24. Подписка на Observable
Теперь мы можем использовать инстанс сервиса dataService
, вызывая getCustomers()
, и подписываться на поток данныхObservable<Customer[]
для получения данных.
Как видите все достаточно просто:
ngOnInit() {
this.title = 'Customers';
this.dataService.getCustomers()
.subscribe((customers: Customer[]) =>
this.people = customers);
}
Осталось разобраться с сервисом сортировки.
Часть 25: Использование SorterService
Сейчас, если мы кликнем на заголовок колонки и ничего не произойдет, в то время как мы хотели бы иметь сортировку по названию колонки. В данном разделе мы воспользуемся готовым сервисом. Как и с другими сервисами нам необходим сделать импорт в customers-list.component:
import { SorterService } from '../../core/sorter.service';
А затем мы внедряем SorterService в наш конструктор:
constructor(private sorterService: SorterService) {}
Наконец, мы использует написав метод sort():
sort(prop: string) {
this.sorterService.sort(this.filteredCustomers, prop);
}
Часть 26: Роутинг (маршрутизация)
В этой части мы научимся работать с роутингом в наших приложения, это один из главных моментов при написании современных приложений. Когда мы создаете приложение на Angular, вы хотите показать разные компоненты, с которыми взаимодействует ваши пользователи. В нашем примере, когда пользователь кликает на ссылку “Заказы”, он хочет посмотреть свои заказы в отдельной странице. Роутинг — один из способов достижения этого.
Роутинг связывает специфичный URL адресной строки браузера, с вашими компонентами или модулями вашего Angular приложения.
Роутинг — это URL-маршруты, адреса которые привязаны к компонентам или модулям, в этом плане у вас нет необходимости в сложной логике (show / hide), чтобы отобразить что-то или скрыть на странице.
Часть 27. Создание маршрутизирующего модуля
Для начала создадим app-routing.module.
Главная цель app-routing.module — обернуть маршруты в массив:
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: '/customers'},
{ path: '**', redirectTo: '/customers' }
];
Три ключевых параметра:
- path —адрес маршрута, куда переходит пользователь, path: ‘’— означает корневой путь. path: ‘**’ — случайный переход, обычно он помещается последним и должен охватывать случаи для любого маршрута, который не указан в вашем массиве.
- pathMatch — как на самом деле должен выглядеть маршрут для отображения определенного компонента.
- redirectTo — это то, куда мы отправляем пользователя. В нашем случае мы отправляем пользователей по адресу ‘/customers’.
Часть 28. Использование router-outlet
Чтобы использовать маршрутизацию при работе с Angular нам необходимо в нашем шаблоне app.component мы заменяем <app-customers></app-customers> на <router-outlet></router-outlet>.
В конечном счете, мы просто убираем жесткие (захардкоженные) названия компонентов, взамен имеем динамическую подстановку на основе выбранного маршрута.
Когда мы выбираем маршрут, компонент, связанный с ним, появляется на месте <router-outlet></router-outlet>. Все просто.
Часть 29.Добавляем новый маршрут
К примеру, мы хотим связать /customer
адрес с нашим компонентомcustomers.component
. Прописав настройку:
const routes: Routes = [
{ path: 'customers', component: CustomersComponent }
];
Перейдя по адресу “customers” мы увидим следующее:
Часть 30. Добавление параметризированных маршрутов
Пришло время отображать наши заказы. Для того, чтобы мы могли кликать на конкретного клиента и отобразить данные заказа, связанные с ним мы должны передавать динамические данные в маршрут. Мы можем достигнуть этого, передавая параметр:
const routes: Routes = [
{ path: 'orders/:id', component: OrdersComponent}
];
Обратите внимание на синтаксис /:id
. В роутинге символ ‘:’ является индикатором того, что он будет динамически заменен, а значение id — просто переменная, и может быть названа как угодно — :country или :book.
Часть 31. Получение параметров из роутинга
В предыдущей главе мы добавили путь ‘orders/:id’ и теперь компонент orders.component необходимо должен как-то перехватить как-то этот id и отобразить заказы конкретного клиента. Чтобы это сделать, необходимо следующее:
let id = this.route.paramMap.get('id');
Преимущество в том, что мы можем подписаться на обновления объекта paramMap и получать от него уведомления, когда любые данные в параметре id изменятся. В данном случае, id нужен нам один раз. Поэтому мы используем snapshot для этого:
let id = this.route.snapshot.paramMap.get('id')
snapshot — просто текущее состояние URL, на тот момент, когда он был вызван для получения. Но стоит учитывать, что параметры всегда строковые, а для получения заказа из DataService нужно число. Мы можем конвертировать через parseInt(), или использовать конвертировать с использованием утиной типизации:
let id = +this.route.snapshot.paramMap.get('id')
Часть 32. Связываем маршрут и ссылки
Последнее, что нам нужно сделать — добавить ссылку на имя клиента, чтобы по клику открывались заказы. Мы знаем, что появляться наши компоненты будут на месте <router-outlet></router-outlet и теперь нам просто нужно сказать нашему приложению, что мы хотим отобразить orders.component когда мы переходим к /orders/:id.
Это можно сделать добавляя ссылку на имя клиента в customers-list.component.html, где мы отображаем все данные.
<a [routerLink]="['/orders', customer.id]">
{{ customer.name | capitalize }}
</a>
И мы видим заказы!
Стоп. А как вернуться назад? Мы можем нажать кнопку “назад” в браузере, но приятнее сделать это через ссылку. Добавим это в customers-list.component.html в самый конец.
<a routerLink="/customers">View All Customers</a>
Часть 33. Обзор курса
Прекрасно, у нас есть готовое приложение!