Изучаем angularjs на пяти практических примерах

Какую версию Angular мне лучше использовать?

На данный момент существует две популярные версии Angular. Версия 1 доступна на https://angularjs.org/  и является обновлённой версией того Angular, что был представлен Miško и его командой в 2011 году. Другая популярная версия теперь называется просто Angular и доступна на https://angular.io/. Современный Angular – это полностью переделанная версия для новых браузеров, рабочих процессов и платформ разработки.

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

Пример NGRX

  1. Установка библиотеки
  2. Создание структуры папок для хранилища
  3. Создание хранилища и начальных значений
  4. Создание действий (Actions)
  5. Создание редукторов (Reducers)
  6. Создание эффектов (Effects)
  7. Создание селекторов (Selectors)
  8. Конечная настройка
  9. Использование хранилища в компонентах

Создание хранилища и начальных значений

  1. Мы создаем и экспортируем интерфейс со структурой пользовательской среды.
  2. Мы делаем то же самое с начальным пользовательским состоянием, которое реализует недавно созданный интерфейс.
  1. Состояние приложения содержит состояние пользователя и конфигурации, а также состояние маршрутизатора.
  2. Потом задаем начальное состояние приложения.
  3. Наконец, экспортирует функцию, чтобы получить начальное состояние (мы будем использовать его позже).

Создание Действий

  1. Мы экспортируем Enum, содержащий определение для типов действий. Таким образом, мы избегаем использования и повторения строк для использования типов действий, процесс, который может легко порождаться ошибками.
  2. Потом мы создаем и экспортируем класс для каждого из ваших действий. Все они должны реализовать интерфейс Action из ngrx. Наконец, мы устанавливаем тип в одно из значений перечислений, и если вам нужно полезное содержимое для действия, вы добавляете его в конструктор класса.
  3. Наконец, мы экспортируем тип, содержащий наши классы действий. Это обеспечит нам проверку типов, которую мы можем использовать, например, в наших редукторах.

Создание Редукторов

  1. Объявление редуктора получает состояние и, в этом случае, действия пользователя и возвращает IUserState.
  2. Используя оператор switch, мы генерируем наблюдения для каждого возможного типа действия.
  3. Каждый случай возвращает новый объект, который является результатом слияния старого состояния и нового значения.
  4. Все редукторы имеют результат по умолчанию, который просто возвращает состояние без каких-либо изменений.

Добавим Эффекты

  1. Мы объявляем наши пользовательские эффекты с помощью инъекционного декоратора.
  2. Мы объявляем наши эффекты, используя декоратор эффектов, предоставленный ngrx/Effects.
  3. Используя действия, предоставленные ngrx / Effects, мы собираемся запустить конвейер нашего оператора для этого эффекта.
  4. Следующим шагом является установка типа действия эффекта с помощью оператора ofType.
  5. Следующие части представляют собой операторы rxjs, которые мы используем для получения того, что нам нужно (у нас уже есть ссылка на документацию по rxjs в этой статье).
  6. Наконец, в последнем операторе Effect отправит еще одно действие.
  7. В конструкторе мы внедряем сервисы, которые мы собираемся использовать, действия для ngrx / Effects, и в этом случае также хранилище (учтите, что это демо, и мы получаем выбранного пользователя из списка пользователей в наше хранилище).

Итоговая настройка

  1. Мы импортируем наши редукторы и предоставляем их в forRoot модуля хранилища.
  2. Мы импортируем наши эффекты и предоставляем их внутри массива в модуль forRoot эффектов.
  3. Мы устанавливаем конфигурацию для модуля состояния маршрутизатора, предоставляющего маршрутизатор stateKey.
  4. И мы добавляем инструменты разработчика магазина, если среда не является производственной.

Использование хранилища в некоторых компонентах

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

  1. Мы добавляем хранилище в наш app.component.
  2. Мы устанавливаем для свойства компонента значение селектора в конфигурации, потому что хотим отобразить часть этой информации в HTML.
  3. В onInit мы отправляем действие, чтобы получить конфигурацию.
  1. Подобно тому, как мы управляем конфигурацией, мы собираемся получить список пользователей. Сначала мы внедряем хранилище в компонент пользователя.
  2. На onInit мы отправляем действие, чтобы получить пользователей.
  3. Мы создаем свойство для компонента и присваиваем ему список пользователей, используя селектор списка пользователей.

Давайте посмотрим на компонент пользовательского контейнера:

Иерархическое внедрение зависимостей

Я уже упоминал, что Angular2-приложение — это дерево компонентов. И у каждого компонента есть свой роутер и инжектор. Таким образом дерево инжекторов и компонентов параллельны.

Какие плюсы даёт такой подход? Например, теперь легко можно настроить один и тот же сервис по-разному, в зависимости от компонента, в который он внедряется. При этом, можно не бояться как-то повлиять на другие компоненты выше или на том же уровне иерархии, так как они будут использовать другие экземпляры того же сервиса. Компонент теперь не зависит от того, как был сконфигурирован какой-то сервис. Если компоненту нужен отдельный экземпляр сервиса, он просто добавляет его в секцию .

Заметьте, в коде сервисов нет нигде упоминания о провайдерах. Мы не можем зарегистрировать какой-то провайдер в рамках какого-нибудь сервиса. Если в сервис внедряется другой сервис, его провайдер регистрируется в каком-то компоненте. Мы не сможем внедрить сервис без компонента. Таким образом, ещё раз подчёркивается компонентный подход всего фреймворка: сервисный слой стал вторичным, на первое место вышли компоненты. И у каждого компонента могут быть свои личные изолированные от других экземпляры сервисов.

Разумеется, ангуляр не создаёт для каждого компонента отдельный инжектор. Это было бы довольно неэффективно. Но в любом случае, каждый компонент имеет свой инжектор, даже если делит его с другим компонентом.

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

Вот пример того, как работают инжекторы с иерархией:

Тут 2 сервиса и 2 компонента. В родительском компоненте регистрируются 2 сервиса ( и ), в дочернем — только . Если понажимать на кнопки , то одинаковые массивы будут только у , так как дочерний компонент, не найдя у себя зависимость использует инстанс, полученный из родительского компонента. А вот экземпляр у дочернего компонента создастся новый. Поэтому дочерний компонент будет писать в свой экземпляр, а родительский — в свой.

Означает ли это, что сервисы в Angular2 не являются синглтонами? В конкретном инжекторе не может быть больше 1-го инстанса сервиса. Но так как самих инжекторов может быть несколько, то и разных инстансов одного и того же сервиса во всём приложении может быть больше одного.

Что такое AngularJS

Последнее обновление: 26.04.2017

AngularJS представляет собой opensource JavaScript-фреймворк, использующий шаблон MVC. Собственно использование MVC является его одной из отличительных особенностей.

Для описания интерфейса используется декларативное программирование, а бизнес-логика отделена от кода интерфейса, что позволяет улучшить тестируемость и
расширяемость приложений.

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

Кроме того, AngularJS поддерживает такие функциональности, как Ajax, управление структорой DOM, анимация, шаблоны, маршрутизация и так далее. Мощь фреймворка,
наличие богатого функционала во многом повлияла на то, что он находит свое применение во все большем количестве веб-приложений, являясь на данный
момент наверное одним из самых популярных javascript-фреймворков.

На момент написания данного руководства последней версией фреймворка была версия 1.6.4.

Начало работы c AngularJS

При загрузке zip-пакета мы найдем в нем кроме самой библиотеки (angular.js) еще ряд дополнительных файлов и их минимизированные версии:

  • angular-touch.js: предоставляет поддержку событий сенсорного экрана

  • angular-animate.js: предоставляет функциональность анимации

  • angular-aria.js: предоставляет поддержку aria-атрибутов (accesible rich internet application)

  • angular-mocks.js: предоставляет mock-объекты для юнит-тестирования

  • angular-route.js: обеспечивает механизм маршрутизации

  • angular-sanitize.js: предоставляет функционал для управления потенциально опасным контентом (javascript, html)

  • angular-cookies.js: обеспечивает функционал для управления куками

  • angular-loader.js: используется для загрузки angularjs-скриптов

  • angular-messages.js: предоставляет функционал для вывода сообщений

  • angular-resource.js: обеспечивает функциональность для работы с ресурсами

  • Папка i18n: содержит js-файлы для разных локалей

Из всех загруженных скриптов в архиве нас будет интересовать прежде всего файл angular.min.js

Теперь собственно создадим приложение. Оно будет стандартным HelloWorld. Код html-страницы будет следующим:

<!doctype html>
<html ng-app>
<head>
<meta charset="utf-8">
</head>
<body>
<label>Введите имя:</label>
<input type="text" ng-model="name" placeholder="Введите имя">

<h1>Добро пожаловать `name`!</h1>
     
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</body>
</html>

Первое, наверное, что бросается в глаза — это новые атрибуты, добавленные к стандартным тегам html (в данном случае и
). Эти атрибуты являются директивами фреймворка AngularJS. Благодаря их встраиванию фреймворк позволяет добавить элементам определенное поведение.

Конкретно в данном случае директива объявляет элемент корневым для всего приложения, а
директива указывает модель «name», к которой будет привязано значение элемента input. И при изменении текста в элементе input, модель «name»
также будет динамически изменять свое значение.

Чуть ниже в элементе выводится значение этой модели.

И мы можем просто открыть данный файл в браузере и протестировать его работу.

Вперед

Загрязнение ngOnInit

— один из самых важных хуков жизненного цикла в компонентах Angular. Он используется для инициализации данных, настройки слушателей, создания соединений и т.д. Однако в некоторых случаях он перегружает хук жизненного цикла:

Рассмотрим этот компонент. У него есть всего два жизненных цикла. Однако метод выглядит ужасно. Он подписывается на различные события изменения формы, потоки , а также загружает много данных. Он состоит из 40 строк кода, а если включить опущенное здесь содержимое обратных вызовов , то мы получим более 100 строк, что противоречит даже самым мягким рекомендациям. Чтобы добраться до других методов, помимо придется прокручивать весь этот беспорядочный кусок кода (или закрывать/открывать его при необходимости). Более того, из-за большого количества смешанных концепций и задач усложняется поиск элементов внутри самого метода .

Теперь рассмотрим исправленную версию того же компонента:

Логика компонента сохраняется, меняется лишь способ организации кода. Теперь метод вызывает три различных метода для загрузки начальных данных из сервисов, установки прослушивателей изменения формы и прослушивателей событий DOM (при необходимости). Эти изменения упрощают чтение компонента с первого раза (прочтите — разберитесь, с чего он начинается, и если вам нужны подробности реализации, просмотрите соответствующие методы). Поиск источника ошибок также упрощается: если прослушиватели форм работают неправильно, перейдите в и т.д.

Не загрязняйте метод  — разделите его на несколько частей!

Подводные камни Angular Universal

Не трогайте DOM

Когда мы начали тестировать компоненты нашего магазина с помощью , нам пришлось потратить некоторое время, чтобы понять, почему наш сервер падает при запуске без вывода серверной страницы. Например, у нас есть компонент Session Flow component, который отслеживает активность пользователя во время сессии (перемещения пользователя, клики, рефферер, информация об устройстве пользователя и т.д.). После поиска информации в issues на мы поняли, что в нет обертки над DOM.

.

Если вы склонируете этот Angular Universal стартер и откроете browser.module.ts вы увидите, что в массиве разработчики Universal предоставляют два:

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

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

Если вы хотите активно взаимодействовать с элементами DOM, используйте сервисы API, такие как, или .

Правильный роутинг

Поскольку сервер отражает наше приложение, у нас была проблема с роутингом.

Поэтому не забудьте добавить тот же роутинг на сервере что и на клиенте. Например, у нас есть такие роуты на клиенте:

Тогда нужно создать файл с массивом роутов сервера. Корневой маршрут можно не добавлять:

Наконец, добавьте роуты на сервер:

Пререндеринг стартовой страницы

Одной из наиболее важных особенностей является пререндеринг. Из исследования Kissmetrics следует, что 47% потребителей ожидают, что веб-страница загрузится за 2 секунды или даже менее

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

Давайте подробнее рассмотрим, что это такое и как его использовать.

Когда пользователь открывает URL нашего магазина, немедленно возвращает предварительно подготовленную HTML страничку с контентом, а уже затем затем начинает загружать все приложение в фоновом режиме. Как только приложение полностью загрузится, подменяет изначальную страницу нашим приложением. Вы спросите, что будет, если пользователь начнет взаимодействовать со страницей до загрузки приложения? Не беспокойтесь, библиотека Preboot.js запишет все события, которые выполнит пользователь и после загрузки приложения выполнит их уже в приложении.

Чтобы включить пререндеринг, просто добавьте в конфигурацию сервера :

Добавление мета-тегов

В случае SEO-ориентированного сайта добавление мета-тегов очень важно. Например, у нас есть , который представляет страницу определенного продукта

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

Команда создала сервис angular2-meta, чтобы легко манипулировать мета-тегами. Вставьте мета-сервис в ваш компонент и несколько строк кода добавлят мета-теги в вашу страницу:

В следующей версии Angular этот сервис будет перемещен в@angular/platform-server

Кэширование данных

запускает ваш XHR запрос дважды: один на сервере, а другой при загрузке приложения магазина.

Но зачем нам нужно запрашивать данные на сервере дважды? PatricJs создал пример, как сделать Http-запрос на сервере один раз и закэшировать полученные данные для клиента. Посмотреть исходный код примера можно . Чтобы использовать его заинжекте и вызовите метод для выполнения http-вызовов с кешированием:

Настроить всё вместе

Отлично, мы создали всё, что нужно нашему хранилищу, но нам пока не хватает одной вещи — собрать всё воедино. Я собираюсь сделать это в модуле , но вы можете применить то же самое в модуле вашего приложения.

Давайте посмотрим модуль приложения:

Файл с описанием модуля приложения

Давайте перечислим то, что необходимо для настройки нашего хранилища:

  1. Мы импортируем наши редукторы, и передаём их в метод модуля хранилища.
  2. Мы импортируем наши эффекты, и передаём их внутри массива в метод модуля эффектов.
  3. Мы передаём настройки для модуля состояния маршрутизатора .
  4. И мы добавляем инструменты разработчика хранилища , если запущена среда разработки.

Первые два шага необходимы, в то время как шаги 3 и 4 я настоятельно рекомендую, но они не являются обязательными.

Теперь мы наконец закончили … и можем использовать хранилище в наших компонентах!

История разработки [ править | править код ]

AngularJS разработан в 2009 году Мишко Хевери и Адамом Абронсом в Brat Tech LLC как программное обеспечение позади сервиса хранения JSON-данных, измеряющихся мегабайтами, для облегчения разработки корпоративных приложений. Сервис располагался на домене «GetAngular.com» и имел нескольких зарегистрированных пользователей, прежде чем они решили отказаться от идеи бизнеса и выпустить Angular как библиотеку с открытым исходным кодом.

Абронс покинул проект, но Хевери, работающий в Google, продолжает развивать и поддерживать библиотеку с другими сотрудниками Google Игорем Минаром и Войта Джином.

В марте 2014 было объявлено о начале разработки AngularJS 2.0 . Новая версия писалась с нуля на TypeScript и очень сильно отличалась от предыдущей, поэтому позже было решено развивать её как отдельный фреймворк с названием Angular. Angular 2 был выпущен 15 сентября 2016 года , тогда как первая версия продолжила развиваться отдельно как AngularJS.

Дочерние маршруты

Последнее обновление: 13.11.2020

В прошлых темах рассматривалась работа с обычными маршрутами. То есть система маршрутизации выбирает один из компонентов на основе url и
помещает его в главный компонент AppComponent. Но что если выбранный компонент сам должен принимать в качестве внутреннего содержимого какой-то другой компонент в зависимости от
запроса. В этому случае нам надо использовать дочерние маршруты (child route).

К примеру, возьмем проект из прошлых тем:

Добавим в папку src/app два простеньких компонента. Вначале добавим файл item.details.component.ts:

import { Component} from '@angular/core';
 
@Component({
    selector: 'item-details',
    template: `<h3>Информация о товаре</h3>`
})
export class ItemDetailsComponent{}

И также добавим второй файл item.stat.component.ts:

import { Component} from '@angular/core';
 
@Component({
    selector: 'item-stat',
    template: `<h3>Статистика товара</h3>`
})
export class ItemStatComponent{}

Итак, есть два компонента ItemDetailsComponent и ItemStatComponent, которые условно раскрывают различные стороны одно и того же товара. Логически они относятся к
ItemComponent, который также связан с определенным товаром.

Мы бы могли определить прямые маршруты к этим компонентам типа:

{ path: 'item/:id/details', component: ItemDetailsComponent},
{ path: 'item/:id/stat', component: ItemStatComponent},

Где в данном случае id — также параметр маршрута, представляющий условный id товара.

Но такие маршруты будут миновать компонент ItemComponent и никак его не затрагивают. Поэтому нам надо использовать другую организацию маршрутов. Для
этого изменим модуль AppModule следующим образом:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {Routes, RouterModule} from '@angular/router';
import { AppComponent }   from './app.component';
import { HomeComponent }   from './home.component';

import { ItemComponent }   from './item.component';
import { ItemStatComponent }   from './item.stat.component';
import { ItemDetailsComponent }   from './item.details.component';

// определение дочерних маршрутов
const itemRoutes: Routes = ;

const appRoutes: Routes =[

	{ path: 'item/:id', component: ItemComponent},
	{ path: 'item/:id', component: ItemComponent, children: itemRoutes},
	{ path: '', component: HomeComponent}
];

@NgModule({
    imports:      ,
    declarations: ,
    bootstrap:    
})
export class AppModule { }

Каждый из дочерних маршрутов сопоставляется не совсем адресом url, а только с его частью. Далее чтобы применить такие маршруты, у маршрута для компонента
ItemComponent применяется свойство children:

{ path: 'item/:id', component: ItemComponent, children: itemRoutes},

Теперь изменим ItemComponent:

import { Component} from '@angular/core';
import { ActivatedRoute} from '@angular/router';
import {Subscription} from 'rxjs';
 
@Component({
    selector: 'item-info',
    template: `<h2>Товар `id`</h2>
			   <router-outlet></router-outlet>`
})
export class ItemComponent{ 
	
	id: number;
	private routeSubscription: Subscription;
	
	constructor(private route: ActivatedRoute){
		
		this.routeSubscription = route.params.subscribe(params=>this.id=params);
	}
}

Для вставки компонентов ItemDetailsComponent и ItemStatComponent здесь определен элемент .

И изменим главный компонент AppComponent, добавив в него для тестирования ссылки на ItemDetailsComponent и ItemStatComponent:

import { Component} from '@angular/core';
 
@Component({
    selector: 'my-app',
    template: `<div>
					<nav>
						<a routerLink="">Главная</a> |
						<a routerLink="/item/5/details">Информация о товаре</a> |
						<a routerLink="/item/5/stat">Статистика товара</a>
					</nav>
					<router-outlet></router-outlet>
			   </div>`
})
export class AppComponent {}

При переходе по подобным ссылкам будет срабатывать маршрутизация к компоненту ItemComponent и ему будет передаваться параметр маршрута — id. И также
будет срабатывать маршрутизация к ItemDetailsComponent или ItemStatComponent, если после id идет какой-нибудь сегмент «details» или «stat»:

НазадВперед

Действия (actions), редукторы (reducer), селекторы (select), хранилище (store) и побочные эффекты (effects) NGRX

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

На этой картинке мы видим жизненный цикл ngrx. Давайте разберём его …

1. В наиболее распространенном сценарии все начинается в представлении компонента (). Взаимодействие с пользователем может привести к тому, что компонент отправит действие ().

2.1. Если действие не вызывает эффект (), то редуктор отфильтрует действие (обычно с помощью оператора switch), и вернёт новое состояние, которое будет результатом слияния старого состояния со значением, которое изменилось после вызова действия.

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

2.2.1. После того, как эффект отработал (побочные эффекты закончились), он запускает новое действие «состояние-результат» (побочные эффекты могут быть успешными или неудачными), и мы возвращаемся к пункту 2.1.

3. Теперь у хранилища есть новое состояние. Состояние может быть большим деревом — объектом, поэтому вводит селекторы, чтобы иметь возможность использовать только необходимые фрагменты объекта.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector