В этой статье я хотел бы сосредоточиться на нескольких проблемах, которые у меня были с компонентом Angular Router. Можно сказать, что я критикую Router за то, что он не подходит для моего варианта использования. Конечно, разработчики Angular создают Angular с учетом некоторых вариантов использования. Это не такая универсальная среда общего использования, и она вообще не подходит для всех случаев использования. Angular Router — отличный тому пример — он едва ли настолько универсален, как можно было бы подумать.

Но часть проблемы, с которой я столкнулся, заключается в том, что мне не объяснили, как правильно использовать Router. Чем больше я его использую, тем больше у меня с ним проблем, и я даже начал думать, что нет правильного способа, как его использовать :(

Иерархические маршруты

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

Сначала я создал маршрут верхнего уровня с помощью LoggedIn Guard, который проверяет, вошел ли пользователь в систему и может ли он перейти по заданному маршруту. Затем я начал использовать то, что я называю маршрутами компоновки.

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

  • родительский маршрут не содержит компонентов
  • родительский маршрут пуст

К сожалению, эти ограничения были слишком ограничивающими для моего варианта использования, и мне пришлось использовать обходной путь. Попробовав несколько вещей, я внедрил ActivatedRoute и Router практически во все свои компоненты. Я прослушал NavigationEndсобытие Router, и когда оно произошло, я использовал AactivatedRoute.snapshot и передал его своей службе, которая затем проходит через все родительский и дочерний маршруты и создает один плоский маршрут со всеми параметрами и данными. Это очень уродливое решение, но оно работает. И это самый важный фактор для меня сейчас.

Но меня начинает беспокоить снижение тестируемости таких компонентов. Имитировать ActivatedRoute.data (или params) теперь недостаточно, я должен также имитировать Router.events. И я начинаю чувствовать, что нарушаю принцип SOLID. Компоненты теперь заботятся не только о своих вещах, но и о маршрутизаторе — они используют данные из маршрутов, не говоря об этом явно. Мне кажется, что это запах кода. Это немного напоминает мне шаблон локатора сервисов (который считается анти-шаблоном и предпочтительнее заменить его конструктором или атрибутом DI).

Изменение маршрута на бескомпонентный

Когда родитель не содержит компонентов (или имеет пустой путь), дочерние маршруты автоматически наследуются, вы не можете предотвратить это. Вы должны справиться с этим. ИМХО, текущая ситуация с иерархическими маршрутами настолько плоха, что я думаю, что было бы лучше, если бы маршруты НИКОГДА не наследуют параметры и данные. Это было бы, по крайней мере, последовательно, если не что-то еще. Теперь у меня есть собственный написанный обходной путь, который отлично работает, когда данные не наследуются, а мне нужно, чтобы они были, но когда они внезапно наследуются, это проблема предотвратить.

Сокращение шаблонов иерархических маршрутов, тестовый хаос

Я пришел с идеей уменьшить шаблон с разрешением иерархических маршрутов. Я добавил большую часть кода в компонент Application и почти не зависел от маршрутизации в других компонентах. Но это привело к хаосу в тестах — мой подготовленный код для имитации маршрутов больше нельзя было использовать таким же образом. И мне пришлось делать совершенно новые обходные пути для тестов.

Конечно, мы можем неправильно понять архитектуру нашего приложения, но работа с маршрутизатором Angular — это огромная головная боль**.