I'm working on a project converting an AngularJS app to Angular and I'm facing a blocker regarding routing.
TL/DR: I need routes to be defined based on an API response before the routing module is used.
Working scenario in AngularJS: (Sort of pseudo code further below)
There are several base routes that exist for everyone, these are defined in the standard AngularJS way:
/home
/settings
...etc
Then there are dynamic routes that are created based on an API response
/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
The basic workflow of the existing AngularJS app:
- The AngularJS app is NOT bound to an element immediately using ng-app, like is usually done.
- A raw (or jQuery) response is received from the API on page load.
- The AngularJS app is initialized using:
angular.bootstrap(document.getElementById('mainElementId'),[‘appName']);
This works because of AngularJS's behavior of not calling .config() on load but on bootstrap of the angular app, which we postpone until after the API response.
Sample AngularJS that works today:
<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 (or, seemingly any recent version of Angular), routes are defined in the routing module before initialization of the main component:
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: '', component: DashboardComponent },
{ path: 'home', component: DashboardComponent },
{ path: 'settings', component: SettingsComponent },
];
Using router.resetConfig
does not work
Let's say I have the main module load the API config first, then use resetConfig
based on the response. This works great if the first page a user loads is /
or /home
or one of the other predefined routes: The new dynamic routes are created and navigation to them works.
However, if a user navigates directly to a route that's not predefined, (let's say /godzilla) the router doesn't even allow the page to load (or) if the wildcard route is set, brings up the 404. The ngOnInit() in the main component (which I was trying to use to load the API response) never gets a chance to run.
Question is: How can I create routes based on the API response before the router navigation is executed or even initialized?