在Angular Angular 9中如何在初始化前动态创建路由?

问题描述 投票:0回答:1

我正在做一个项目,将一个AngularJS应用转换为Angular,我面临着一个关于路由的阻塞。

TLDR。我需要在使用路由模块之前,根据API响应来定义路由。

AngularJS中的工作场景。(下面再讲讲有点伪代码)

每个人都有几个基础路由存在,这些路由是以标准的AngularJS方式定义的。

/home
/settings
...etc

然后有一些动态路由是基于API响应创建的

/purchase-requests
/invoices
/godzilla
...etc. Content doesn’t matter, basically, a dynamic list of routes that an existing API gives as an array of strings

现有AngularJS应用的基本工作流程。

  1. AngularJS应用程序并不像通常那样使用ng-app立即绑定到元素上。
  2. 在页面加载时,从API接收到一个原始(或jQuery)响应。
  3. AngularJS应用是用ng-app初始化的。
 angular.bootstrap(document.getElementById('mainElementId'),[‘appName']);

这是因为AngularJS的行为是不在加载时调用.config(),而是在angular应用的bootstrap时调用,我们将其推迟到API响应之后。

今天工作的AngularJS样本。

<script>

  let appList = [];
  const mainApp = angular.module('mainApp', ['ngRoute']);


  // Controllers
  mainApp.controller('mainController', mainController);
  mainApp.controller('homeController', homeController);
  mainApp.controller('appListController', appListController);
  mainApp.controller('appSingleController', appSingleController);
  mainApp.controller('errorController', errorController);

  // config will not be called until the app is bootstrapped
  mainApp.config(function($routeProvider) {

    // Default routes that everyone gets
    $routeProvider.when('/', { templateUrl: 'views/home.html', controller: 'homeController'});
    $routeProvider.when('/home', { templateUrl: 'views/home.html', controller: 'homeController'});

    // Add the dynamic routes that were retreived from the API
    for (let appName in appList) {
      $routeProvider.when(`/${appName}`, { templateUrl: 'views/app-list.html', controller: 'appListController'});
      $routeProvider.when(`/${appName}/new`, { templateUrl: 'views/app-single.html', controller: 'appSingleController'});
      $routeProvider.when(`/${appName}/view/:itemNumber`, { templateUrl: 'views/app-single.html', controller: 'appSingleController'});
    }

    $routeProvider.otherwise({ templateUrl: 'views/error.html', controller: 'errorController'});
  });



  $(document).ready(function() {
    const options = {
      type: 'GET',
      url: '/api/apps/getAvailableApps',
      success: onAppSuccess,
    };
    $.ajax(options);
  });

  function onAppSuccess(response) {
    appList = response.appList;
    angular.bootstrap(document.getElementById('mainApp'), ['mainApp']);
  }

</script>

<!-- Typically, you bind to the app using ng-app="mainApp" -->
<div id="mainApp" class="hidden" ng-controller="mainController">

  <!-- Route views -->
  <div ng-view></div>

</div>

在Angular 9(或者,似乎是任何最新版本的Angular)中,路由是在主组件初始化之前在路由模块中定义的。

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: '', component: DashboardComponent },
  { path: 'home', component: DashboardComponent },
  { path: 'settings', component: SettingsComponent },
];

使用 router.resetConfig 行不通

比方说,我让主模块先加载API配置,然后用 resetConfig 基于响应。如果用户加载的第一个页面是 //home 或其他预定义的路径之一。新的动态路由被创建,导航到它们的时候就可以了。

然而,如果用户直接导航到一个非预定义的路由,(比方说godzilla)路由器甚至不允许页面加载(或者)如果设置了通配符路由,就会弹出404。主组件中的ngOnInit()(我试图用它来加载API响应)从来没有机会运行。

问题是 我怎样才能在路由器导航被执行甚至被初始化之前 根据API响应创建路线?

javascript angular typescript angular-router angular9
1个回答
0
投票

我添加动态路由的方式是使用参数预先定义路由url模板。

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: '', component: DashboardComponent },
  { path: 'home', component: DashboardComponent },
  { path: 'settings', component: SettingsComponent },
  { path: ':appName', canActivate: AppGuard, children: [
    { path: '', component: AppListComponent },
    { path: 'new', component: 'NewAppComponent' },
    { path: 'view/:itemNumber', component: AppSingleComponent }
  ] },
  { path: '**', component: ErrorComponent }
];

路由是按顺序匹配的,所以 "已知 "的路由应该先走。任何带有单段的URL,如果没有与 "已知 "路由匹配,则会被匹配到 :appName. 您可以声明一个卫士来验证 :appName 参数是有效的。如果无效,则 '**' 路径将与之匹配。

警卫会是这样的。

@Injectable({ providedIn: 'root' })
export class AppGuard implements CanActivate {
  constructor(private appService: AppService) {
  }

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    const appName = route.params.appName;
    return this.appService.isValidAppName(appName);
  }
}

哪儿 appService.isValidAppName 是一些验证应用名称的函数。


0
投票

如何在路由器导航执行甚至初始化之前,根据API响应创建路由?

有两种方法可以做到这一点。

第一种方法是使用 组成部分 来显示所有的动态路由。首先定义所有静态路由,最后将动态路由路由路由到 DynamicComponent 与路由参数 id. 在 DynamicComponent,我们使用ActivatedRoute来获取路由参数,并使用Router来导航到 404 故障时的路线。

app-routing.module.ts

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: "prefix" },
  { path: 'login', component: LoginComponent },
  { path: 'home', component: DashboardComponent },
  { path: 'settings', component: SettingsComponent },
  { path: '404', component: PageNotFoundComponent },
  { path: ':id', component: DynamicComponent },
];

DynamicComponent

constructor(private aroute: ActivatedRoute, private router: Router) { }

ngOnInit(): void {

  this.aroute.params.pipe(first()).subscribe((param) => {
    console.log(param.id)

    ...   // make any API call with param.id and get a response as promise

    .then( (response) => {

       ...    // do whatever you want to do on success

    })
    .catch( (error) => {

       console.error(error);
       this.router.navigate(['404']);    // route to 404 on failure

    })


  }
}

第二种方法是使用 服务项目 来过滤所有未知路由。首先定义所有的静态路由,最后将动态路由路由路由到 DynamicComponent 过滤的 DynamicRouteService 它实现了 CanActivate. 在 DynamicRouteService,我们映射出 next.params 返回一个 Observable<boolean> 到Router模块,并将保持路由,直到满足可观察的条件。

app-routing.module.ts

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: "prefix" },
  { path: 'login', component: LoginComponent },
  { path: 'home', component: DashboardComponent },
  { path: 'settings', component: SettingsComponent },
  { path: '404', component: PageNotFoundComponent },
  { path: ':id', component: DynamicComponent, canActivate: [DynamicRouteService] },
];

注意:一定要加上 DynamicRouteServiceprovidersapp.module.ts

dynamic-route.service.ts

export class DynamicRouteService implements CanActivate {

  constructor(private router: Router) { }

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

    return next.params.pipe(first()).pipe(map(param) => {
      console.log(param.id)

      return ...   // make any API call with param.id and get a response as promise

      .then( (response) => {

         ...    // do whatever you want to do on success
         return true;

      })
      .catch( (error) => {

         console.error(error);
         this.router.navigate(['404']);    // route to 404 on failure
         return false;

      }))

    }
  }
}

0
投票

谢谢大家的回复。

我最终用不同的方法解决了这个问题

我本来打算用 "DynamicRouter "组件,但发现使用APP_INITIALIZER的解决方案简单多了。

我已经在中回答了这个问题。在初始化之前从REST服务中获取Angular负载路由。

© www.soinside.com 2019 - 2024. All rights reserved.