Учебник по маршрутизатору: экскурсия по героям¶
28.02.2022
В этом учебнике представлен обширный обзор маршрутизатора Angular. В этом уроке вы, основываясь на базовой конфигурации маршрутизатора, изучите такие возможности, как дочерние маршруты, параметры маршрута, ленивая загрузка NgModules, защитные маршруты и предварительная загрузка данных для улучшения пользовательского опыта.
Рабочий пример окончательной версии приложения смотрите в живом примере.
Цели¶
Это руководство описывает разработку многостраничного маршрутизируемого примера приложения. Попутно в нем освещаются ключевые особенности маршрутизатора, такие как:
- Организация функций приложения в модули
- Переход к компоненту (Герои ссылка на "Список героев")
- Включение параметра маршрута (передача
id
героя при маршрутизации к "Детали героя") - Дочерние маршруты (у Кризисного центра есть свои собственные маршруты)
- Страж
canActivate
(проверка доступа к маршруту) - Страж
canActivateChild
(проверка доступа к дочерним маршрутам) - Страж
canDeactivate
(запрашивать разрешение на удаление несохраненных изменений) - Страж
resolve
(предварительная выборка данных маршрута) - Ленивая загрузка
NgModule
- Страж
canMatch
(проверка перед загрузкой активов функционального модуля)
Это руководство построено в виде последовательности этапов, как если бы вы создавали приложение шаг за шагом, но предполагает, что вы знакомы с основными концепциями Angular. Для общего введения в angular смотрите Getting Started.
Для более подробного обзора смотрите учебник Tour of Heroes.
Предварительные условия¶
Чтобы завершить этот учебник, вы должны иметь базовое понимание следующих концепций:
- JavaScript
- HTML
- CSS
- Angular CLI.
Вам может быть полезен учебник Tour of Heroes, но он не обязателен.
Пример приложения в действии¶
Пример приложения для этого учебника помогает Агентству по трудоустройству героев находить кризисы для героев.
Приложение имеет три основные функции:
-
Кризисный центр" для ведения списка кризисов для поручения героям.
-
Область Герои для ведения списка героев, нанятых агентством.
-
Область Админ для управления списком кризисов и героев.
Попробуйте это сделать, щелкнув по этой ссылке.
Приложение отображается с рядом навигационных кнопок и представлением Heroes со списком героев.
Выберите одного героя, и приложение переведет вас на экран редактирования героя.
Измените имя. Нажмите кнопку "Назад", и приложение вернется к списку героев, в котором будет отображаться измененное имя героя. Обратите внимание, что изменение имени вступило в силу немедленно.
Если бы вы нажали кнопку "Назад" браузера, а не кнопку "Назад" приложения, приложение также вернуло бы вас к списку героев. Навигация в приложениях Angular обновляет историю браузера так же, как и обычная веб-навигация.
Теперь нажмите на ссылку Кризисный центр для получения списка текущих кризисов.
Выберите кризис, и приложение переведет вас на экран редактирования кризиса. На той же странице, под списком, в дочернем компоненте появляется Деталь кризиса.
Измените название кризиса. Обратите внимание, что соответствующее название в списке кризисов не изменяется.
В отличие от Hero Detail, который обновляется по мере ввода текста, изменения в Crisis Detail являются временными, пока вы не сохраните или не отмените их нажатием кнопок "Сохранить" или "Отмена". Обе кнопки позволяют вернуться к Кризисному центру и его списку кризисов.
Нажмите кнопку "Назад" браузера или ссылку "Герои", чтобы активировать диалог.
Вы можете сказать "OK" и потерять свои изменения или нажать "Cancel" и продолжить редактирование.
За этим поведением стоит защита маршрутизатора canDeactivate
. Эта защита дает вам шанс очистить или спросить разрешения пользователя, прежде чем переходить от текущего представления.
Кнопки Admin
и Login
иллюстрируют другие возможности маршрутизатора, которые будут рассмотрены далее в руководстве.
Этап 1: Начало работы¶
Начните с базовой версии приложения, которое перемещается между двумя пустыми представлениями.
Создайте пример приложения¶
-
Создайте новый проект Angular, angular-router-tour-of-heroes.
1
ng new angular-router-tour-of-heroes
Когда появится запрос
Вы хотите добавить маршрутизацию Angular?
, выберитеN
.На вопрос
Какой формат таблицы стилей вы хотите использовать?
выберитеCSS
.Через несколько мгновений новый проект
angular-router-tour-of-heroes
будет готов. -
В терминале перейдите в каталог
angular-router-tour-of-heroes
. -
Убедитесь, что ваше новое приложение работает как ожидалось, выполнив команду
ng serve
.1
ng serve
-
Откройте браузер на
http://localhost:4200
.Вы должны увидеть запущенное приложение в браузере.
Определение маршрутов¶
Маршрутизатор должен быть сконфигурирован со списком определений маршрутов.
Каждое определение преобразуется в объект Route, который содержит две вещи: path
— сегмент пути URL для этого маршрута; и component
— компонент, связанный с этим маршрутом.
Маршрутизатор обращается к своему реестру определений при изменении URL браузера или когда код приложения указывает маршрутизатору перемещаться по пути маршрута.
Первый маршрут делает следующее:
- Когда URL-адрес местоположения браузера меняется на соответствующий сегмент пути
/crisis-center
, маршрутизатор активирует экземплярCrisisListComponent
и отображает его представление. - Когда приложение запрашивает навигацию к пути
/crisis-center
, маршрутизатор активирует экземплярCrisisListComponent
, отображает его представление и обновляет адрес местоположения и историю браузера с URL для этого пути.
Первая конфигурация определяет массив из двух маршрутов с минимальными путями, ведущими к CrisisListComponent
и HeroListComponent
.
Создайте компоненты CrisisList
и HeroList
, чтобы маршрутизатору было что отображать.
1 |
|
1 |
|
Замените содержимое каждого компонента следующим образцом HTML.
1 2 |
|
1 2 |
|
Зарегистрируйте Router
и Routes
¶
Чтобы использовать Router
, вы должны сначала зарегистрировать RouterModule
из пакета @angular/router
. Определите массив маршрутов, appRoutes
, и передайте их в метод RouterModule.forRoot()
.
Метод RouterModule.forRoot()
возвращает модуль, содержащий настроенный поставщик услуг Router
, а также другие поставщики, необходимые библиотеке маршрутизации.
После загрузки приложения Router
выполняет начальную навигацию на основе текущего URL браузера.
Метод RouterModule.forRoot()
— это шаблон, используемый для регистрации провайдеров всего приложения. Подробнее о провайдерах для всего приложения читайте в руководстве Singleton services.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Добавление настроенного RouterModule
к AppModule
достаточно для минимальных конфигураций маршрутов. Однако, по мере роста приложения, рефакторинг конфигурации маршрутизации в отдельный файл и создание модуля маршрутизации.
Модуль маршрутизации — это специальный тип Service Module
, предназначенный для маршрутизации.
Регистрация RouterModule.forRoot()
в массиве AppModule
imports
делает сервис Router
доступным везде в приложении.
Добавьте выход маршрутизатора¶
Корневой AppComponent
— это оболочка приложения. Она имеет заголовок, панель навигации с двумя ссылками и выход маршрутизатора, в котором маршрутизатор рендерит компоненты.
Розетка маршрутизатора служит в качестве места, куда выводятся маршрутизируемые компоненты.
Соответствующий шаблон компонента выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Определение маршрута с подстановочным знаком¶
До сих пор вы создали два маршрута в приложении: один к /crisis-center
и другой к /heroes
. Любой другой URL вызывает ошибку маршрутизатора и крах приложения.
Добавьте маршрут с подстановочными знаками, чтобы перехватывать недействительные URL и обрабатывать их изящно. Маршрут с подстановочным знаком имеет путь, состоящий из двух звездочек.
Он соответствует любому URL.
Таким образом, маршрутизатор выбирает этот маршрут с подстановочным знаком, если не может найти маршрут в предыдущей конфигурации.
Маршрут с подстановочным знаком может переходить к пользовательскому компоненту "404 Not Found" или redirect к существующему маршруту.
Note
Маршрутизатор выбирает маршрут с помощью стратегии первое совпадение выигрывает. Поскольку маршрут с подстановочным знаком является наименее специфичным маршрутом, поместите его последним в конфигурации маршрута.
Чтобы проверить эту возможность, добавьте кнопку с RouterLink
в шаблон HeroListComponent
и установите ссылку на несуществующий маршрут под названием "/sidekicks"
.
1 2 3 4 5 6 |
|
Приложение потерпит неудачу, если пользователь нажмет на эту кнопку, потому что вы еще не определили маршрут "/sidekicks"
.
Вместо добавления маршрута sidekicks
определите маршрут wildcard
и попросите его перейти к PageNotFoundComponent
.
1 |
|
Создайте компонент PageNotFoundComponent
для отображения, когда пользователи посещают недопустимые URL.
1 |
|
1 |
|
Теперь, когда пользователь посещает /sidekicks
, или любой другой недействительный URL, браузер выдает "Страница не найдена". Адресная строка браузера продолжает указывать на недопустимый URL.
Настройка перенаправлений¶
Когда приложение запускается, по умолчанию в строке браузера находится начальный URL:
1 |
|
Это не соответствует ни одному из жестко закодированных маршрутов, что означает, что маршрутизатор переходит на маршрут с подстановочным знаком и отображает PageNotFoundComponent
.
Приложению нужен маршрут по умолчанию к действительной странице. Страница по умолчанию для этого приложения — это список героев.
Приложение должно переходить на нее, как если бы пользователь щелкнул по ссылке "Heroes" или вставил localhost:4200/heroes
в адресную строку.
Добавьте маршрут redirect
, который переводит исходный относительный URL (''
) в путь по умолчанию (/heroes
), который вы хотите.
Добавьте маршрут по умолчанию где-то выше маршрута дикого символа. Он находится чуть выше маршрута wildcard в следующей выдержке, показывающей полный appRoutes
для этой вехи.
1 2 3 4 5 6 7 8 9 |
|
В адресной строке браузера отображается .../heroes
, как если бы вы перешли туда напрямую.
Маршрут перенаправления требует свойства pathMatch
, чтобы указать маршрутизатору, как сопоставить URL с путем маршрута. В этом приложении маршрутизатор должен выбрать маршрут к HeroListComponent
только тогда, когда полный URL соответствует ''
, поэтому установите значение pathMatch
в 'full'
.
Внимание на pathMatch
Технически, pathMatch = 'full'
приводит к попаданию в маршрут, когда оставшиеся, несопоставленные сегменты URL совпадают с ''
. В данном примере перенаправление происходит в маршруте верхнего уровня, поэтому оставшийся URL и полный URL — это одно и то же.
Другим возможным значением pathMatch
является 'prefix'
, которое указывает маршрутизатору соответствовать маршруту перенаправления, если оставшийся URL начинается с пути префикса маршрута перенаправления. Это не относится к данному примеру приложения, потому что если бы значение pathMatch
было 'prefix'
, каждый URL совпадал бы с 'prefix'
.
Попробуйте установить значение 'prefix'
и нажмите кнопку Go to sidekicks
. Поскольку это плохой URL, вы должны увидеть страницу "Страница не найдена".
Вместо этого вы все еще находитесь на странице "Герои".
Введите плохой URL-адрес в адресную строку браузера.
Вы будете мгновенно перенаправлены на страницу /heroes
.
Каждый URL, хороший или плохой, который попадает в это определение маршрута, является подходящим.
Маршрут по умолчанию должен перенаправлять на HeroListComponent
только в том случае, если весь URL имеет вид ''
''. Не забудьте восстановить перенаправление на pathMatch = 'full'
.
Узнайте больше в посте Виктора Савкина о редиректах.
Завершение этапа 1¶
Ваш пример приложения может переключаться между двумя представлениями, когда пользователь нажимает на ссылку.
Этап 1 охватывает следующие действия:
- Загрузить библиотеку маршрутизатора
- Добавить навигационную панель в шаблон оболочки с тегами якоря, директивами
routerLink
иrouterLinkActive
. - Добавить
router-outlet
в шаблон оболочки, где отображаются представления. - Настройте модуль маршрутизатора с помощью
RouterModule.forRoot()
. - Настройте маршрутизатор на составление HTML5 URL браузера.
- Обрабатывать недействительные маршруты с помощью
wildcard
. - Переход к маршруту по умолчанию, когда приложение запускается с пустым путем.
Структура стартового приложения выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Вот файлы этой вехи.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
1 2 3 4 5 6 |
|
1 2 |
|
1 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Этап 2: Модуль маршрутизации¶
Этот этап показывает, как настроить специализированный модуль Модуль маршрутизации, который хранит конфигурацию маршрутизации вашего приложения.
Модуль маршрутизации имеет несколько характеристик:
- Отделяет проблемы маршрутизации от других проблем приложения.
- Предоставляет модуль для замены или удаления при тестировании приложения
- Обеспечивает известное местоположение для поставщиков услуг маршрутизации, таких как охранники и резолверы.
- Не объявляет компоненты
Интегрируйте маршрутизацию в ваше приложение¶
Пример приложения с маршрутизацией не включает маршрутизацию по умолчанию. Когда вы используете Angular CLI для создания проекта, который использует маршрутизацию, установите опцию --routing
для проекта или приложения, а также для каждого NgModule.
Когда вы создаете или инициализируете новый проект (используя команду CLI ng new
) или новое приложение (используя команду ng generate app
), укажите опцию --routing
.
Это даст команду CLI включить пакет @angular/router
npm и создать файл с именем app-routing.module.ts
.
Затем вы сможете использовать маршрутизацию в любом Ng-модуле, который вы добавите в проект или приложение.
Например, следующая команда создает NgModule, который может использовать маршрутизацию.
1 |
|
Это создает отдельный файл my-module-routing.module.ts
для хранения маршрутов NgModule. Файл включает пустой объект Routes
, который вы можете заполнить маршрутами к различным компонентам и NgModules.
Рефакторинг конфигурации маршрутизации в модуль маршрутизации¶
Создайте модуль AppRouting
в папке /app
, который будет содержать конфигурацию маршрутизации.
1 |
|
Импортируйте символы CrisisListComponent
, HeroListComponent
и PageNotFoundComponent
, как вы это сделали в app.module.ts
. Затем перенесите импорт Router
и конфигурацию маршрутизации, включая RouterModule.forRoot()
, в этот модуль маршрутизации.
Реэкспортируйте модуль Angular RouterModule
, добавив его в массив exports
модуля. Благодаря реэкспорту RouterModule
здесь, компоненты, объявленные в AppModule
, получают доступ к директивам маршрутизации, таким как RouterLink
и RouterOutlet
.
После этих шагов файл должен выглядеть следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Далее обновите файл app.module.ts
, удалив RouterModule.forRoot
в массиве imports
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Позже в этом руководстве будет показано, как создать несколько модулей маршрутизации и импортировать эти модули маршрутизации в правильном порядке.
Приложение продолжает работать точно так же, и вы можете использовать AppRoutingModule
в качестве центрального места для сохранения будущей конфигурации маршрутизации.
Преимущества модуля маршрутизации¶
Модуль маршрутизации, часто называемый AppRoutingModule
, заменяет конфигурацию маршрутизации в корневом или функциональном модуле.
Модуль маршрутизации полезен по мере роста вашего приложения и когда конфигурация включает специализированные функции guard
и resolver
.
Некоторые разработчики пропускают модуль маршрутизации, когда конфигурация минимальна, и объединяют конфигурацию маршрутизации непосредственно в сопутствующий модуль (например, AppModule
).
Большинство приложений должны реализовать модуль маршрутизации для согласованности. Он сохраняет код чистым, когда конфигурация становится сложной.
Он облегчает тестирование функционального модуля.
Его существование привлекает внимание к тому, что модуль маршрутизирован.
Именно здесь разработчики ожидают найти и расширить конфигурацию маршрутизации.
Веха 3: Функция героев¶
Этот этап включает в себя следующее:
- Организация приложения и маршрутов в области функций с помощью модулей.
- Императивная навигация от одного компонента к другому.
- Передача необходимой и необязательной информации в параметрах маршрута.
Этот пример приложения воссоздает функцию героев в разделе "Услуги" учебника Tour of Heroes и использует большую часть кода из Код примера Tour of Heroes: Услуги.
Типичное приложение имеет несколько функциональных областей, каждая из которых посвящена определенной бизнес-цели и имеет свою собственную папку.
В этом разделе показано, как рефакторить приложение на различные функциональные модули, импортировать их в главный модуль и перемещаться между ними.
Добавить функциональность героев¶
Выполните следующие шаги:
-
Для управления героями создайте
HeroesModule
с маршрутизацией в папке heroes и зарегистрируйте его в корневомAppModule
.1
ng generate module heroes/heroes --module app --flat --routing
-
Переместите папку placeholder
hero-list
, находящуюся в папкеapp
, в папкуheroes
. -
Скопируйте содержимое
heroes/heroes.component.html
из учебника "Services" в шаблонhero-list.component.html
.-
Переименуйте
<h2>
в<h2>HEROES</h2>
. -
Удалите компонент
<app-hero-detail>
в нижней части шаблона.
-
-
Скопируйте содержимое файла
heroes/heroes.component.css
из живого примера в файлhero-list.component.css
. -
Скопируйте содержимое
heroes/heroes.component.ts
из живого примера в файлhero-list.component.ts
.-
Измените имя класса компонента на
HeroListComponent
. -
Измените
selector
наapp-hero-list
.Селекторы не требуются для маршрутизируемых компонентов, поскольку компоненты динамически вставляются при отображении страницы.
Однако они полезны для их идентификации и назначения в дереве элементов HTML.
-
-
Скопируйте папку
hero-detail
, файлыhero.ts
,hero.service.ts
иmock-heroes.ts
в подпапкуheroes
. -
Скопируйте файл
message.service.ts
в папкуsrc/app
. -
Обновите импорт относительного пути к
message.service
в файлеhero.service.ts
.
Далее обновите метаданные HeroesModule
.
- Импортируйте и добавьте
HeroDetailComponent
иHeroListComponent
в массивdeclarations
вHeroesModule
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Структура файлов управления героями выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Требования к маршрутизации функции героев¶
Функция героев состоит из двух взаимодействующих компонентов — списка героев и подробной информации о героях. Когда вы переходите к представлению списка, оно получает список героев и отображает их.
Когда вы нажимаете на героя, вид детализации должен отобразить именно этого героя.
Вы указываете детальному представлению, какого героя отображать, включив идентификатор выбранного героя в URL маршрута.
Импортируйте компоненты героев из их новых мест в папке src/app/heroes/
и определите два маршрута героев.
Теперь, когда у вас есть маршруты для модуля Heroes
, зарегистрируйте их в Router
с помощью RouterModule
, как вы это делали в AppRoutingModule
, с важным отличием.
В модуле AppRoutingModule
вы использовали статический метод RouterModule.forRoot()
для регистрации маршрутов и поставщиков услуг уровня приложения. В функциональном модуле вы используете статический метод forChild()
.
Вызывайте RouterModule.forRoot()
только в корневом AppRoutingModule
(или AppModule
, если в нем вы регистрируете маршруты приложений верхнего уровня). В любом другом модуле вы должны вызвать метод RouterModule.forChild()
для регистрации дополнительных маршрутов.
Обновленный HeroesRoutingModule
выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Рассмотрите возможность предоставления каждому функциональному модулю собственного файла конфигурации маршрута. Хотя в настоящее время маршруты функций минимальны, маршруты имеют тенденцию к усложнению даже в небольших приложениях.
Удаление дубликатов маршрутов героев¶
Маршруты героев в настоящее время определяются в двух местах: в HeroesRoutingModule
, посредством HeroesModule
, и в AppRoutingModule
.
Маршруты, предоставляемые функциональными модулями, объединяются маршрутизатором в маршруты их импортированного модуля. Это позволяет вам продолжать определять маршруты функциональных модулей без изменения конфигурации основных маршрутов.
Удалите импорт HeroListComponent
и маршрут /heroes
из файла app-routing.module.ts
.
Оставьте маршруты по умолчанию и wildcard, так как они все еще используются на верхнем уровне приложения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Удалите декларации героев¶
Поскольку HeroesModule
теперь предоставляет HeroListComponent
, удалите его из массива declarations
модуля AppModule
. Теперь, когда у вас есть отдельный HeroesModule
, вы можете развивать функцию героя с помощью большего количества компонентов и различных маршрутов.
После этих шагов AppModule
должен выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Порядок импорта модулей¶
Обратите внимание, что в массиве импортов
модулей, AppRoutingModule
стоит последним и идет после HeroesModule
.
1 2 3 4 5 6 |
|
Порядок конфигурации маршрутов важен, потому что маршрутизатор принимает первый маршрут, который соответствует пути навигационного запроса.
Когда все маршруты были в одном AppRoutingModule
, вы поместили маршруты по умолчанию и wildcard последними, после маршрута /heroes
, чтобы у маршрутизатора был шанс сопоставить URL с маршрутом /heroes
до попадания на маршрут wildcard и перехода к "Страница не найдена".
Каждый модуль маршрутизации дополняет конфигурацию маршрута в порядке импорта. Если вы перечислили AppRoutingModule
первым, маршрут wildcard будет зарегистрирован перед маршрутами героев.
Маршрут дикого знака — который соответствует каждому URL— будет перехватывать попытку перехода к маршруту героя.
Пересмотрите модули маршрутизации, чтобы увидеть, как щелчок по ссылке героев приводит к "Страница не найдена". Узнайте о проверке конфигурации маршрутизатора во время выполнения ниже.
Параметры маршрута¶
Определение маршрута с параметром¶
Вернитесь к модулю HeroesRoutingModule
и снова посмотрите на определения маршрутов. Маршрут к HeroDetailComponent
имеет токен :id
в пути.
1 |
|
Токен :id
создает слот в пути для параметра маршрута. В данном случае эта конфигурация заставляет маршрутизатор вставить id
героя в этот слот.
Если вы скажете маршрутизатору перейти к компоненту detail и отобразить "Magneta", вы ожидаете, что ID героя появится в URL браузера следующим образом:
1 |
|
Если пользователь введет этот URL в адресную строку браузера, маршрутизатор должен распознать шаблон и перейти к тому же детальному представлению "Magneta".
Параметр маршрута: Обязательный или необязательный?
Встраивание маркера параметра маршрута, :id
, в путь определения маршрута является хорошим выбором для данного сценария, поскольку id
требуется компонентом HeroDetailComponent
и поскольку значение 15
в пути четко отличает маршрут к "Magneta" от маршрута к какому-либо другому герою.
Установка параметров маршрута в представлении списка¶
После перехода к компоненту HeroDetailComponent
вы ожидаете увидеть подробную информацию о выбранном герое. Вам нужны две части информации: путь маршрутизации к компоненту и id
героя.
Соответственно, массив параметров ссылки состоит из двух элементов: путь маршрутизации и параметр маршрута, который определяет id
выбранного героя.
1 |
|
Маршрутизатор составляет URL назначения из массива следующим образом: localhost:4200/hero/15
.
Маршрутизатор извлекает параметр маршрута (id:15
) из URL и передает его в HeroDetailComponent
с помощью сервиса ActivatedRoute
.
Активированный маршрут
в действии¶
Импортируйте токены Router
, ActivatedRoute
и ParamMap
из пакета router.
1 2 3 4 5 |
|
Импортируйте оператор switchMap
, поскольку он понадобится вам позже для обработки параметров маршрута Observable
.
1 |
|
Добавьте сервисы как приватные переменные в конструктор, чтобы Angular инжектировал их (сделал их видимыми для компонента).
1 2 3 4 5 |
|
В методе ngOnInit()
используйте службу ActivatedRoute
для получения параметров маршрута, извлечения id
героя из параметров и получения героя для отображения.
1 2 3 4 5 6 |
|
Когда карта изменяется, paramMap
получает параметр id
из измененных параметров.
Затем вы говорите HeroService
получить героя с этим id
и возвращаете результат запроса HeroService
.
Оператор switchMap
делает две вещи. Он сглаживает Observable<Hero>
, который возвращает HeroService
, и отменяет предыдущие ожидающие запросы.
Если пользователь повторно переходит на этот маршрут с новым id
, в то время как HeroService
все еще получает старый id
, switchMap
отбрасывает старый запрос и возвращает героя для нового id
.
AsyncPipe
обрабатывает наблюдаемую подписку, и свойство hero
компонента будет установлено с полученным героем.
ParamMap
API¶
API ParamMap
вдохновлен интерфейсом URLSearchParams. Он предоставляет методы для обработки доступа к параметрам как для параметров маршрута (paramMap
), так и для параметров запроса (queryParamMap
).
Member | Details |
---|---|
has(name) | Возвращает true , если имя параметра есть в карте параметров. |
get(name) | Возвращает значение имени параметра (строку), если оно есть, или null , если имя параметра отсутствует в карте параметров. Возвращает первый элемент, если значение параметра представляет собой массив значений. |
getAll(name) | Возвращает строковый массив значений имени параметра, если он найден, или пустой массив , если значение имени параметра отсутствует в карте. Используйте getAll , когда один параметр может иметь несколько значений. |
keys | Возвращает массив строк всех имен параметров в карте. |
Observable paramMap
и повторное использование компонентов¶
В этом примере вы получаете карту параметров маршрута из Observable
. Это означает, что карта параметров маршрута может меняться в течение жизни этого компонента.
По умолчанию маршрутизатор повторно использует экземпляр компонента при повторном переходе к одному и тому же типу компонента без предварительного посещения другого компонента. Параметры маршрута могут меняться каждый раз.
Предположим, на навигационной панели родительского компонента есть кнопки "вперед" и "назад", которые прокручивают список героев. Каждое нажатие кнопки обязательно приводило к переходу к HeroDetailComponent
со следующим или предыдущим id
.
Вы не хотите, чтобы маршрутизатор удалял текущий экземпляр HeroDetailComponent
из DOM только для того, чтобы заново создать его для следующего id
, так как это приведет к повторному рендерингу представления. Для улучшения UX маршрутизатор повторно использует один и тот же экземпляр компонента и обновляет параметр.
Поскольку ngOnInit()
вызывается только один раз при инстанцировании компонента, вы можете определить, когда параметры маршрута изменяются в пределах одного и того же экземпляра, используя наблюдаемое свойство paramMap
.
При подписке на наблюдаемую переменную в компоненте вы почти всегда отписываетесь от нее, когда компонент уничтожается.
Однако наблюдаемые ActivatedRoute
являются исключением, поскольку ActivatedRoute
и его наблюдаемые изолированы от самого Router
. Маршрутизатор" уничтожает маршрутизируемый компонент, когда он больше не нужен.
Это означает, что все члены компонента также будут уничтожены, включая инжектированный ActivatedRoute
и подписки на его свойства Observable
.
Маршрутизатор не завершает
ни одну обсервируемую
из ActivatedRoute
, поэтому любые блоки finalize
или complete
не будут выполняться. Если вам нужно обработать что-то в finalize
, вам все равно нужно отписаться в ngOnDestroy
.
Вы также должны отписаться, если в вашем наблюдаемом пайпе есть задержка с кодом, который вы не хотите запускать после уничтожения компонента.
snapshot
: ненаблюдаемая альтернатива¶
Это приложение не будет повторно использовать HeroDetailComponent
. Пользователь всегда возвращается к списку героев, чтобы выбрать другого героя для просмотра.
Невозможно перейти от одной детали героя к другой детали героя без посещения компонента списка между ними.
Поэтому маршрутизатор каждый раз создает новый экземпляр HeroDetailComponent
.
Когда вы точно знаете, что экземпляр HeroDetailComponent
никогда не будет использоваться повторно, вы можете использовать snapshot
.
route.snapshot
предоставляет начальное значение карты параметров маршрута. Вы можете получить доступ к параметрам напрямую, без подписки или добавления операторов наблюдения, как показано ниже:
1 2 3 4 5 |
|
При использовании этой техники snapshot
получает только начальное значение карты параметров. Используйте подход с наблюдаемой paramMap
, если есть вероятность, что маршрутизатор может повторно использовать компонент. В этом учебном примере приложения используется наблюдаемая paramMap
.
Навигация назад к компоненту списка¶
Кнопка "Назад" компонента HeroDetailComponent
использует метод gotoHeroes()
, который принудительно перемещает обратно к компоненту HeroListComponent
.
Метод маршрутизатора navigate()
принимает тот же массив параметров ссылки из одного элемента, который вы можете связать с директивой [routerLink]
. Он содержит путь к компоненту HeroListComponent
:
1 2 3 |
|
Параметры маршрута: Обязательные или необязательные?¶
Используйте route parameters для указания обязательного значения параметра в URL маршрута, как при переходе к HeroDetailComponent
для просмотра героя с id
15:
1 |
|
Вы также можете добавить необязательную информацию в запрос маршрута. Например, при возврате к списку hero-detail.component.ts
из представления деталей героя было бы неплохо, если бы просматриваемый герой был предварительно выбран в списке.
Вы реализуете эту возможность, включив id
просматриваемого героя в URL в качестве необязательного параметра при возврате из HeroDetailComponent
.
Необязательная информация может также включать другие формы, такие как:
- Свободно структурированные критерии поиска; например,
name='wind*'
. - Множественные значения; например,
after='12/31/2015' & before='1/1/2017'
— без особого порядка —before='1/1/2017' & after='12/31/2015'
— в различных форматах —during='currentYear'
.
Поскольку такие параметры не вписываются в путь URL, вы можете использовать необязательные параметры для передачи произвольно сложной информации во время навигации. Необязательные параметры не участвуют в сопоставлении шаблонов и обеспечивают гибкость выражения.
Маршрутизатор поддерживает навигацию с необязательными параметрами так же, как и с обязательными параметрами маршрута. Определите необязательные параметры в отдельном объекте после определения обязательных параметров маршрута.
В общем случае, используйте обязательный параметр маршрута, когда значение является обязательным (например, если необходимо отличить один путь маршрута от другого); и необязательный параметр, когда значение является необязательным, сложным и/или многомерным.
Список героев: опциональный выбор героя¶
При переходе к компоненту HeroDetailComponent
вы указали требуемый id
редактируемого героя в параметре маршрута и сделали его вторым элементом массива link parameters array.
1 |
|
Маршрутизатор встроил значение id
в навигационный URL, потому что вы определили его как параметр маршрута с маркером-заполнителем :id
в маршруте path
:
1 |
|
Когда пользователь нажимает кнопку "Назад", HeroDetailComponent
создает другой массив параметров ссылок, который он использует для перехода обратно к HeroListComponent
.
1 2 3 |
|
В этом массиве отсутствует параметр route, потому что ранее вам не нужно было отправлять информацию в HeroListComponent
.
Теперь отправьте id
текущего героя с навигационным запросом, чтобы HeroListComponent
мог выделить этого героя в своем списке.
Отправьте id
с объектом, который содержит необязательный параметр id
. Для демонстрации в объекте есть дополнительный параметр (foo
), который HeroListComponent
должен игнорировать.
Вот пересмотренный навигационный оператор:
1 2 3 4 5 6 7 |
|
Приложение по-прежнему работает. Нажатие кнопки "назад" возвращает к представлению списка героев.
Посмотрите на адресную строку браузера.
Она должна выглядеть примерно так, в зависимости от того, где вы ее запустили:
1 |
|
Значение id
отображается в URL как (;id=15;foo=foo
), а не в пути URL. Путь для маршрута "Герои" не содержит токена :id
.
Необязательные параметры маршрута не разделены символами "?" и "&", как это было бы в строке запроса URL. Они разделяются точкой с запятой ";".
Это матричная нотация URL.
Матричная нотация URL — это идея, впервые представленная в предложении 1996 года основателем Интернета Тимом Бернерсом-Ли.
Хотя матричная нотация так и не вошла в стандарт HTML, она является легальной и стала популярной среди систем маршрутизации браузеров как способ изолировать параметры, принадлежащие родительским и дочерним маршрутам. Таким образом, Router обеспечивает поддержку матричной нотации во всех браузерах.
Параметры маршрута в сервисе ActivatedRoute
¶
В текущем состоянии разработки список героев не изменяется. Ни одна строка героев не выделена.
Для HeroListComponent
необходим код, который ожидает параметры.
Ранее, при переходе от HeroListComponent
к HeroDetailComponent
, вы подписывались на карту параметров маршрута Observable
и делали ее доступной для HeroDetailComponent
в службе ActivatedRoute
.
Вы внедрили этот сервис в конструктор HeroDetailComponent
.
На этот раз вы будете перемещаться в обратном направлении, от HeroDetailComponent
к HeroListComponent
.
Сначала расширьте оператор импорта маршрутизатора, включив в него служебный символ ActivatedRoute
:
1 |
|
Импортируйте оператор switchMap
для выполнения операции над Observable
карты параметров маршрута.
1 2 |
|
Вставьте ActivatedRoute
в конструктор HeroListComponent
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Свойство ActivatedRoute.paramMap
представляет собой Observable
карту параметров маршрута. При переходе пользователя к компоненту paramMap
выдает новую карту значений, включающую id
.
В функции ngOnInit()
вы подписываетесь на эти значения, устанавливаете selectedId
и получаете героев.
Обновите шаблон с помощью class binding. Привязка добавляет CSS-класс selected
, когда сравнение возвращает true
, и удаляет его, когда false
.
Ищите его в повторяющемся теге <li>
, как показано здесь:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Добавьте несколько стилей для применения при выборе героя.
1 2 3 4 5 6 7 |
|
Когда пользователь переходит от списка героев к герою "Magneta" и обратно, "Magneta" отображается выбранной:
Необязательный параметр маршрута foo
безвреден, и маршрутизатор продолжает его игнорировать.
Добавление маршрутизируемых анимаций¶
В этом разделе показано, как добавить некоторые анимации в HeroDetailComponent
.
Сначала импортируйте BrowserAnimationsModule
и добавьте его в массив imports
:
1 2 3 4 5 6 7 |
|
Далее, добавьте объект data
в маршруты для HeroListComponent
и HeroDetailComponent
. Переходы основаны на states
, и вы используете данные animation
из маршрута, чтобы предоставить именованную анимацию state
для переходов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Создайте файл animations.ts
в корневой папке src/app/
. Его содержимое выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
Этот файл делает следующее:
- Импортирует символы анимации, которые создают триггеры анимации, контролируют состояние и управляют переходами между состояниями.
- Экспортирует константу с именем
slideInAnimation
, установленную на триггер анимации с именемrouteAnimation
. - Определяет один переход при переключении между маршрутами
героев
игероев
для облегчения входа компонента с левой стороны экрана, когда он входит в представление приложения (:enter
), другой для анимации компонента справа, когда он покидает представление приложения (:leave
)
Вернитесь в AppComponent
, импортируйте токен RouterOutlet
из пакета @angular/router
и slideInAnimation
из './animations.ts
.
Добавьте массив animations
в метаданные @Component
, содержащие slideInAnimation
.
1 2 3 4 5 6 7 8 9 |
|
Чтобы использовать маршрутизируемые анимации, оберните RouterOutlet
внутри элемента, используйте триггер @routeAnimation
и привяжите его к элементу.
Чтобы @routeAnimation
переходила в состояния выключения ключа, предоставьте ей данные
из ActivatedRoute
. Переменная RouterOutlet
раскрывается как переменная шаблона outlet
, поэтому вы привязываете ссылку на выход маршрутизатора.
В данном примере используется переменная routerOutlet
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Свойство @routeAnimation
связано с getAnimationData()
, которое возвращает свойство анимации из данных
, предоставленных основным маршрутом. Свойство animation
соответствует именам transition
, которые вы использовали в slideInAnimation
, определенном в animations.ts
.
1 2 3 4 5 6 7 8 |
|
При переключении между двумя маршрутами, HeroDetailComponent
и HeroListComponent
теперь облегчаются слева при маршрутизации к, и сдвигаются вправо при навигации прочь.
Завершение этапа 3¶
В этом разделе было рассмотрено следующее:
- Организация приложения по функциональным областям
- Императивная навигация от одного компонента к другому
- Передача информации в параметрах маршрута и подписка на них в компоненте
- Импорт функциональной области NgModule в
AppModule
. - Применение маршрутизируемых анимаций на основе страницы.
После этих изменений структура папок выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Вот соответствующие файлы для этой версии примера приложения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Веха 4: Функция кризисного центра¶
В этом разделе показано, как добавлять дочерние маршруты и использовать относительную маршрутизацию в вашем приложении.
Чтобы добавить дополнительные возможности к текущему кризисному центру приложения, проделайте те же шаги, что и для функции героев:
- Создайте подпапку
crisis-center
в папкеsrc/app
. - Скопируйте файлы и папки из папки
app/heroes
в новую папкуcrisis-center
. - В новых файлах измените все упоминания "героя" на "кризис", а "героев" на "кризисы".
- Переименуйте файлы NgModule в
crisis-center.module.ts
иcrisis-center-routing.module.ts
.
Используйте имитацию кризисов вместо имитации героев:
1 2 3 4 5 6 7 8 9 10 11 |
|
Получившийся кризисный центр является основой для введения новой концепции — дочерней маршрутизации. Вы можете оставить Heroes в его текущем состоянии в качестве контраста с Кризисным центром.
В соответствии с принципом Separation of Concerns, изменения в Кризисном центре не влияют на AppModule
или любой другой компонент функции.
Кризисный центр с дочерними маршрутами¶
В этом разделе показано, как организовать кризисный центр в соответствии со следующим рекомендуемым шаблоном для приложений Angular:
- Каждая область функций находится в своей собственной папке.
- Каждая функция имеет свой модуль функции Angular
- Каждая область имеет свой корневой компонент области
- Каждый корневой компонент области имеет свой собственный выход маршрутизатора и дочерние маршруты.
- Маршруты областей функций редко (если вообще когда-либо) пересекаются с маршрутами других функций.
Если в вашем приложении много областей функций, деревья компонентов могут состоять из множества компонентов для этих функций, каждый из которых имеет ответвления от других, связанных с ним компонентов.
Дочерний компонент маршрутизации¶
Создайте компонент CrisisCenter
в папке crisis-center
:
1 |
|
Обновите шаблон компонента с помощью следующей разметки:
1 2 |
|
Компонент CrisisCenterComponent
имеет следующее общее с AppComponent
:
- Он является корнем области кризисного центра, так же как
AppComponent
является корнем всего приложения. - Это оболочка для области функций кризисного управления, так же как
AppComponent
является оболочкой для управления рабочим процессом высокого уровня.
Как и большинство оболочек, класс CrisisCenterComponent
является минимальным, поскольку в нем нет бизнес-логики, а в его шаблоне нет ссылок, только заголовок и <router-outlet>
для дочернего компонента кризисного центра.
Конфигурация дочернего маршрута¶
В качестве главной страницы для функции "Кризисный центр" создайте компонент CrisisCenterHome
в папке crisis-center
.
1 |
|
Обновите шаблон с приветственным сообщением для Кризисного центра
.
1 |
|
Обновите crisis-center-routing.module.ts
, который вы переименовали после копирования из файла heroes-routing.module.ts
. На этот раз вы определяете дочерние маршруты внутри родительского маршрута crisis-center
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Обратите внимание, что родительский маршрут crisis-center
имеет свойство children
с единственным маршрутом, содержащим CrisisListComponent
. Маршрут CrisisListComponent
также имеет массив children
с двумя маршрутами.
Эти два маршрута ведут к дочерним компонентам кризисного центра, CrisisCenterHomeComponent
и CrisisDetailComponent
, соответственно.
Существуют важные различия в том, как маршрутизатор обрабатывает дочерние маршруты.
Маршрутизатор отображает компоненты этих маршрутов в RouterOutlet
оболочки CrisisCenterComponent
, а не в RouterOutlet
оболочки AppComponent
.
Компонент CrisisListComponent
содержит список кризисов и RouterOutlet
для отображения компонентов маршрута Crisis Center Home
и Crisis Detail
.
Маршрут Crisis Detail
является дочерним для Crisis List
. По умолчанию маршрутизатор повторно использует компоненты, поэтому компонент Crisis Detail
используется повторно при выборе различных кризисов.
В отличие от этого, в маршруте Деталь героя
компонент создавался заново каждый раз, когда вы выбирали другого героя из списка героев.
На верхнем уровне пути, начинающиеся с /
, ссылаются на корень приложения. Но дочерние маршруты расширяют путь родительского маршрута.
При каждом шаге вниз по дереву маршрута добавляется косая черта, за которой следует путь маршрута, если только путь не пуст.
Примените эту логику к навигации внутри кризисного центра, для которого родительский путь — /crisis-center
.
- Чтобы перейти к компоненту
CrisisCenterHomeComponent
, полный URL будет/crisis-center
(/crisis-center
+''
+''
) - Чтобы перейти к компоненту
CrisisDetailComponent
для кризиса сid=2
, полный URL будет/crisisis-center/2
(/crisisis-center
+''
+'/2'
)
Абсолютный URL для последнего примера, включая localhost
, выглядит следующим образом:
1 |
|
Вот полный файл crisis-center-routing.module.ts
с его импортами.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
Импортируйте модуль кризисного центра в маршруты AppModule
¶
Как и в случае с HeroesModule
, вы должны добавить CrisisCenterModule
в массив imports
AppModule
перед AppRoutingModule
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Порядок импорта модулей важен, поскольку порядок маршрутов, определенных в модулях, влияет на согласование маршрутов. Если бы AppModule
был импортирован первым, его маршрут с подстановочным знаком (путь: '**'
) имел бы приоритет над маршрутами, определенными в CrisisCenterModule
. Для получения дополнительной информации смотрите раздел порядок маршрутов.
Удалите начальный маршрут кризисного центра из файла app-routing.module.ts
, поскольку теперь модули HeroesModule
и CrisisCenter
предоставляют функциональные маршруты.
Файл app-routing.module.ts
сохраняет маршруты верхнего уровня приложения, такие как маршруты по умолчанию и маршруты подстановочных знаков.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Относительная навигация¶
При создании функции кризисного центра вы перешли к маршруту подробной информации о кризисе, используя абсолютный путь, начинающийся со слэша.
Маршрутизатор сопоставляет такие абсолютные пути с маршрутами, начиная с верхней части конфигурации маршрута.
Вы могли бы продолжать использовать абсолютные пути, подобные этому, для навигации внутри функции Кризисного центра, но это привязывает ссылки к родительской структуре маршрутизации. Если вы измените родительский путь /crisis-center
, вам придется изменить массив параметров ссылки.
Вы можете освободить ссылки от этой зависимости, определив пути, относительные к текущему сегменту URL. Навигация в области функции остается нетронутой, даже если вы измените путь родительского маршрута к функции.
Маршрутизатор поддерживает синтаксис, подобный каталогу, в списке параметров ссылки, чтобы помочь в поиске имени маршрута:
Синтаксис, подобный директории | Подробности |
---|---|
./ без ведущей косой черты | Относительно текущего уровня. |
../ | На один уровень вверх по пути маршрута. |
Можно комбинировать синтаксис относительной навигации и путь предка. Если вам нужно перейти к родственному маршруту, вы можете использовать соглашение ../<sibling>
для перехода на один уровень вверх, затем на другой и вниз по маршруту родственника.
Чтобы перейти по относительному пути с помощью метода Router.navigate
, вы должны предоставить ActivatedRoute
, чтобы маршрутизатор знал, где вы находитесь в текущем дереве маршрутов.
После массива параметров ссылки добавьте объект со свойством relativeTo
, установленным на ActivatedRoute
. После этого маршрутизатор вычисляет целевой URL, основываясь на местоположении активного маршрута.
Всегда указывайте полный абсолютный путь при вызове метода маршрутизатора navigateByUrl()
.
Переход к списку кризисов с помощью относительного URL-адреса¶
Вы уже ввели ActivatedRoute
, который необходим для составления относительного пути навигации.
При использовании RouterLink
для навигации вместо сервиса Router
, вы будете использовать тот же массив параметров ссылки, но не будете предоставлять объекту свойство relativeTo
. Свойство ActivatedRoute
является неявным в директиве RouterLink
.
Обновите метод gotoCrises()
компонента CrisisDetailComponent
для перехода обратно к списку кризисных центров, используя навигацию по относительному пути.
1 2 3 4 5 |
|
Обратите внимание, что путь поднимается на уровень вверх, используя синтаксис ../
. Если текущий кризис id
равен 3
, то путь обратно к списку кризисов будет /crisis-center/;id=3;foo=foo
.
Отображение нескольких маршрутов в именованных торговых точках¶
Вы решили предоставить пользователям возможность связаться с кризисным центром. Когда пользователь нажимает кнопку "Связаться", вы хотите отобразить сообщение во всплывающем окне.
Всплывающее окно должно оставаться открытым, даже при переключении между страницами приложения, пока пользователь не закроет его, отправив сообщение или отменив его. Очевидно, что вы не можете поместить всплывающее окно в ту же розетку, что и другие страницы.
До сих пор вы определяли один аутлет и вставляли дочерние маршруты под этот аутлет, чтобы сгруппировать маршруты вместе. Маршрутизатор поддерживает только один первичный безымянный аутлет для каждого шаблона.
Шаблон также может иметь любое количество именованных аутлетов. Каждый именованный аутлет имеет свой собственный набор маршрутов со своими компонентами.
Несколько розеток могут одновременно отображать различное содержимое, определяемое различными маршрутами.
Добавьте аутлет с именем "popup" в AppComponent
, непосредственно за неименованным аутлетом.
1 2 3 4 |
|
Вот куда попадает всплывающее окно, как только вы узнаете, как направить к нему всплывающий компонент.
Вторичные маршруты¶
Именованные розетки являются целями вторичных маршрутов.
Вторичные маршруты похожи на первичные, и вы настраиваете их таким же образом. Они отличаются по нескольким ключевым параметрам.
- Они независимы друг от друга
- Они работают в комбинации с другими маршрутами
- Они отображаются в именованных точках.
Создайте новый компонент для составления сообщения.
1 |
|
Он отображает короткую форму с заголовком, полем ввода для сообщения и двумя кнопками "Отправить" и "Отмена".
Вот компонент, его шаблон и стили:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
== "src/app/compose-message/compose-message.component.css"
1 2 3 4 5 6 7 8 |
|
Он выглядит так же, как и любой другой компонент в этом руководстве, но есть два ключевых отличия.
Метод send()
имитирует задержку, ожидая секунду перед "отправкой" сообщения и закрытием всплывающего окна.
Метод closePopup()
закрывает всплывающее представление, переходя к выходу из всплывающего окна с помощью null
, о чем говорится в разделе очистка вторичных маршрутов.
Добавление вторичного маршрута¶
Откройте модуль AppRoutingModule
и добавьте новый маршрут compose
в appRoutes
.
1 2 3 4 5 |
|
В дополнение к свойствам path
и component
, есть новое свойство outlet
, которое установлено в 'popup'
. Теперь этот маршрут нацелен на всплывающий аутлет, и компонент ComposeMessageComponent
будет отображаться там.
Чтобы дать пользователям возможность открыть всплывающее окно, добавьте ссылку "Contact" в шаблон AppComponent
.
1 2 3 |
|
Хотя маршрут compose
настроен на аутлет "popup", этого недостаточно для подключения маршрута к директиве RouterLink
. Вы должны указать именованный аутлет в массиве параметров ссылки и связать его с RouterLink
с помощью привязки свойств.
Массив параметров ссылки содержит объект с одним свойством outlets
, значением которого является другой объект, ключом которого является одно (или более) имя розетки. В данном случае есть только свойство "popup" outlet, и его значением является другой массив link parameters, который определяет маршрут compose
.
Другими словами, когда пользователь нажимает на эту ссылку, маршрутизатор отображает компонент, связанный с маршрутом compose
в аутлете popup
.
Этот объект outlets
внутри внешнего объекта был ненужным, когда существовал только один маршрут и один безымянный выход.
Маршрутизатор предположил, что ваша спецификация маршрута нацелена на безымянный первичный аутлет, и создал эти объекты для вас.
Маршрутизация к именованной розетке выявила особенность маршрутизатора: вы можете нацелить несколько розеток несколькими маршрутами в одной директиве RouterLink
.
Вторичная навигация по маршруту: объединение маршрутов во время навигации¶
Перейдите в Кризисный центр и нажмите "Контакт". В адресной строке браузера вы должны увидеть что-то вроде следующего URL.
1 |
|
Соответствующая часть URL следует за ...
:
кризисный центр
является основной навигацией.- Круглые скобки окружают вторичный маршрут
- Вторичный маршрут состоит из названия выхода (
popup
), разделителяcolon
и пути вторичного маршрута (compose
).
Нажмите на ссылку Герои и снова посмотрите на URL.
1 |
|
Первичная навигационная часть изменилась; вторичный маршрут остался прежним.
Маршрутизатор отслеживает две отдельные ветви в навигационном дереве и генерирует представление этого дерева в URL.
Вы можете добавить еще много выходов и маршрутов, на верхнем уровне и во вложенных уровнях, создавая дерево навигации с множеством ветвей, и маршрутизатор будет генерировать URL для этого.
Вы можете указать маршрутизатору на навигацию по всему дереву сразу, заполнив объект outlets
, а затем передать этот объект внутри массива параметров ссылок методу router.navigate
.
Очистка вторичных маршрутов¶
Как и обычные аутлеты, вторичные аутлеты сохраняются до тех пор, пока вы не перейдете к новому компоненту.
Каждый вторичный выход имеет свою собственную навигацию, независимую от навигации, управляющей первичным выходом. Изменение текущего маршрута, отображаемого в первичном аутлете, не влияет на всплывающий аутлет.
Вот почему всплывающее окно остается видимым, пока вы перемещаетесь между кризисами и героями.
Снова метод closePopup()
:
1 2 3 4 5 |
|
Нажатие на кнопки "отправить" или "отменить" очищает всплывающее окно. Функция closePopup()
осуществляет императивную навигацию с помощью метода Router.navigate()
, передавая массив параметров ссылки.
Как и массив, связанный с Contact RouterLink
в AppComponent
, этот массив включает объект со свойством outlets
. Значение свойства outlets
— это другой объект с именами аутлетов для ключей.
Единственным именованным аутлетом является 'popup'
.
На этот раз значение свойства 'popup'
равно null
. Это не маршрут, но это легитимное значение.
Установив для popup RouterOutlet
значение null
, очистите аутлет и удалите вторичный маршрут popup из текущего URL.
Веха 5: Охрана маршрутов¶
В настоящее время любой пользователь может перемещаться в любое место приложения в любое время, но иногда вам необходимо контролировать доступ к различным частям вашего приложения по различным причинам, некоторые из которых могут быть следующими:
- Возможно, пользователь не имеет права переходить к целевому компоненту.
- Возможно, пользователь должен сначала войти в систему (authenticate)
- Возможно, вам нужно получить некоторые данные перед отображением целевого компонента.
- Возможно, вы захотите сохранить изменения перед тем, как покинуть компонент.
- Вы можете спросить пользователя, можно ли отбросить ожидающие изменения, а не сохранять их.
Для обработки этих сценариев в конфигурацию маршрута добавляются защитные функции.
Возвращаемое значение охранника управляет поведением маршрутизатора:
Возвращаемое значение | Подробности |
---|---|
true | Процесс навигации продолжается |
false | Процесс навигации останавливается, и пользователь остается на месте |
UrlTree | Текущая навигация отменяется и начинается новая навигация к возвращенному UrlTree . |
Примечание
Guard также может указать маршрутизатору перейти в другое место, фактически отменяя текущую навигацию. Если это делается внутри guard
, то guard должен возвращать UrlTree
.
Охранник может вернуть свой булев ответ синхронно. Но во многих случаях охранник не может выдать ответ синхронно. Страж может задать вопрос пользователю, сохранить изменения на сервере или получить свежие данные.
Все это асинхронные операции.
Соответственно, страж маршрутизации может возвращать Observable<boolean>
или Promise<boolean>
, а маршрутизатор будет ждать, пока наблюдаемая или обещание разрешатся в true
или false
.
Наблюдаемая, предоставленная Router
, автоматически завершается после получения первого значения.
Маршрутизатор поддерживает несколько методов охраны:
Интерфейсы охраны | Подробности |
---|---|
canActivate | Для опосредованной навигации к маршруту |
canActivateChild | Для опосредования навигации к дочернему маршруту |
canDeactivate | Для опосредованной навигации в сторону от текущего маршрута |
resolve | Для выполнения поиска данных маршрута до активации маршрута |
canMatch | Чтобы контролировать, должен ли вообще использоваться маршрут , даже если путь соответствует сегменту URL |
Вы можете иметь несколько защит на каждом уровне иерархии маршрутизации. Сначала маршрутизатор проверяет защиту canDeactivate
, начиная с самого глубокого дочернего маршрута и заканчивая самым верхним.
Затем он проверяет защиты canActivate
и canActivateChild
сверху вниз до самого глубокого дочернего маршрута.
Если функциональный модуль загружается асинхронно, то защита canMatch
проверяется до загрузки модуля.
За исключением canMatch
, если любой страж возвращает false, незавершенные стражи отменяются, и вся навигация отменяется. Если защита canMatch
возвращает значение false
, маршрутизатор
продолжает обработку остальных маршрутов
, чтобы проверить, не соответствует ли URL другой конфигурации маршрута
. Вы можете думать об этом
как будто Router
делает вид, что маршрут
с защитой canMatch
не существует.
В следующих разделах будет приведено несколько примеров.
canActivate
: требование аутентификации¶
Приложения часто ограничивают доступ к области функций в зависимости от того, кем является пользователь. Вы можете разрешить доступ только аутентифицированным пользователям или пользователям с определенной ролью.
Можно заблокировать или ограничить доступ до тех пор, пока учетная запись пользователя не будет активирована.
Защита canActivate
является инструментом для управления этими бизнес-правилами навигации.
Добавление модуля функций администратора¶
Этот раздел поможет вам расширить кризисный центр некоторыми новыми административными функциями. Начните с добавления нового функционального модуля с именем AdminModule
.
Создайте папку admin
с файлом функционального модуля и файлом конфигурации маршрутизации.
1 |
|
Затем создайте вспомогательные компоненты.
1 |
|
1 |
|
1 |
|
1 |
|
Структура файла функции администратора выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Функциональный модуль администратора содержит AdminComponent
, используемый для маршрутизации внутри функционального модуля, маршрут приборной панели и два незавершенных компонента для управления кризисами и героями.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1 |
|
1 |
|
Хотя ссылка RouterLink
на приборную панель администратора содержит только относительную косую черту без дополнительного сегмента URL, она подходит к любому маршруту в области функций администратора. Вы хотите, чтобы ссылка Dashboard
была активна только тогда, когда пользователь посещает этот маршрут. Добавление дополнительной привязки к Dashboard
routerLink, [routerLinkActiveOptions]="{ exact: true }"
, отмечает ссылку ./
как активную, когда пользователь переходит на URL /admin
, а не при переходе на любой из дочерних маршрутов.
Маршрут без компонента: группировка маршрутов без компонента¶
Начальная конфигурация маршрутизации администратора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Дочерний маршрут под AdminComponent
имеет свойство path
и children
, но он не использует component
. Это определяет маршрут без компонента.
Для группировки маршрутов управления Кризисного центра
по пути admin
компонент не нужен. Кроме того, маршрут без компонента облегчает охрану дочерних маршрутов.
Далее импортируйте AdminModule
в app.module.ts
и добавьте его в массив imports
для регистрации маршрутов администратора.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Добавьте ссылку "Admin" в оболочку AppComponent
, чтобы пользователи могли получить доступ к этой функции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Охрана функции администратора¶
В настоящее время каждый маршрут в Кризисном центре открыт для всех. Новая функция администратора должна быть доступна только для аутентифицированных пользователей.
Напишите защитный метод canActivate()
, чтобы перенаправлять анонимных пользователей на страницу входа в систему, когда они пытаются войти в область администратора.
Создайте новый файл с именем auth.guard.ts
в папке auth
. Файл auth.guard.ts
будет содержать функцию authGuard
.
1 |
|
Чтобы продемонстрировать основные принципы, в этом примере только регистрируется в консоли, немедленно возвращается true
и разрешается продолжить навигацию:
1 2 3 4 |
|
Далее откройте admin-routing.module.ts
, импортируйте функцию authGuard
и обновите административный маршрут со свойством canActivate
guard, которое ссылается на нее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
Функция администрирования теперь защищена стражем, но для полноценной работы стража требуется дополнительная настройка.
Аутентификация с помощью authGuard
¶
Сделайте authGuard
имитирующим аутентификацию.
authGuard
должен вызывать прикладную службу, которая может регистрировать пользователя и сохранять информацию о текущем пользователе. Создайте новый AuthService
в папке auth
:
1 |
|
Обновите AuthService
для входа пользователя в систему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Хотя он не выполняет вход в систему, у него есть флаг isLoggedIn
, чтобы сообщить вам, аутентифицирован ли пользователь. Его метод login()
имитирует вызов API к внешней службе, возвращая наблюдаемую, которая успешно разрешается после небольшой паузы.
Свойство redirectUrl
хранит URL, к которому хотел получить доступ пользователь, чтобы вы могли перейти к нему после аутентификации.
Чтобы сделать все минимальным, этот пример перенаправляет неаутентифицированных пользователей на /admin
.
Пересмотрите authGuard
для вызова AuthService
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Эта защита возвращает синхронный булев результат или UrlTree
. Если пользователь вошел в систему, он возвращает true
и навигация продолжается.
В противном случае он перенаправляет на страницу входа; страницу, которую вы еще не создали.
Возврат UrlTree
указывает Router
отменить текущую навигацию и запланировать новую для перенаправления пользователя.
Добавьте LoginComponent
¶
Вам нужен LoginComponent
для входа пользователя в приложение. После входа в систему вы будете перенаправлять на сохраненный URL, если он доступен, или использовать URL по умолчанию.
Нет ничего нового в этом компоненте или способе его использования в конфигурации маршрутизатора.
1 |
|
Зарегистрируйте маршрут /login
в файле auth/auth-routing.module.ts
. В файле app.module.ts
импортируйте и добавьте AuthModule
в массив импортов AppModule
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
canActivateChild
: защита дочерних маршрутов¶
Вы также можете защитить дочерние маршруты с помощью защиты canActivateChild
. Защита canActivateChild
аналогична защите canActivate
.
Ключевое отличие заключается в том, что он запускается до активации любого дочернего маршрута.
Вы защитили функциональный модуль администратора от несанкционированного доступа. Вам также следует защитить дочерние маршруты внутри функционального модуля.
Добавьте тот же authGuard
к админскому маршруту component-less
, чтобы защитить все остальные дочерние маршруты одновременно, вместо того, чтобы добавлять authGuard
к каждому маршруту по отдельности.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
canDeactivate
: обработка несохраненных изменений¶
Вернемся к рабочему процессу "Герои", приложение принимает каждое изменение героя немедленно без проверки.
В реальном мире вам, возможно, придется накапливать изменения пользователей, проверять их по полям, проверять на сервере или держать изменения в состоянии ожидания, пока пользователь не подтвердит их как группу или не отменит и не вернет все изменения.
Когда пользователь отходит, вы можете позволить ему самому решить, что делать с несохраненными изменениями. Если пользователь отменит изменения, вы останетесь на месте и разрешите новые изменения.
Если пользователь одобрит изменения, приложение можно сохранить.
Вы все еще можете задержать навигацию до тех пор, пока сохранение не будет успешным. Если вы позволите пользователю сразу перейти к следующему экрану, а сохранение окажется неудачным (возможно, данные будут признаны недействительными), вы потеряете контекст ошибки.
Вам нужно остановить навигацию, пока вы асинхронно ждете ответа сервера.
Защита canDeactivate
поможет вам решить, что делать с несохраненными изменениями и как действовать дальше.
Отмена и сохранение¶
Пользователи обновляют информацию о кризисе в компоненте CrisisDetailComponent
. В отличие от компонента HeroDetailComponent
, изменения пользователя не обновляют сущность кризиса немедленно.
Вместо этого приложение обновляет сущность, когда пользователь нажимает кнопку Сохранить, и отбрасывает изменения, когда пользователь нажимает кнопку Отмена.
Обе кнопки позволяют вернуться к списку кризисов после сохранения или отмены.
1 2 3 4 5 6 7 8 |
|
В этом сценарии пользователь может нажать на ссылку героев, отменить действие, нажать кнопку возврата браузера или уйти без сохранения.
Этот пример приложения просит пользователя быть явным с диалоговым окном подтверждения, которое асинхронно ожидает ответа пользователя.
Вы можете ждать ответа пользователя с помощью синхронного, блокирующего кода, однако приложение будет более отзывчивым — и сможет выполнять другую работу — если будет ждать ответа пользователя асинхронно.
Создайте службу Dialog
для обработки подтверждения пользователя.
1 |
|
Добавьте метод confirm()
в DialogService
, чтобы попросить пользователя подтвердить свое намерение. Метод window.confirm
является блокирующим действием, которое отображает модальный диалог и ожидает взаимодействия с пользователем.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Он возвращает Observable
, который разрешается, когда пользователь в конечном итоге решает, что делать: либо отменить изменения и перейти к следующему (true
), либо сохранить ожидающие изменения и остаться в кризисном редакторе (false
).
Создайте защиту, которая проверяет наличие метода canDeactivate()
в компоненте — любом компоненте.
1 |
|
Вставьте следующий код в свой страж.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Хотя стражу не обязательно знать, какой компонент имеет метод deactivate
, он может определить, что компонент CrisisDetailComponent
имеет метод canDeactivate()
и вызвать его. Незнание сторожем деталей метода деактивации любого компонента делает его многоразовым.
В качестве альтернативы вы можете сделать специфичный для компонента canDeactivate
guard для CrisisDetailComponent
. Метод canDeactivate()
предоставляет вам текущий экземпляр компонента
, текущий ActivatedRoute
и RouterStateSnapshot
на случай, если вам понадобится доступ к какой-либо внешней информации.
Это было бы полезно, если бы вы хотели использовать эту защиту только для этого компонента и вам нужно было бы получить свойства компонента или подтвердить, должен ли маршрутизатор разрешить навигацию от него.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Если вернуться к CrisisDetailComponent
, то он реализует рабочий процесс подтверждения для несохраненных изменений.
1 2 3 4 5 6 7 8 9 |
|
Обратите внимание, что метод canDeactivate()
может возвращаться синхронно; он возвращает true
немедленно, если нет кризиса или нет ожидающих изменений. Но он также может возвращать Promise
или Observable
, и маршрутизатор будет ждать, пока это разрешится в истинное (navigate) или ложное (stay on the current route) решение.
Добавьте Guard
к маршруту деталей кризиса в crisis-center-routing.module.ts
, используя свойство массива canDeactivate
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Теперь вы обеспечили пользователю защиту от несохраненных изменений.
Resolve: предварительная выборка данных компонента¶
В Hero Detail
и Crisis Detail
приложение ждало, пока активируется маршрут, чтобы получить данные о соответствующем герое или кризисе.
Если вы используете реальный API, то может возникнуть некоторая задержка, прежде чем данные для отображения будут возвращены с сервера. Вы же не хотите отображать пустой компонент в ожидании данных.
Чтобы улучшить это поведение, вы можете предварительно получить данные с сервера с помощью резольвера, чтобы они были готовы в момент активации маршрута. Это также позволяет обрабатывать ошибки перед маршрутизацией к компоненту.
Нет смысла переходить к детализации кризиса для id
, для которого нет записи.
Лучше отправить пользователя обратно к Списку кризисов
, который показывает только действующие кризисные центры.
В общем, вы хотите отложить отображение компонента маршрутизации до тех пор, пока не будут получены все необходимые данные.
Выборка данных перед навигацией¶
В данный момент CrisisDetailComponent
извлекает данные о выбранном кризисе. Если кризис не найден, маршрутизатор возвращается к представлению списка кризисов.
Возможно, было бы лучше, если бы все это обрабатывалось сначала, до активации маршрута. Резольвер crisisDetailResolver
мог бы получить кризис
или перейти в сторону, если кризис
не существует, до активации маршрута и создания CrisisDetailComponent
.
Создайте файл crisis-detail-resolver.ts
в области функции Crisis Center
. Этот файл будет содержать функцию crisisDetailResolver
.
1 |
|
1 |
|
Перенесите соответствующие части логики поиска кризисов в CrisisDetailComponent.ngOnInit()
в crisisDetailResolver
. Импортируйте модель Crisis
, CrisisService
и Router
, чтобы вы могли перейти в другое место, если не сможете найти кризис.
Будьте явными и используйте тип ResolveFn
с типом Crisis
.
Вставьте CrisisService
и Router
. Этот метод может возвращать Promise
, Observable
или синхронное возвращаемое значение.
Метод CrisisService.getCrisis()
возвращает наблюдаемое значение, чтобы предотвратить загрузку маршрута до тех пор, пока не будут получены данные.
Если он не возвращает действительный кризис
, то возвращает пустой Observable
, отменяет предыдущую текущую навигацию к CrisisDetailComponent
и возвращает пользователя к CrisisListComponent
. Обновленная функция resolver выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Импортируйте этот resolver в crisis-center-routing.module.ts
и добавьте объект resolve
в конфигурацию маршрута CrisisDetailComponent
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
Компонент CrisisDetailComponent
больше не должен получать данные о кризисе. Когда вы переконфигурировали маршрут, вы изменили местоположение кризиса.
Обновите CrisisDetailComponent
, чтобы получить кризис из свойства ActivatedRoute.data.crisis
вместо этого;
1 2 3 4 5 6 7 8 |
|
Рассмотрите следующие три важных момента:
-
Функция маршрутизатора
ResolveFn
является необязательной. -
Маршрутизатор вызывает резольвер в любом случае, когда пользователь может переместиться в сторону, поэтому вам не придется писать код для каждого случая использования.
-
Возвращение пустого
Observable
хотя бы в одном резольвере отменяет навигацию.
Соответствующий код Кризисного центра для этого этапа приведен ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
1 |
|
1 2 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Гварды
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Параметры запроса и фрагменты¶
В разделе параметры маршрута вы имели дело только с параметрами, специфичными для маршрута. Однако вы можете использовать параметры запроса для получения необязательных параметров, доступных для всех маршрутов.
Фрагменты ссылаются на определенные элементы на странице, идентифицируемые атрибутом id
.
Обновите authGuard
, чтобы обеспечить запрос session_id
, который сохраняется после перехода к другому маршруту.
Добавьте элемент anchor
, чтобы можно было перейти к определенной точке страницы.
Добавьте объект NavigationExtras
в метод router.navigate()
, который переводит вас на маршрут /login
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Вы также можете сохранять параметры запроса и фрагменты при разных навигациях без необходимости предоставлять их снова при переходе. В LoginComponent
вы добавите объект в качестве второго аргумента в функцию router.navigate()
и предоставите queryParamsHandling
и preserveFragment
для передачи текущих параметров запроса и фрагмента в следующий маршрут.
1 2 3 4 5 6 7 8 9 |
|
Функция queryParamsHandling
также предоставляет опцию merge
, которая сохраняет и объединяет текущие параметры запроса с любыми предоставленными параметрами запроса при навигации.
Чтобы перейти к маршруту Admin Dashboard после входа в систему, обновите admin-dashboard.component.ts
для обработки параметров запроса и фрагмента.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Параметры запроса и фрагменты также доступны через службу ActivatedRoute
. Как и параметры маршрута, параметры запроса и фрагменты предоставляются в виде Observable
.
Обновленный компонент Crisis Admin передает Observable
непосредственно в шаблон с помощью AsyncPipe
.
Теперь вы можете нажать на кнопку Admin, которая приведет вас на страницу входа с предоставленными queryParamMap
и fragment
. После того, как вы нажмете кнопку входа, обратите внимание, что вы были перенаправлены на страницу Admin Dashboard
с параметрами запроса и фрагментом, все еще сохранившимися в адресной строке.
Вы можете использовать эти постоянные биты информации для вещей, которые необходимо предоставлять на разных страницах, например, токены аутентификации или идентификаторы сессии.
Параметры query params
и fragment
также могут быть сохранены с помощью RouterLink
с привязками queryParamsHandling
и preserveFragment
соответственно.
Веха 6: Асинхронная маршрутизация¶
По мере прохождения этапов приложение, естественно, становилось все больше. В какой-то момент вы достигнете точки, когда приложение будет долго загружаться.
Чтобы решить эту проблему, используйте асинхронную маршрутизацию, которая загружает функциональные модули лениво, по запросу. Ленивая загрузка имеет несколько преимуществ.
- Вы можете загружать функциональные области только по запросу пользователя.
- Вы можете ускорить время загрузки для пользователей, которые посещают только определенные области приложения
- Вы можете продолжать расширять области функций с ленивой загрузкой, не увеличивая размер начального пакета загрузки.
Вы уже прошли часть этого пути. Разбив приложение на модули —AppModule
, HeroesModule
, AdminModule
и CrisisCenterModule
— вы получили естественных кандидатов для ленивой загрузки.
Некоторые модули, например AppModule
, должны быть загружены с самого начала. Но другие можно и нужно загружать в ленивом режиме.
Например, модуль AdminModule
нужен нескольким авторизованным пользователям, поэтому его следует загружать только по запросу нужных людей.
Конфигурация маршрута с ленивой загрузкой¶
Измените путь admin
в admin-routing.module.ts
с 'admin'
на пустую строку, ''
, пустой путь.
Используйте маршруты с пустым путем для группировки маршрутов вместе без добавления дополнительных сегментов пути к URL. Пользователи по-прежнему будут посещать /admin
, а AdminComponent
по-прежнему будет служить компонентом маршрутизации, содержащим дочерние маршруты.
Откройте AppRoutingModule
и добавьте новый маршрут admin
в его массив appRoutes
.
Задайте ему свойство loadChildren
вместо свойства children
. Свойство loadChildren
принимает функцию, которая возвращает обещание, используя встроенный в браузер синтаксис для ленивой загрузки кода с использованием динамического импорта import('...')
.
Путь — это расположение `AdminModule' (относительно корня приложения).
После запроса и загрузки кода Promise
разрешает объект, содержащий NgModule
, в данном случае AdminModule
.
1 2 3 4 |
|
При использовании абсолютных путей расположение файла NgModule
должно начинаться с src/app
для корректного разрешения. Для пользовательского отображения путей с абсолютными путями необходимо настроить свойства baseUrl
и paths
в проекте tsconfig.json
.
Когда маршрутизатор переходит к этому маршруту, он использует строку loadChildren
для динамической загрузки AdminModule
. Затем он добавляет маршруты AdminModule
в свою текущую конфигурацию маршрутов. Наконец, он загружает запрошенный маршрут в целевой компонент администратора.
Ленивая загрузка и переконфигурация происходят только один раз, когда маршрут запрашивается впервые; модуль и маршруты сразу же становятся доступными для последующих запросов.
Сделайте последний шаг и отделите набор функций администратора от основного приложения. Корневой AppModule
не должен загружать или ссылаться на AdminModule
или его файлы.
В файле app.module.ts
удалите оператор импорта AdminModule
из верхней части файла и удалите AdminModule
из массива imports
модуля NgModule.
canMatch
: защита несанкционированного доступа к функциональным модулям¶
Вы уже защищаете модуль AdminModule
защитой canActivate
, которая предотвращает доступ неавторизованных пользователей к области функций администратора. Он перенаправляет на страницу входа в систему, если пользователь не авторизован.
Но маршрутизатор все равно загружает AdminModule
, даже если пользователь не может посетить ни один из его компонентов. В идеале, вы должны загружать AdminModule
, только если пользователь вошел в систему.
Защита canMatch
контролирует, пытается ли Router
соответствовать Route
. Это позволяет вам иметь несколько конфигураций маршрутов
, которые имеют один и тот же путь
, но сопоставляются на основе различных условий. Такой подход позволяет маршрутизатору
вместо подстановочного знака маршрут
.
Существующий authGuard
содержит логику для поддержки защиты canMatch
.
Наконец, добавьте authGuard
к свойству массива canMatch
для маршрута admin
. Готовый маршрут администратора выглядит следующим образом:
1 2 3 4 5 |
|
Предварительная загрузка: фоновая загрузка областей функций¶
Помимо загрузки модулей по требованию, вы можете загружать модули асинхронно с помощью предварительной загрузки.
Модуль AppModule
загружается с нетерпением при запуске приложения, что означает, что он загружается сразу. Теперь AdminModule
загружается только тогда, когда пользователь нажимает на ссылку, что называется ленивой загрузкой.
Предварительная загрузка позволяет загружать модули в фоновом режиме, чтобы данные были готовы к отображению, когда пользователь активирует определенный маршрут. Рассмотрим кризисный центр. Это не первый вид, который видит пользователь. По умолчанию Герои являются первым видом. Для наименьшей начальной полезной нагрузки и быстрого времени запуска вам следует нетерпеливо загружать AppModule
и HeroesModule
.
Вы можете лениво загрузить Кризисный центр. Но вы почти уверены, что пользователь посетит Кризисный центр в течение нескольких минут после запуска приложения. В идеале приложение должно запускаться только с загруженными AppModule
и HeroesModule
, а затем, почти сразу, загружать CrisisCenterModule
в фоновом режиме. К тому времени, когда пользователь переходит в Кризисный центр, его модуль уже загружен и готов к работе.
Как работает предварительная загрузка¶
После каждой успешной навигации маршрутизатор ищет в своей конфигурации незагруженный модуль, который он может предварительно загрузить. Будет ли он предварительно загружать модуль, и какие модули он предварительно загружает, зависит от стратегии предварительной загрузки.
Маршрутизатор Router
предлагает две стратегии предварительной загрузки:
Стратегии | Подробности |
---|---|
Без предварительной загрузки | По умолчанию. Лениво загруженные области функций по-прежнему загружаются по требованию. |
Предварительная загрузка | Все лениво загруженные области функций предварительно загружаются. |
Маршрутизатор либо никогда не выполняет предварительную загрузку, либо выполняет предварительную загрузку каждого лениво загруженного модуля. Маршрутизатор также поддерживает пользовательские стратегии предварительной загрузки для точного контроля над тем, какие модули и когда предварительно загружать.
Этот раздел поможет вам обновить модуль CrisisCenterModule
, чтобы он загружался лениво по умолчанию и использовал стратегию PreloadAllModules
для загрузки всех лениво загруженных модулей.
Ленивая загрузка кризисного центра¶
Обновите конфигурацию маршрута для ленивой загрузки модуля CrisisCenterModule
. Выполните те же шаги, что и при настройке AdminModule
для ленивой загрузки.
- Измените путь
crisis-center
вCrisisCenterRoutingModule
на пустую строку. - Добавьте маршрут
кризисного центра
вAppRoutingModule
. - Установите строку
loadChildren
для загрузки модуляCrisisCenterModule
. - Удалите все упоминания о
CrisisCenterModule
изapp.module.ts
.
Вот обновленные модули до включения предварительной загрузки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
Вы можете попробовать сделать это сейчас и убедиться, что модуль CrisisCenterModule
загружается после нажатия кнопки "Crisis Center".
Чтобы включить предварительную загрузку всех лениво загруженных модулей, импортируйте маркер PreloadAllModules
из пакета Angular router.
Второй аргумент в методе RouterModule.forRoot()
принимает объект для дополнительных опций конфигурации. Одним из таких параметров является preloadingStrategy
. Добавьте маркер PreloadAllModules
к вызову forRoot()
:
1 2 3 4 |
|
Это настраивает предзагрузчик Router
на немедленную загрузку всех лениво загруженных маршрутов (маршруты со свойством loadChildren
).
Когда вы посещаете сайт http://localhost:4200
, маршрут /heroes
загружается сразу после запуска, а маршрутизатор начинает загружать CrisisCenterModule
сразу после загрузки HeroesModule
.
Пользовательская стратегия предзагрузки¶
Предварительная загрузка каждого лениво загруженного модуля хорошо работает во многих ситуациях. Однако, учитывая такие вещи, как низкая пропускная способность и пользовательские показатели, вы можете использовать пользовательскую стратегию предварительной загрузки для определенных функциональных модулей.
Этот раздел поможет вам добавить пользовательскую стратегию, которая будет предзагружать только те маршруты, флаг data.preload
которых установлен на true
. Напомним, что в свойство data
маршрута можно добавить что угодно.
Установите флаг data.preload
в маршруте crisis-center
в модуле AppRoutingModule
.
1 2 3 4 5 |
|
Создайте новую службу SelectivePreloadingStrategy
.
1 |
|
Замените содержимое selective-preloading-strategy.service.ts
на следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
SelectivePreloadingStrategyService
реализует PreloadingStrategy
, которая имеет один метод preload()
.
Маршрутизатор вызывает метод preload()
с двумя аргументами:
- Маршрут, который необходимо рассмотреть.
- Функция загрузчика, которая может асинхронно загружать маршрутизируемый модуль.
Реализация preload
должна возвращать Observable
. Если маршрут выполняет предварительную загрузку, он возвращает наблюдаемую, полученную при вызове функции-загрузчика. Если маршрут не загружается, он возвращает Observable
в значении null
.
В этом примере метод preload()
загружает маршрут, если флаг маршрута data.preload
является истинным. Мы также пропускаем загрузку маршрута
, если есть защита canMatch
, потому что пользователь может не иметь к ней доступа. не имеет к нему доступа.
В качестве побочного эффекта, SelectivePreloadingStrategyService
записывает путь
выбранного маршрута в свой публичный массив preloadedModules
.
Вскоре вы расширите AdminDashboardComponent
, чтобы внедрить этот сервис и отобразить его массив preloadedModules
.
Но сначала внесите несколько изменений в AppRoutingModule
.
- Импортируйте
SelectivePreloadingStrategyService
вAppRoutingModule
. - Замените стратегию
PreloadAllModules
в вызовеforRoot()
на этуSelectivePreloadingStrategyService
.
Теперь отредактируйте AdminDashboardComponent
для отображения журнала предварительно загруженных маршрутов.
- Импортируйте
SelectivePreloadingStrategyService
. - Вставьте его в конструктор приборной панели.
- Обновите шаблон, чтобы отобразить массив
preloadedModules
службы стратегий.
Теперь файл выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Как только приложение загрузит начальный маршрут, модуль Кризис-центр
будет предварительно загружен. Убедитесь в этом, войдя в область функций Admin
и отметив, что кризис-центр
находится в списке предзагруженных модулей
.
Он также регистрируется в консоли браузера.
Перенос URL-адресов с помощью перенаправлений¶
Вы настроили маршруты для перемещения по вашему приложению и используете навигацию императивно и декларативно. Но, как и в любом приложении, требования со временем меняются. Вы настроили ссылки и навигацию на /heroes
и /hero/:id
из компонентов HeroListComponent
и HeroDetailComponent
.
Если бы существовало требование, чтобы ссылки на героев
стали супергероями
, вы бы все равно хотели, чтобы предыдущие URL были корректными для навигации. Вы также не хотите обновлять каждую ссылку в вашем приложении, поэтому перенаправления делают рефакторинг маршрутов тривиальным.
Изменение /heroes
на /superheroes
¶
Этот раздел поможет вам перенести маршруты Hero
на новые URL. Маршрутизатор Router
проверяет наличие перенаправлений в вашей конфигурации перед началом навигации, поэтому каждое перенаправление запускается по мере необходимости.
Чтобы поддержать это изменение, добавьте перенаправления со старых маршрутов на новые в heroes-routing.module
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Обратите внимание на два разных типа перенаправления. Первое изменение — с /heroes
на /superheroes
без каких-либо параметров. Второе изменение — с /hero/:id
на /superhero/:id
, которое включает параметр маршрута :id
. Перенаправления маршрутизатора также используют мощное сопоставление шаблонов, поэтому Router
проверяет URL и заменяет параметры маршрута в path
на соответствующие назначения. Ранее вы переходили на URL, например /hero/15
, с параметром маршрута id
, равным 15
.
Маршрутизатор Router
также поддерживает параметры запроса и фрагмент при использовании перенаправлений.
- При использовании абсолютных перенаправлений
Router
использует параметры запроса и фрагмент изredirectTo
в конфигурации маршрута. - При использовании относительных перенаправлений
Router
использует параметры запроса и фрагмент из исходного URL
В настоящее время пустой маршрут перенаправляет на /heroes
, который перенаправляет на /superheroes
. Это не будет работать, потому что Router
обрабатывает перенаправления один раз на каждом уровне конфигурации маршрутизации. Это предотвращает цепочку перенаправлений, которая может привести к бесконечным петлям перенаправления.
Вместо этого обновите пустой маршрут пути в app-routing.module.ts
, чтобы он перенаправлял на /superheroes
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
Ссылка routerLink
не привязана к конфигурации маршрута, поэтому обновите связанные с ней ссылки маршрутизатора, чтобы они оставались активными при активном новом маршруте. Обновите шаблон app.component.ts
для /heroes
routerLink
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
Обновите метод goToHeroes()
в hero-detail.component.ts
для перехода к /superheroes
с необязательными параметрами маршрута.
1 2 3 4 5 6 7 |
|
После установки перенаправлений все предыдущие маршруты теперь указывают на новые места назначения, и оба URL по-прежнему функционируют как положено.
Проверка конфигурации маршрутизатора¶
Чтобы определить, действительно ли ваши маршруты оцениваются в правильном порядке, вы можете проверить конфигурацию маршрутизатора.
Для этого нужно внедрить маршрутизатор и записать в консоль его свойство config
. Например, обновите AppModule
следующим образом и посмотрите в окне консоли браузера готовую конфигурацию маршрута.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Готовое приложение¶
Готовое приложение маршрутизатора смотрите в примере для окончательного исходного кода.