Хорошо, я нашел рабочее решение, которое мне очень нравится: кажется, оно работает хорошо и не выглядит слишком хакерским. На самом деле это не соответствует ни одному из опубликованных предложений, но спасибо за все ответы и комментарии, поскольку они действительно помогли мне найти окончательное решение!
Окончательное решение использует в своей основе защиту, реализующую CanActivate, в сочетании с router.resetConfig()
и router.navigate()
внутри защиты.
В качестве точки входа в свое решение я использую стандартный модуль маршрутизации. Это все еще полезно для моего варианта использования, поскольку есть компоненты с одним статическим маршрутом.
файл: app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './features/home/home.component';
import { StudentListComponent} from './features/student-list/student-list.component';
import { CourseListComponent} from './features/course-list/course-list.component';
import { LoadingComponent } from './features/loading/loading.component';
import { NotFoundComponent } from './features/not-found/not-found.component';
import { DynamicRouteGuard } from './guards/dynamic-route.guard';
const routes: Routes = [
{
path: '/',
component: HomeComponent
},
{
path: 'students',
component: StudentListComponent
},
{
path: 'courses',
component: CourseListComponent
},
{
path: 'not-found',
component: NotFoundComponent
},
{
path: '**',
canActivate: [ DynamicRouteGuard ],
component: LoadingComponent,
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Пути /, студенты, курсы и не найдены — это обычные статические маршруты, как и в большинстве приложений Angular. Любой маршрут, который не соответствует им, будет обработан подстановочным знаком внизу. Этот маршрут загружает компонент, который может содержать счетчик загрузки. Он будет виден, пока Guard асинхронно выполняет вызов API к серверной части, чтобы определить, какой компонент загрузить.
файл: dynamic-route.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { EntityTypeService } from '../../services/entity-type.service';
import { IEntityTypeModel } from '../../models/entity-type.model';
import { EntityType } from '../../models/entity-type.enum';
import { StudentDetailComponent } from '../features/student-detail/student-detail.component';
import { CourseDetailComponent } from '../features/course-detail/course-detail.component';
@Injectable({
providedIn: 'root'
})
export class DynamicRouteGuard implements CanActivate {
constructor(
private entityTypeService : EntityTypeService,
private router : Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// At this point, only routes with a single path element are considered valid routes.
if (route.url.length != 1) {
this.router.navigate(['not-found']);
return false;
}
let path = route.url[0].toString();
// Ask backend, what kind of entity the requested path matches to
this.entityTypeService.getEntityTypeForPath(path).subscribe(response => {
let entityTypeModel = response as IEntityTypeModel;
// If backend did not recognize path --> redirect to page not found component.
if (entityTypeModel.entityType === EntityType.Unknown) {
this.router.navigate(['not-found']);
}
// Build a new routes array. Slicing is required as wildcard route
// should be omitted from the new routes array (risk of endless loop)
let routes = this.router.config;
let newRoutes = routes.slice(0, routes.length - 1);
// Add a new route for the requested path to the correct component.
switch(entityTypeModel.entityType) {
case EntityType.Student:
newRoutes.push({ path: path, component: StudentDetailComponent, data: { resourceName: path } });
break;
case EntityType.Course:
newRoutes.push({ path: path, component: CourseDetailComponent, data: { resourceName: path } });
break;
default:
this.router.navigate(['not-found']);
return;
}
// Reload routes and navigate.
this.router.resetConfig(newRoutes);
this.router.navigate([path]);
});
// Guard always returns true and loads LoadingComponent while API
// request is being executed.
return true;
}
}
В стороже запрошенный маршрут доступен, и можно легко внедрить службу API. Используя эту службу, серверная часть ищет путь в базе данных и возвращает значение перечисления, указывающее, является ли это студентом, курсом или ни тем, ни другим. В зависимости от этого в массив Routes добавляется новый маршрут для этого конкретного имени студента/курса, связанный с соответствующим компонентом. После этого маршруты перезагружаются, и маршрутизатор может напрямую перейти к нужному компоненту.
person
Finrod_Amandil
schedule
17.12.2020
students
или курс с коротким названиемcourses
? - person Andrei Tătar   schedule 15.12.2020{ path: '*', component: FakeComponent, resolve: { route: RouteResolver } }
- person Finrod_Amandil   schedule 15.12.2020path: ':route'
, и тогда я смогу просто работать с именем маршрута в распознавателе/защите. Это звучит как очень многообещающий подход, я попробую, спасибо! - person Finrod_Amandil   schedule 15.12.2020{path: '/students', component: ......, data: {key: someNecessary data which needed in the guard } }
, а в гвардии вы можете получить егоroute.data
. - person Ashot Aleqsanyan   schedule 15.12.2020