Как динамически создавать маршруты перед инициализацией в Angular / Angular 9 - PullRequest
3 голосов
/ 09 мая 2020

Я работаю над проектом по преобразованию приложения AngularJS в Angular, и я сталкиваюсь с блокировщиком маршрутизации.

TL / DR: Мне нужно определить маршруты на основе ответа API перед использованием модуля маршрутизации.

Рабочий сценарий в AngularJS: (Тип псевдокода далее ниже)

Есть несколько базовых маршрутов, которые существуют для всех, они определены стандартным AngularJS способом:

/home
/settings
...etc

Затем есть динамические c маршруты которые создаются на основе ответа 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

Основной c рабочий процесс существующего AngularJS приложения:

  1. AngularJS приложение НЕ привязывается к элементу сразу с помощью ng-app, как это обычно делается.
  2. Необработанный (или jQuery) ответ получен от API при загрузке страницы.
  3. Приложение AngularJS инициализируется с использованием:
 angular.bootstrap(document.getElementById('mainElementId'),[‘appName']);

Это работает из-за того, что AngularJS не вызывает .config () при загрузке. бу t на bootstrap приложения angular, которое мы откладываем до ответа 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>

In 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 или один из других предопределенных маршрутов: создаются новые маршруты Dynami c и навигация по ним работает.

Однако, если пользователь переходит непосредственно к маршруту, который не определен заранее (скажем, / godzilla), маршрутизатор даже не позволяет загрузиться странице (или), если задан маршрут с подстановочными знаками, вызывает сообщение 404. ngOnInit () в основном компонент (который я пытался использовать для загрузки ответа API) никогда не запускается.

Вопрос: Как я могу создать маршруты на основе ответа API перед навигацией маршрутизатора выполняется или даже инициализируется?

Ответы [ 3 ]

0 голосов
/ 09 мая 2020

Как я могу создать маршруты на основе ответа API до того, как навигация маршрутизатора будет выполнена или даже инициализирована?

Есть два способа сделать это.

Первый способ - использовать Component для отображения всех динамических c маршрутов. Сначала определяются все маршруты stati c, а в конце маршруты dynamic c направляются в DynamicComponent с параметром маршрутизации id. В DynamicComponent мы используем ActivatedRoute для получения параметра маршрутизации и используем Router для перехода к маршруту 404 в случае сбоя.

In 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 },
];

In 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

    })


  }
}

Второй способ - использовать Service для фильтрации всех неизвестных маршрутов. Все маршруты stati c определяются в первую очередь и, наконец, маршруты dynamic c направляются в DynamicComponent, отфильтрованный DynamicRouteService, который реализует CanActivate. В DynamicRouteService мы сопоставляем next.params, чтобы вернуть Observable<boolean> в модуль маршрутизатора и удерживать маршрутизацию до тех пор, пока не будет выполнено наблюдаемое.

In 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] },
];

Примечание: не забудьте добавить DynamicRouteService к providers in app.module.ts

In 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 голосов
/ 11 мая 2020

Спасибо за ответы.

В итоге я решил эту проблему по-другому

Я шел по пути создания компонента «DynamicRouter», но нашел гораздо более простое решение, используя APP_INITIALIZER.

Я ответил на это в: Angular маршрутизация нагрузки из службы REST перед инициализацией

0 голосов
/ 09 мая 2020

Я добавляю динамические c маршруты, предварительно определяя шаблоны 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 }
];

Маршруты сопоставляются по порядку, поэтому «известные» маршруты должны go первыми. Любой 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 - некоторая функция, которая проверяет приложение имя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...