Давным-давно веб-страницы были настоящими HTML-страницами. При переходе на веб-сайт ваш браузер получал и отображал HTML-документ. Нажимая кнопку, ведущую на другую страницу, вы запрашивали новый документ HTML.

Эти времена более или менее прошли. Большинство веб-сайтов теперь используют JavaScript для динамической загрузки контента и адаптации того, что вы видите. Технически, когда вы перемещаетесь по веб-сайту, вы остаетесь в одном и том же месте, изменяется только отображаемый вам контент. Это сокращает время загрузки за счет экономии объема передаваемых данных. Такие веб-сайты называются одностраничными приложениями (SPA).

Проблема с этим подходом заключается в том, что, поскольку страница технически не изменяется, когда пользователь нажимает кнопку, URL-адрес тоже не изменяется. Вы не можете сохранять ссылки на определенный контент на странице и не можете обмениваться ими с людьми. Поэтому нам необходимо адаптировать то, что отображается в адресной строке, когда пользователь перемещается, и отображать правильный контент, когда пользователь нажимает на ссылку. Это роль маршрутизаторов. Angular - это полноценный фреймворк, поэтому он имеет собственный Angular Router.

Вот моя точка зрения: поскольку исторически URL-адреса - это пути к страницам, мы забываем тот факт, что Angular Router более гибкий, чем это. Он связывает представления и пути. На примере мы увидим, насколько это может быть мощнее.

Базовая маршрутизация страницы

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

ng new fridge-inventory --routing --style=scss

Нам нужен сервис для доставки наших данных, компонент для каждой вкладки и общий компонент списка. Генерируем все это:

ng g service services/inventory
ng g component components/dairies
ng g component components/fruitsAndVegetables
ng g component components/meatAndEggs
ng g component components/list

Служба инвентаризации предоставляет списки строк (молочные продукты, фрукты и овощи,…). Сервис внедрен в три компонента: dairies.component.ts, fruitAndVegetables.component.ts и meatAndEggs.component.ts. Каждый из них берет интересующие его данные и передает их в качестве входных данных компоненту списка, который затем отображает их правильно. Вы можете посмотреть этот коммит.

Что нас сейчас интересует, так это маршрутизация. В зависимости от того, какая кнопка нажата, мы хотим отобразить другое представление. В app-routing.module.ts мы определяем маршруты, то есть сообщаем маршрутизатору, какой компонент отображать при вызове заданного пути.

Каждая кнопка имеет атрибут routerLink, который сообщает маршрутизатору, какой маршрут открыть при нажатии. Он также имеет атрибут routerActiveLink, который позволяет нам назначить класс css для активной кнопки. Чтобы сообщить Angular, что делать с компонентом, который дает нам маршрутизатор, мы используем директиву router-outlet. Это заполнитель, который отмечает место, где должен отображаться этот компонент. В нашем случае прямо под кнопками.

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

Используйте именованную розетку

Теперь мы хотим разрешить пользователю добавлять элементы в список покупок. Нам нужен сервис, в котором будет храниться список, и компонент для его отображения.

ng g service services/grocery-list
ng g component components/grocery-list

Мы хотим отобразить кнопку «Открыть список покупок» в нижнем колонтитуле. При нажатии на эту кнопку список должен открыться в правой части экрана, а кнопка «Закрыть список покупок» должна отображаться в нижнем колонтитуле. Пользователь может переключать вкладки, это не влияет на список.

Вы также можете заметить, что ссылка меняется при открытии и закрытии списка покупок.

Список и его кнопки отображаются из нижнего колонтитула страницы. Это не оптимально, но для примера подойдет. Список имеет абсолютную позицию. Чтобы стилизовать нижний колонтитул и любую кнопку в нем, мы используем проекцию контента. Создаем нижний колонтитул компонента . Мы передаем наши кнопки в качестве содержимого этому компоненту, но компонент footer заботится о стилизации нижнего колонтитула и кнопок с помощью :: ng-deep . Чтобы узнать больше об этом шаблоне, вы можете проверить этот ресурс.

В app.component.html мы добавляем вторичный маршрутизатор-выход. У нас может быть только один основной безымянный router-outlet, второму нужно имя. Мы называем его groceryList.

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

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

ng g component components/open-grocery-list-button
ng g component components/grocery-list

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

По умолчанию, когда путь пуст, мы отображаем кнопку для открытия списка:

Эта кнопка также имеет ссылку routerLink, но выглядит немного иначе. Это полный синтаксис, то, что мы видели ранее, было синтаксическим сахаром для наиболее распространенного случая первичного выхода. На этот раз мы сообщаем маршрутизатору, к какой розетке относится маршрут, и указываем ему путь. Исходя из этого, маршрутизатор находит маршрут и отображает нужный компонент в нашей вторичной розетке. Вы можете видеть, что мы передаем объект как выходы. Это означает, что теоретически мы можем изменить путь сразу многих торговых точек. Мы меняем только один, при нажатии на кнопку открывается список продуктов и изменяется адрес.

В нашем компоненте списка есть текстовое поле и кнопка. Эта кнопка имеет ссылку routerLink, которая выглядит как та, что была у нас только что. Щелчком по кнопке мы очищаем вторичный выход и его путь, снова отображая компонент OpenGroceryListButton.

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

Вы можете найти весь проект здесь.