Научная статья на тему 'КАК СДЕЛАТЬ ДЕКАЛАРАТИВНЫЙ РОУТИНГ ДИАЛОГОВ В ANGULAR НА ПРИМЕРЕ TAIGA UI'

КАК СДЕЛАТЬ ДЕКАЛАРАТИВНЫЙ РОУТИНГ ДИАЛОГОВ В ANGULAR НА ПРИМЕРЕ TAIGA UI Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
61
9
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
Frontend / Angular / декларативность / Taiga UI / диалоги.

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Пурис Дмитрий Николаевич

в статье анализируются два подхода написания роутинга диалогов в Angular: императивный и декларативный.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «КАК СДЕЛАТЬ ДЕКАЛАРАТИВНЫЙ РОУТИНГ ДИАЛОГОВ В ANGULAR НА ПРИМЕРЕ TAIGA UI»

КАК СДЕЛАТЬ ДЕКАЛАРАТИВНЫИ РОУТИНГ ДИАЛОГОВ В ANGULAR НА

ПРИМЕРЕ TAIGA UI Пурис Д.Н.

Пурис Дмитрий Николаевич - эксперт-разработчик, Направление: Frontend, Tinkoff, г. Тверь

Аннотация: в статье анализируются два подхода написания роутинга диалогов в Angular: императивный и декларативный.

Ключевые слова: Frontend, Angular, декларативность, Taiga UI, диалоги.

Часто на фронтенде нужно открывать модальные окна по определенному пути. Изначально ангуляр не предоставляет такой возможности, так же как и популярные ui-kit-библиотеки. И разработчики каждый раз ищут способ, как это сделать. Предлагаю на примере простой задачи разобрать два подхода к созданию модальных окон, связанных с url: императивный и декларативный. Условие задачи

Нужно открыть по url /page/dialog диалог поверх страницы page.

Рис. 1. Схема веб-страницы с открытым диалогом.

Подход первый — императивный

На странице page в методе ngOnInit или конструкторе проверяем текущий url. Если там содержится путь до диалога — открываем диалог. Звучит понятно и просто, в коде выглядит так:

this.tuiDialogService.open(DialogContentComponent).subscribe();

Рис. 2. Исходный код PageComponent.

В Taiga UI([1]) диалоги открываются методом open, который возвращает Observable. При подписке на Observable диалог открывается, а при отписке — закрывается. Чтобы по url на диалог открывалась страница page, нужно сконфигурировать роутер:

const router: Routes = [ {

path: 'page', component: PageComponent,

{

// по прямому пути на диалог будет открываться страница path: 'page/dialog1, component: PageComponent,

Рис. 3. Исходный код роутинга.

Теперь при переходе по прямой ссылке /page/dialog откроется диалог поверх страницы. Важно учесть, что при закрытии диалога нужно менять роутинг обратно на родительскую страницу. Пример

реализации:

constructor(private readonly location: Locatioi ) {}

Рис. 4. Исходный код DialogContentComponent с location.

При закрытии диалога вызывается метод, возвращающий на предыдущую страницу. Но есть неприятный нюанс: если открыть диалог по прямой ссылке, например, с главной страницы Google, то при закрытии случится редирект обратно на Google. Эту проблему можно исправить, если знать «обратный путь» от диалога до страницы.

}

Рис. 5. Исходный код DialogContentComponent c router.

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

Но метод возвращения должен знать конкретный путь до диалога. Если нужно будет поменять путь до диалога, например, на page/path/to/dialog — придется менять и код возвращения: с this.router.navigate(['..', {relativeTo: this.route}) на this.router.navigate(['../../..', {relativeTo: this.route}).

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

Минусы решения:

1. Копирование кода из ngOnInit при добавлении диалога на новую страницу — не переиспользуемо.

2. Диалог должен знать путь до родителя — нарушение границ ответственности.

3. Страница-родитель должна знать путь до диалога.

4. Тянутся лишние зависимости в компонент страницы.

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

Подход второй — декларативный

При реализации переиспользуемого механизма модальных окон, связанных с url, нужно задать два вопроса:

1. Можно ли сделать проще?

2. Можно ли использовать готовые механизмы роутинга в ангуляре?

Навигация для страниц выглядит так:

Рис. 6. Исходный код примера конфигруации роутинга в ангуляре для страниц.

В app-component находится <router-outlet>, в который рендерится компонент текущей страницы по конфигу из роутера. Подход декларативный, понятный, и хочется сделать решение с навигируемыми диалогами в подобном формате с условиями:

• Диалог открывается поверх страницы.

• Удобно конфигурировать.

• Удобно возвращаться.

• Универсально для любых диалогов.

Роутинг ангуляра в минимальном варианте требует два параметра: path и component, или модуль страницы вместо компонента. Особенность в том, что роутинг может работать только с двумя сущностями: компонентом и lazy-loading-модулем, который все равно рендерит компонент. Получается, что роутеру в любом случае нужно подавать компонент.

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

С компонентом-оберткой понятно, а как задавать path для диалога? Под path можно использовать путь относительно родителя — path: 'path/to/dialog'.

Еще один важный момент — открытие диалога поверх страницы, а не вместо нее.

Добиться этого можно в два шага:

1. Добавить на страницу <router-outlet>, чтобы появилась возможность рендерить контент поверх страницы:

Рис. 7. Исходный код page.component.html.

2. Разместить конфигурацию роута на диалог в children-свойстве конфига страницы. В противном случае диалог будст в <router-outlet>^^ на уровне выше вместо текущей страницы:

Рис. 8. Исходный код app-routing.module.ts.

Теперь реализуем компонент-обертку, открывающую переданный компонент диалога:

Рис. 9. Исходный код конструктора RoutableDialogComponent.

Теперь можно открывать любой диалог по любому пути на любой странице. Но осталась проблема с закрытием диалога и возвращением на родительский url. В TaigaUI диалоги открываются подпиской на метод открытия диалогов. При закрытии диалога подписка завершается. Используем этот факт для возвращения:

@Component({... >) class RoutableDialogComponent { constructor^ ) { tuiDialogService .open(...) . subscribed

// при завершении потока возвращаемся complete: О => this.navigateToParent(),

»;

>

private navigateToParent(): void { >

Рис. 10. Исходный код RoutableDialogComponent с методом navigateToParent.

Чтобы отвязать компонент-обертку от конкретных путей, вынесем параметр backUrl в конфигурацию роутера:

Рис. 11. Исходный код app-routing.module.ts.

Было бы здорово не писать backUrl руками, а вычислять из пути. Для этого можно сделать хелпер-генератор роута:

// хелпер принимает только два нужных параметра - компонент и путь function tuiGenerateDialogableRoutefcomponent, path): Route {

// вычисляет обратный путь заменой сегментов пути на две точки

Рис. 12. Исходный код tuiGenerateDialogableRoute.

Хелпер помогает в двух моментах:

1. Скрывает детали реализации backUrl.

2. Позволяет правильно формировать роут, не давая ошибиться программисту. В результате итоговый конфиг роутера для страницы с диалогом будет таким:

tuiGenerateDialogableRoute(DialogContentComponent, 1 path/to/dialog 1)

Рис. 13. Исходный код конфигурации роутинга.

Итого

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

О возвращении можно не задумываться: все скрыто за деталями реализации. И такое решение можно переиспользовать по всему проекту.

Это решение я сделал в онлайн-бухгалтерии для Тинькофф Бизнес и перенес в UI Kit — Taiga UI [2]. Посмотреть документацию о routable dialog [3] можно в описании пользовательского интерфейса. Там же есть и вариант для lazy-loading-диологов [4].

Список литературы

1. Документация по Taiga UI. [Электронный ресурс]. Режим доступа: https://taiga-ui.dev/ (дата обращения: 01.05.2023).

2. Пулл-реквест в Taiga UI на Github. [Электронный ресурс]. Режим доступа: https://github.com/Tinkoff/taiga-ui/pull/3369/ (дата обращения: 01.05.2023).

3. Документация по навигируемым диалогам в Taiga UI. [Электронный ресурс]. Режим доступа: https://taiga-ui.dev/dialog/routable/ (дата обращения: 01.05.2023).

4. Документация по lazy-loading диалогам в Taiga UI. [Электронный ресурс. Режим доступа: https://taiga-ui.dev/dialog/lazy-routable/ (дата обращения: 01.05.2023).

i Надоели баннеры? Вы всегда можете отключить рекламу.