Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2: routing within a module (secondary nested router-outlet)

I've been playing around with the Angular 2 webpack starter found here, which has a ton of useful examples. I've run into an issue with routing, though. I've removed a few of the modules & added one of my own, which contains children (I'm still not sure if using child modules or child components would be best practice- I'm currently trying this with components).

My objective is to be able to switch between top-level modules as normal (Home and About, in this case), but after loading my Home module, swap between multiple child components (or modules, if that's better supported) within the Home module's template.

I'm trying to use another <router-outlet> within Home- I've tried naming it & using the outlet paramter in the Home module routing definition, but clicking links to load within Home doesn't currently work. Code below:

This is my current app structure (with most of the extra webpack stuff left out):

app
  |-app.component.ts (template contains router-outlet)
  |-app.module.ts
  |-app.routes.ts
  |-about
    |-about.component.ts
    |-about.component.html
  |-home
    |-home.module.ts
    |-home.component.ts
    |-home.component.html (contains router outlet for children)
    |-home.routes.ts
    |-signup
      |-signup.component.ts
      |-signup.component.html
    |-login
      |-login.component.ts
      |-login.component.html

This is my app.component, which is almost the same as it starts in the starter repo- I just added a link to my own module & removed the others:

app.component.ts

import {
 Component,
  OnInit,
  ViewEncapsulation
} from '@angular/core';

@Component({
  selector: 'app',
  encapsulation: ViewEncapsulation.None,
  styleUrls: [
    './app.component.css'
  ],
  template: `
    <nav>
      <a [routerLink]=" ['./home'] " routerLinkActive="active">
        Home
      </a>      
      <a [routerLink]=" ['./about'] " routerLinkActive="active">
        About
      </a>
    </nav>
    <main>
      <router-outlet></router-outlet>
    </main>
  `
})
export class AppComponent implements OnInit {

  constructor(
    public appState: AppState
  ) {}

  public ngOnInit() {
    console.log('Initial App State', this.appState.state);
  }

}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import {
  NgModule,
  ApplicationRef
} from '@angular/core';
import {
  removeNgStyles,
  createNewHosts,
  createInputTransfer
} from '@angularclass/hmr';
import {
  RouterModule,
  PreloadAllModules
} from '@angular/router';

/*
 * Platform and Environment providers/directives/pipes
 */
import { ENV_PROVIDERS } from './environment';
import { ROUTES } from './app.routes';
// App is our top level component
import { AppComponent } from './app.component';
import { APP_RESOLVER_PROVIDERS } from './app.resolver';
import { AppState, InternalStateType } from './app.service';
import { AboutComponent } from './about';
import { HomeModule } from './home';
import { XLargeDirective } from './home/x-large';

// app services needed globally
import { AuthenticationService, UserService, AlertService } from './shared';

import '../styles/styles.scss';
import '../styles/headings.css';

// Application wide providers
const APP_PROVIDERS = [
  ...APP_RESOLVER_PROVIDERS,
  AppState
];

type StoreType = {
  state: InternalStateType,
  restoreInputValues: () => void,
  disposeOldHosts: () => void
};

/**
 * `AppModule` is the main entry point into Angular2's bootstraping process
 */
@NgModule({
  bootstrap: [ AppComponent ],
  declarations: [
    AppComponent,
    AboutComponent,
    NoContentComponent,
    XLargeDirective
  ],
  imports: [ // import Angular's modules
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(ROUTES, { useHash: true, preloadingStrategy: PreloadAllModules })
  ],
  providers: [ // expose our Services and Providers into Angular's dependency injection
    ENV_PROVIDERS,
    APP_PROVIDERS,
    UserService,
    AuthenticationService,
    AlertService
  ]
})
export class AppModule {

  constructor(
    public appRef: ApplicationRef,
    public appState: AppState
  ) {}

}

app.routes.ts

import { Routes } from '@angular/router';
import { HomeModule } from './home';
import { AboutComponent } from './about';

import { DataResolver } from './app.resolver';

export const ROUTES: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'about', component: AboutComponent },
  { path: 'home', loadChildren: './home#HomeModule' },
  { path: '**', redirectTo: '/home', pathMatch: 'full' }
];

Navigation to my Home module works fine- I think the issue is the routing within it. This is the Home module, component, & routing.

home.module.ts

import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { routes } from './home.routes';
import { HomeComponent } from './home.component';
import { SignupComponent } from './signup/signup.component';
import { LoginComponent } from './login/login.component';

console.log('`Home` loaded');

@NgModule({
  declarations: [
    // Components / Directives/ Pipes
    HomeComponent,
    SignupComponent,
    LoginComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    RouterModule.forChild(routes),
  ],
})
export class HomeModule {
  public static routes = routes;
}

home.component.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'home',
  styleUrls: [ './home.component.css' ],
  templateUrl: './home.component.html'
})
export class HomeComponent implements {

}

home.component.html

<h1>Home</h1>
<div>
    <h2>Hello from home module</h2>
</div>
<span>
    <a [routerLink]=" ['./signup'] ">
    Sign Up
    </a>
</span>
<span>
    <a [routerLink]=" ['./login'] ">
    Login
    </a>
</span>
<router-outlet name="aux"></router-outlet>
<div>
    <h2>Bottom info</h2>
</div>

home.routes.ts

import { HomeComponent } from './home.component';
import { SignupComponent } from './signup/signup.component';
import { LoginComponent } from './login/login.component';

export const routes = [
    {   path: '', 
        component: HomeComponent,
        children: [
            {
                path: 'signup',
                component: SignupComponent,
                outlet: 'aux'
            },
            {
                path: 'login',
                component: LoginComponent,
                outlet: 'aux'
            }
        ] }
];

The current behavior is that nothing happens when I click one of the links for Signup or Login - no errors, but the components are not loaded at the aux router-outlet, either.

I've tried configuring Signup as a module, but that hasn't worked for me yet either- I think there's some extra config that I don't understand, and figured treating the children as components would help me understand the basic required routing.

From researching this question, it seems like multiple router-outlets hasn't been well supported, especially using them with the router-link syntax, until around RC5. This recent answer to a similar question suggests that it does work now, but similar syntax isn't working for me.

Is it an issue to have the router-outlets nested? Should I be defining the Signup and Login components as modules instead? Or is there some other problem altogether with the way I've defined the routing, perhaps at the uppermost AppModule level?

EDIT

I've added app.module.ts to the question. Additionally, after looking at the base configuration of the starter project I used, it seems like using child modules can simplify things- specifically the barrel module (viewable here) has a child-barrel module, and it's loaded into the router-outlet present in the barrel.component template. If there was a child-barrel-2 module at the same level, this would be the sort of thing I'm looking for.

I also found this question, where the top answer states that

Modules are recommended

Should I be trying to mimic the structure of the starter, where the parent-child relationship is accomplished by using modules?

like image 254
dkhaupt Avatar asked Feb 15 '17 13:02

dkhaupt


People also ask

Can we use multiple router outlet in Angular 2?

Yes you can as said by @tomer above. i want to add some point to @tomer answer. firstly you need to provide name to the router-outlet where you want to load the second routing view in your view. (aux routing angular2.)

Can we have more than one router outlet in Angular?

Descriptionlink Using named outlets and secondary routes, you can target multiple outlets in the same RouterLink directive.

Can Angular 8 use multiple router outlets?

Yes! We can use multiple router-outlets in same template by configuring our routers and simply add the router-outlet name. You can see in the example.


1 Answers

Why are you using '.' in front of '/' in <a [routerLink]=" ['./signup'] "> Try removing it. .what is your base href

EDIT

Why don't you try something like this in template.

<a [routerLink]="[{outlets: {primary: 'home', aux: 'signup'}}]">signup </a>

and below code in routes config

{path: 'signup', component: signUpComponent, outlet:"aux"}

like image 167
Arun Sivan Avatar answered Sep 21 '22 21:09

Arun Sivan