Angular. Роутинг для продвинутых — мешаем каталог с сайтом

Запись от 12.09.2017
Задача такая — преобразовать навигацию по сайту:
/
/news
/contacts
/catalog
/catalog/:sectionCode
/catalog/:sectionCode/news
/catalog/:sectionCode/shares
** (404 error)
 -> /
/news
/contacts

/:sectionCode
/:sectionCode/news
/:sectionCode/shares
** (404 error)

То есть, исключить разводящую страницу «/catalog» и переместить все разделы уровнем ниже, при этом не смешав с 404-ой ошибкой.

Для этой задачи понадобится создать сервис, который позволит переключать правила навигации с дефолтной навигации:
/
/news
/contacts
**
на навигацию по каталогу:
/:sectionCode
/:sectionCode/news
/:sectionCode/shares
**

Плюс, надо будет отслеживать текущий роут «activatedRoute» в компоненте, который привяжем к правилу «**» дефолтной навигации.

Так что для начала из основного роутинга приложения (app-routing.module.ts) убираем правила роутинга для каталога и назначаем в конце маршрутов:
{
  path: '**',
  component: CatalogRoutingComponent,
}

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

Код компонента «CatalogRoutingComponent»:
@Component({
	selector: 'app-catalog-routing',
	template: '<app-common-page *ngIf="showCommonPage"></app-common-page>',
})
export class CatalogRoutingComponent implements OnInit {
	public showCommonPage: boolean = false;

	constructor(
		private activatedRoute: ActivatedRoute,
		private catalogRoutingService: CatalogRoutingService,
		private router: Router,
	) {}

	public ngOnInit(): void {
		this.activatedRoute.url.subscribe(params => {
			if (this.catalogRoutingService.checkUrlSegments(params)) {
				this.catalogRoutingService.setCatalogRoutes();
			}

			this.updateShowCommonPage();
		});


		const routeSubscription = this.router.events.subscribe(event => {
			if (event instanceof NavigationStart) {
				const urlTree = this.router.parseUrl(event['url']);

				if (urlTree) {
					const urlSegments = (urlTree.root.children['primary'])
						? urlTree.root.children['primary'].segments : [];

					if (!this.catalogRoutingService.checkUrlSegments(urlSegments)) {
						this.catalogRoutingService.setDefaultRoutes();
						this.updateShowCommonPage();
					}
				}
			}
		});

		this.catalogRoutingService.setRouteSubscription(routeSubscription);
	}

	private updateShowCommonPage(): void {
		this.showCommonPage = !this.catalogRoutingService.getIsCatalogRoutes();
	}

}

Здесь компонент подписывается на изменения адреса страницы и в зависимости от него через сервис назначает дефолтный роутинг или роутинг каталога. А переменная «showCommonPage» используется для передачи управления следующему компоненту в правиле «**», что видно по используемому шаблону.

Остается дело за сервисом (провайдится в app):
@Injectable()
export class CatalogRoutingService {
	private isCatalogRoutes: boolean = false;
	private catalogRoutes: Routes = [{
		path: ':sectionCode',
		component: PageCatalogSectionComponent,
	}, {
		path: ':sectionCode/news',
		component: PageCatalogNewsComponent,
	}, {
		path: ':sectionCode/shares',
		component: PageCatalogSharesComponent,
	}];
	private defaultRoutes: Routes;
	private routeSubscription: Subscription;

	constructor(
		private router: Router,
	) {
		this.defaultRoutes = this.router.config;
	}

	public getIsCatalogRoutes(): boolean {
		return this.isCatalogRoutes;
	}

	public setCatalogRoutes(): void {
		this.isCatalogRoutes = true;
		this.router.resetConfig(this.catalogRoutes);
		this.router.navigateByUrl(this.router.url);
	}

	public setDefaultRoutes(): void {
		this.router.resetConfig(this.defaultRoutes);
		this.isCatalogRoutes = false;
	}

	public setRouteSubscription(subscription: Subscription): void {
		if (this.routeSubscription) {
			this.routeSubscription.unsubscribe();
		}

		this.routeSubscription = subscription;
	}

	public checkUrlSegments(url: UrlSegment[]): boolean {
		return true; // Некоторое условие для проверки `url`
	}

}

Переменные:
isCatalogRoutes — true, если активен роутинг каталога;
catalogRoutes — правила роутинга для каталога и его подразделов;
defaultRoutes — здесь будет храниться дефолтный конфиг роутинга;
routeSubscription — подписка на изменения роутинга, что бы не утекала память на множестве подписок.

По методам добавить нечего, названия достаточно ёмкие. Только в checkUrlSegments() должна быть написана действительная проверка, является ли url частью каталога. Тут может быть что угодно: например, проверка по заранее готовому массиву, или обращение к бекенду за актуальными кодами разделов или что-то ещё.