1

I am attempting to follow the feature-module approach in Angular.

I have an application with an Admin module. Within this module will be sub modules (i.e. User-Management, Project-Management).

Below is the structure I came up with:

app.component.css
app.component.html
app.component.ts
app.module.ts
app-routing.module.ts

   admin/
      admin.component.css
      admin.component.html
      admin.component.ts
      admin.module.ts
      admin-routing.module.ts

      user-management/
         user-management.component.css
         user-management.component.html
         user-management.component.ts
         user-management.module.ts
         user-management-routing.module.ts

         user-list/
            user-list.component.css
            user-list.component.html
            user-list.component.ts

         user-detail/
            user-detail.component.css
            user-detail.component.html
            user-detail.component.ts

      project-management/
         project-management.component.css
         project-management.component.html
         project-management.component.ts
         project-management.module.ts
         project-management-routing.module.ts

         project-list/
            project-list.component.css
            project-list.component.html
            project-list.component.ts

         project-detail/
            project-detail.component.css
            project-detail.component.html
            project-detail.component.ts

Correct me if I am wrong, but I believe the above structure is as textbook as it gets. I am really struggling to make routing work with this approach.

Please see my routes below:

const appRoutes: Routes = [
   { path: 'admin', loadChildren: './admin/admin.module#AdminModule', canLoad: 
  [AuthGuard] },
  { path: '',   redirectTo: '/login', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

    const adminRoutes: Routes = [
      {
        path: '',
        component: AdminComponent,
        canActivate: [AuthGuard],
        children: [
          {
            path: '',
            canActivateChild: [AuthGuard],
            children: [
              { path: 'users', component: UserManagementComponent },
              { path: 'projects', component: ProjectManagementComponent }
            ]
          }
        ]
      }
    ];

    const manageUsers: Routes = [
      {path: 'users', component: UserListComponent},
      {path: 'users/edit/:id', component: UserDetailComponent}
    ];

    const manageProjects: Routes = [
      {path: 'projects', component: ProjectListComponent},
      {path: 'projects/edit/:id', component: ProjectDetailComponent}
    ];

The way I would like things to navigate is the following:

/admin (displays nothing besides the admin.component.html layout - someday will be display a dashboard component)
/admin/users (displays a listing of users)
/admin/users/edit/1 (displays detail of a user)

I think my main struggle is the fact that I have a user-management component and what really is supposed to be displayed on it by default is the user-list component. There is a router-outlet in the admin.component.html and there is a router-outlet in the user-management.component.html. Is a double route-outlet needed for something like this or should I just be displaying directly on the user-management-component.html page instead of a router-outlet?

Blake Rivell
  • 13,105
  • 31
  • 115
  • 231

1 Answers1

4

Here is a routing structure that would directly correspond to your proposed module component hierarchy:

const appRoutes: Routes = [
  {
    path: 'admin',
    component: AdminComponent, // has <router-outlet>
    children: [
      {
        path: 'users',
        component: UserManagementComponent, // has <router-outlet>
        children: [
          {
            path: '',
            component: UserListComponent
          },
          {
            path: 'edit/:id',
            component: UserDetailComponent
          }
        ]
      },
      {
        path: 'projects',
        component: ProjectManagementComponent, // has <router-outlet>
        children: [
          {
            path: '',
            component: ProjectListComponent
          },
          {
            path: 'edit/:id',
            component: ProjectDetailComponent
          }
        ]
      }
    ]
  },
  { path: '', redirectTo: '/admin' },
  { path: '**', component: PageNotFoundComponent }
];

This would give you a main "admin" page which would display nothing besides the HTML from the template (if it contained anything aside from the <router-outlet>). When navigating to /admin/users, it would display the UserListComponent inside of the UserManaementComponent (considering that UserManagementComponent has a <router-outlet>). If you click on a link to a /admin/users/edit/:id (where :id is some ID), then the UserListComponent would get swapped with the UserDetailComponent inside of the UserManagementComponent.

Likewise for the "project" side.

Edit

If you want to break out into feature modules, you could do so like this:

app-routing/module.ts

// From app-routing.module.ts
const appRoutes: Routes = [
  {
    path: 'admin',
    loadChildren: './admin/admin.module#AdminModule',
    canLoad: [AuthGuard]
  },
  { path: '',   redirectTo: '/login', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

/admin/admin-routing.module.ts

// From /admin/admin-routing.module.ts
const ADMIN_ROUTES: Routes = [
  {
    path: '',
    component: AdminComponent,
    canActivate: [AuthGuard],
    children: [
      {
        path: '',
        canActivateChild: [AuthGuard],
        children: [
          { path: 'users', loadChildren: './admin/user-management/user-management.module#UserManagementModule' },
          { path: 'projects', loadChildren: './admin/project-management/project-management.module#ProjectManagementModule' }
        ]
      }
    ]
  }
];

/admin/user-management/user-management-routing.module.ts

// From /admin/user-management/user-management-routing.module.ts
const USER_MANAGEMENT_ROUTES: Routes = [
  {path: '', component: UserListComponent},
  {path: 'edit/:id', component: UserDetailComponent}
];

/admin/project-management/project-management-routing.module.ts

// From /admin/project-management/project-management-routing.module.ts
const PROJECT_MANAGEMENT_ROUTES: Routes = [
  {path: '', component: ProjectListComponent},
  {path: 'edit/:id', component: ProjectDetailComponent}
];
Community
  • 1
  • 1
Tim Klein
  • 2,538
  • 15
  • 19
  • Thanks! This is exactly what I was looking for. Yes, UserManagementComponent just has a router-outlet in it, nothing else. Going to see if it works now. – Blake Rivell Aug 13 '18 at 18:43
  • Also, if your `appRoutes` structure becomes very large, you can break it out, like you have in your question, however, it should be used with care, so that you still achieve the appropriate nesting. It is much easier to reason about routes by looking at the tree of children all together. – Tim Klein Aug 13 '18 at 18:45
  • Yes, as you can see Admin has routes, and additional each sub module has routes. The smallest thing can cause a problem especially if they are imported in an incorrect order. I think I am going to do what you said and put everything in app-routing and slowly break it out. – Blake Rivell Aug 13 '18 at 18:48
  • If you take a look at my Admin Routes in my post you will see that I was using a ComponentLess route. I guess this is not what I should have been doing and using the UserManagementComponent here like you are. Make sense? – Blake Rivell Aug 13 '18 at 18:58
  • That would be valid if you are actually going to separate the functionality out into feature modules (as you mentioned), but you would still need to ensure that your route nesting is valid so that every child has a `` to occupy when that route is loaded (a little harder to tell if you are loading separate modules for routes). – Tim Klein Aug 13 '18 at 19:11
  • Thank you so much for breaking it out. In the admin.module.ts what would the order be of the module imports: AdminRoutingModule, ManageTenantsModule. I am still experiencing trouble where when navigating to /admin/users the UserManagemant.component.html is displayed but nothing is displaying in the router-outlet. Shouldnt: {path: '', component: TenantListComponent}, be making the list show? Also I see you are lazy loading the sub modules, it doesn't make a difference if I just specify the components for these does it? – Blake Rivell Aug 13 '18 at 19:40
  • Sorry, this is driving me absolutely crazy. When navigating to /admin/users it doesn't want to display the UserListComponent inside the UserManagement router-outlet when we are clearly telling it to. It is displaying an empty UserManagementComponent.html... – Blake Rivell Aug 13 '18 at 19:53
  • Just realized something. If I am not actually clicking any routerLinks inside of the UserManagement component and all of the links are in the navbar of the AdminComponent is this why it is almost as if the router-outlet inside of UserManagement.component.html does not exist? – Blake Rivell Aug 13 '18 at 20:06
  • I created a stackblitz if you don't mind taking a look. https://stackblitz.com/edit/angular-ki61xh – Blake Rivell Aug 13 '18 at 21:41
  • I think you are confusing how **Modules** handle routes and how **Components** handle routes. I specified the lazy loading of the **Modules** because you had your features broken down into separate **Modules**. If you are simply trying to use a **Component** hierarchy, then you have to specify the child routes for every **Component**. In your `admin-routing.module.ts`, the last route available is `/admin/users` and then no other **Component** will be loaded no matter the path or structure of your **Module** because you haven't specified that the route for `/admin/users` has any children. – Tim Klein Aug 14 '18 at 11:18
  • See [my update to your StackBlitz](https://angular-xu18uk.stackblitz.io/admin/users). – Tim Klein Aug 14 '18 at 11:25
  • Are you saying manage-users-routing.module is not needed? Just handle everything from the admin-routing.module? I try to follow the pattern that everything is a feature-module. For example, Admin is a feature module that I will lazy-load. Then within admin there is Users and Projects which are also feature modules and should contain their own .module.ts and routing.module.ts. At the same time maybe these shouldn't be feature modules and should just be components within the Admin feature module. Do you see where I am getting at? I don't know when to stop creating modules. – Blake Rivell Aug 14 '18 at 15:21
  • In most examples I see they never create feature modules within feature modules ever. If you know of one please send it to me because I feel like I am the only one trying to do it because it seems to be what the angular docs says. However, every example I see it seems like feature modules are in the App root directory only, then within each feature module is just a bunch of components. Is this feature module within feature module thing that I am trying to do even a thing? – Blake Rivell Aug 14 '18 at 15:24
  • You can do what you are describing, based on the routing structure I laid out in the second part of my answer. When you think about it, loading feature-modules from within the main AppModule is the same concept as nested feature-modules. You just have to make sure your nested feature modules are being loaded correctly (with or without lazy-loading) and that their respective routing modules are valid. – Tim Klein Aug 14 '18 at 16:40
  • See [this SO post which should basically answer your question](https://stackoverflow.com/questions/39492458/angular2-rc6-nested-modules-with-routing). – Tim Klein Aug 14 '18 at 16:41
  • thank you. I don't feel as bad now after finding this post. https://github.com/angular/angular/issues/10647 and this post: https://github.com/angular/angular/issues/10958 – Blake Rivell Aug 14 '18 at 20:40