3

In Angular 2.0 stable, I have a application in which I have to define/config routes based on JSON data I receive. I don't have any predefined routes.I am getting this data in the constructor of my bootstrap component.

How can i achieve that? Is it possible?

4
  • You can use router.replaceConfig(routes: Routes), just have to generate routes based on your JSON... Commented Oct 14, 2016 at 9:58
  • That is the issue. At that time i do not have a component as i don't know which one is required. So i cann't make a route like {path:'Page2',component: Page2}, Commented Oct 14, 2016 at 10:01
  • You can use {path:'Page2', loadChildren: ",.module#Page2"}, it's just a string... Commented Oct 14, 2016 at 10:03
  • 1
    Not working for me. Can you please provide a plunkr? Commented Oct 14, 2016 at 10:11

2 Answers 2

6

The way that I achieved this was to load the routes before bootstrapping the angular app. This way when the app starts all the dynamic routes are already there.

So you have to load all the routes in the main.ts file before the platformBrowserDynamic().bootstrapModule(AppModule); line.

Here is an example of the main.ts file where the routes are fetched as a json and loaded before bootstrapping.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { environment } from './environments/environment';
import { AppModule } from './app/app.module';
import { IRoute, IRouteData } from './core/models/route';
import { getComponent } from './core/utils/type-component-mapper';

import { AppRoutes } from './app/app.routes';

export function main() {
    console.log('[main]')
    return platformBrowserDynamic()
        .bootstrapModule(AppModule)
        .catch(err => console.error(err));
}

switch (document.readyState) {
    case 'interactive':
    case 'complete':
        getRoutes();
        break;
    case 'loading':
    default:
        document.addEventListener('DOMContentLoaded', () => getRoutes());
}

function getRoutes() {
    // This is where you load the routes json from your api
    fetch(environment.apiUrl + '/api/routes/get').then((value: Response)  =>                         
    {
        return value.json();
    }).then((routes: IRoute[]) => {
        routes.forEach((o: IRoute) => {
            iterate(o);
        });

        // add each dynamic route to the angular route collection
        AppRoutes.unshift(routes[0]);

        // all dynamic routes have been added, start the angular app
        main();
    });
}

function iterate(route: IRoute) {
    var children = route.children;
    if (children) {
        Array.from(children).forEach((o: IRoute) => {
            iterate(o)
        });
    }

    var component = getComponent(route.data.type);
    if (component) {
            route.component = component;
    }
}

In this example the routes json which is returned from the api would be in this form:

[{
    "path" : Shop,
    "data" : {
        "type" : ShopPage
    },
    "children" : [{
        "path" : Bread,
        "data" : {
            "type" : ProductPage
        }
    },
    {
        "path" : Milk,
        "data" : {
            "type" : ProductPage
        }
    }]
}]

This Json data is parsed into the IRoute and IRouteData types:

export interface IRoute {
    path: string;
    data: IRouteData;
    children?: IRoute[];
}

export interface IRouteData {
    type: string;
}

It is also important that you export your AppRoutes const, so that you can push the new dynamic routes to your AppRoutes collection. It would look something like this:

export const AppRoutes: Routes = [
    {
        path: 'hardCodedWelcome',
        component: WelcomeComponent
    }
];

You also need to add the correct component for each route. In this case, I do a switch on the data type property to get the correct component. This is why I import the get component function from the type-component-mapper file which looks like this:

import { Router, Route } from '@angular/router';

import { Type } from '@angular/core';
import { PageTypes } from './page-types';
import { ShopComponent } from '../../app/Shop/shop.component';
import { ProductComponent } from '../../app/Product/Product.component';

export function getComponent(type: string): Type<any> {
    switch (type) {
        case PageTypes.Shop: {
            return ShopComponent;
        }
        case PageTypes.Product: {
            return ProductComponent;
        }
    }
}

Page types here is just a simple list of page types so I do not have to use magic strings.

export const PageTypes = {
    Shop: 'ShopPage',
    Product: 'ProductPage',
};

When the app starts all dynamic routes are there in the route collection and can be resolved as normal by angular. This adds some startup time to the app, depending on how many routes there are. But once the app has started, all routes are in the config routes collection and there is no further impact on performance.

Note this works only if AOT is false. If you want to use this with AOT, you have to inject the router into the app.component constructor and then add this line: router.resetConfig(allRoutes);where all routes is the array you pushed the routes to before bootstrapping.

Sign up to request clarification or add additional context in comments.

Comments

0

It is possible using APP_INITIALIZER, function which is executed when an application is initialized.

There is a great article about fetching routes from an API when the app is initialized https://long2know.com/2017/11/angular-dynamic-routes-and-application-initialization/

Basically, you have to create a service which fetches the data (routes) from the API, create a factory in AppModule and add it to providers array. The article I've mentioned above describes it very clearly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.