5
votes

I have an Angular 5 application which can dynamically load feature modules and display their content in the main router outlet. The code below show my basic configuration:

app.component from root module (the main router outlet):

<router-outlet></router-outlet>

routing config for root (modules are loaded dynamically from passed urls):

const routes: Routes = [
    { path: 'module1', loadChildren: ...},
    { path: 'module2', loadChildren: ...}
];

export const AppRoutes = RouterModule.forRoot(routes);

example routing for one of the feature modules:

const routes: Routes = [
    { path: 'report/:reportId', component: ReportComponent}
];

export const AppRoutes = RouterModule.forChild(routes);

and its app.component which contains another router outlet:

<router-outlet></router-outlet>

Such configuration works correctly - if I go to mysite.com/module1, then module1 is loaded and angular gives control to the feature module router, so accessing mysite.com/module1/report/2 displays ReportComponent inside the feature module's router outlet.

What I'm trying to do now is to add TabsComponent to my main module to be able to compose pages from several tabs where each tab will contain one specific module. I would like to somehow use the already specified routing for root, without manually repeating it as children for the TabsComponent, and load the feature module into the TabsComponent's router outlet.

tabs.component:

<tabs-navigation></tabs-navigation>
<router-outlet></router-outlet>

tabs.configuration (loaded dynamically in runtime via http):

{
    pageId: "page1",
    tabs: [{
        tabId: "tab1",
        module: "module1",
        params: "report/2"
    }, {
        tabId: "tab2",
        module: "module2"
    }]
}

And additional line in my routing config for root:

{ path: 'tabs/:pageId', component: TabsComponent}

From now on when accessing mysite.com/tabs/page1 I would like to:

  1. Read configuration for page1 via http endpoint.
  2. Generate links to all the configured tabs for that page as tabs navigation.
  3. Display tab's content inside the TabsComponent's router outlet after clicking a link (by default the first tab should be displayed). It basically means loading a feature module to that router outlet instead of the main one.

My two questions are:

  1. How to properly configure the routing provided I'd like to have all feature modules to be loaded either as a standalone page (as they work now) or as a tab in a tabbed page.
  2. How to generate links for the tabbed scenario so that I can go to the specific tab via navigation or directly from a given url? I guess I would need to include both page and tab id and the module details to make all the parts work correctly, e.g. mysite.com/page1/tab1/module1/report/2.
1
Did you ever get to the bottom of this? - Luke

1 Answers

0
votes

@syntax_error this is how I did it. I had similar requirements for Angular. My application has a page that shows multiple user and each user has a corresponding detail view/routes. For example

  • User's list page listing user A, B, C
  • User A's info
    • User A's home info
    • User A's city home info
    • User A's country home info
  • User A's office info

User A's info has child state as well (home info, city info, country home all are child route of User A's info) This is the case with most of the top level routes. I have used ngrx and mat-tab to solve my problem. The shell component is aligned as below.

<header></header>
<my-tab></my-tab>
<router-outlet></router-outlet>
<footer></footer>

The my-tab component is responsible to listen to an observable that triggers whenever list of tab's is updated. Whenever the observable gets response the tab component renders updated tabs. A single tab is represented as

{
 id: unique tab id,
 isActive: is tab current active,
 isHomeTab: denotes if a tab is home tab or user tab
 currentViewURL: current route url for this tab
 timestamp: time in ms when this tab was created //used to decide the order in which tabs are created
}

The flow is as follows,

  1. Application bootstraps and ngrx loads a default tab. For default tab current view url is blank
  2. Once a new tab is created, a side effect will launch the tab by routing to the state stored in the tab. For default tab its empty and empty url has been mapped to home state and hence in the router outlet home route gets activated.
  3. Now when end user selects a new user from user list, a new tab is created and added to state. The route for this points to default route of info, also isActive of this tab set to true while other tabs isActive is set to false. After tab is added to state the side effect mentioned in step 2 activates the tab by reading the current view url and navigating to it.
  4. When the tab is created, the tab observable gets updated which triggers the listener in tab component, which then re-renders the tab as per the new list.
  5. TO maintain router state for tab, listen to route navigation complete event and then find out the current active tab and update its current view url.
  6. Whenever already existing tab is clicked, read the tab url and navigate to that tab.
  7. Whenever a tab close is clicked, remove the tab from state. This will trigger a effect which will then find tab which was created before this tab using the timestamp property. Once tab is found it will use current view url property to navigate to the tab.

Hope this helps you or others out in deciding how to move ahead with the tab implementation.