2
votes

I've been searching for a solution to this weird behavior with no luck.

  • There are no errors in the angular 4 console or the javascript console
    • Using Angular CLI (Angular 4)

I'm getting extremely weird behavior with the angular router. I'm attempting to load a child route, which has it's own module, from the parent AppModule. The route is at /products

At the /products module (products.module.ts): I have 3 routes:

  1. /products/overview
  2. /products/intro [redirected to from /products]
  3. /products/product/:product

The products module is supposed to have it's own navbar UNDER the global AppModule's navbar, which is above the router-outlet in the products.component.html component template.

The main problem: When I load the page (not using an angular routerLink) to /products:

  • both navbars are visible
  • the page that is LOADED (ex /intro) has the navbar visible, but the child component (IntroComponent) is NOT visible
  • HOWEVER, when I navigate to any other subroute within the /products module routes, (ex /overview or /product/:product): it renders just fine. So whatever route is loaded on a full page refresh is not visible.

The weirdest part: When I LOAD the page (refresh) on a route that isn't /products (ex /home) and then use the main navbar to navigate to /products:

  • The navbar is not visible ?????
  • The sub component (ex /intro component) IS visible

This has been causing me a great deal of hell for the past few days.

Now, some code:

app.module.ts [main AppModule]

    @NgModule({
    declarations: [
        AppComponent,
        HomeComponent,
        LoginComponent,
        SignupComponent,
        NotFoundComponent,
        LogoutComponent,
        ErrorComponent,
        AboutComponent,
        ApiComponent,
        StatusComponent,
        SupportComponent,
        CustomersComponent,
        JobsComponent,
        TosComponent
    ],
    imports: [
        BrowserModule,
        HttpModule,
        FormsModule,
        SharedModule,
        //ProductsModule,
        AppRoutingModule
    ],
    providers: [
        UserService,
        AuthGuard,
        ErrorService,
        NavbarService,
        MailerService,
        IOService,
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

shared.module.ts -- This has my ViewAppComponent (which is just a router outlet with the global navbar) and other global components (like the navbar and footer that should be shown on all routes)

@NgModule({
  imports: [
    CommonModule,
      RouterModule
  ],
  declarations: [ViewAppComponent, FooterComponent, NavbarComponent],
  exports: [CommonModule, ViewAppComponent, FooterComponent, NavbarComponent]
})
export class SharedModule { }

app-routing.module.ts

const routes: Routes = [
    {
        path: '',
        component: ViewAppComponent,
        children: [
            {path: 'products', loadChildren: () => ProductsModule}, // where im trying to load my products module from the main module
            {path: 'home', component: HomeComponent},
            {path: 'about', component: AboutComponent},
            {path: 'api', component: ApiComponent},
            {path: 'status', component: StatusComponent},
            {path: 'support', component: SupportComponent},
            {path: 'customers', component: CustomersComponent},
            {path: 'jobs', component: JobsComponent},
            {path: 'tos', component: TosComponent},
            {path: 'signup', component: SignupComponent, canActivate: [AuthGuard]},
            {path: 'logout', component: LogoutComponent, canActivate: [AuthGuard]},
            {path: 'login', component: LoginComponent},
            {path: 'error', component: ErrorComponent, canActivate: [ErrorService]},
            {path: "404", component: NotFoundComponent},
            {path: '', redirectTo: '/home', pathMatch: 'full'},
        ]
    },
    {path: "admin", loadChildren: "./admin/admin.module#AdminModule"},
    {path: '**', redirectTo: '/404'}
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {}

products.module.ts [main product module where im trying to load subroutes]

@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        SharedModule,
        RouterModule,
        ProductsRoutingModule
    ],
    declarations: [ProductsComponent, OverviewComponent, IntroComponent, NavigationComponent, ProductComponent],
    exports: [
        ProductsComponent, OverviewComponent, IntroComponent, NavigationComponent, ProductComponent, RouterModule, ProductsRoutingModule
    ]
})
export class ProductsModule {
}

products-routing.module.ts [router for products submodule]

export const productRoutes: Routes = [
    {
        path: '',
        component: ProductsComponent,
        children: [
            {
                path: 'overview',
                component: OverviewComponent,
            },
            {
                path: 'intro',
                component: IntroComponent
            },
            {
                path: 'product/:product',
                component: ProductComponent
            },
            {
                path: '',
                redirectTo: '/products/intro',
                pathMatch: 'full'
            },
            {
                path: '**',
                redirectTo: '/404',
                pathMatch: 'full'
            }
        ]
    }
];

@NgModule({
    imports: [RouterModule.forChild(productRoutes)],
    exports: [RouterModule]
})
export class ProductsRoutingModule {}

products.component.html [template for products w/ outlet for subcomponents in products]

<h1>Products</h1>
<br/>
<app-product-navigation></app-product-navigation>
<br/>
<router-outlet></router-outlet>

navigation.component.html [app-product-navigation component template]

<nav class="navbar navbar-info" role="navigation">
  <div class="container-fluid">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#products-navbar-collapse">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
    </div>

    <div class="collapse navbar-collapse" id="products-navbar-collapse">
      <ul class="nav navbar-nav">
        <li routerLinkActive="active"><a routerLink="/products/intro">Intro</a></li>
        <li routerLinkActive="active"><a routerLink="/products/overview">Overview</a></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown">Products <b class="caret"></b></a>
          <ul class="dropdown-menu">
            <li routerLinkActive="active"><a routerLink="/products/product/reflex">Reflex</a></li>
            <li routerLinkActive="active"><a routerLink="/products/product/override">Project Override</a></li>
          </ul>
        </li>
      </ul>
    </div>
  </div>
</nav>
<br/>

I'll also provide my package.json dependencies for versions, I'm using ng serve for testing.

  "dependencies": {
    "@agm/core": "1.0.0-beta.0",
    "@angular/animations": "4.3.1",
    "@angular/cdk": "2.0.0-beta.10",
    "@angular/common": "4.3.1",
    "@angular/compiler": "4.3.1",
    "@angular/core": "4.3.1",
    "@angular/forms": "4.3.1",
    "@angular/http": "4.3.1",
    "@angular/material": "2.0.0-beta.10",
    "@angular/platform-browser": "4.3.1",
    "@angular/platform-browser-dynamic": "4.3.1",
    "@angular/platform-server": "4.3.1",
    "@angular/router": "4.3.1",
    "angular2-material-datepicker": "0.5.0",
    "animate.css": "3.5.2",
    "arrive": "2.3.1",
    "bootstrap": "3.3.5",
    "bootstrap-material-design": "0.5.10",
    "bootstrap-notify": "3.1.3",
    "bootstrap-select": "1.12.2",
    "bootstrap-tagsinput": "0.7.1",
    "chartist": "0.9.4",
    "chartist-plugin-zoom": "0.4.0",
    "core-js": "2.4.1",
    "datatables": "1.10.12",
    "datatables.net-bs": "1.10.12",
    "datatables.net-responsive": "2.1.1",
    "eonasdan-bootstrap-datetimepicker": "4.17.47",
    "fullcalendar": "3.4.0",
    "googleapis": "19.0.0",
    "jasny-bootstrap": "3.1.3",
    "jquery": "1.12.4",
    "moment": "2.18.1",
    "moment-timezone": "0.4.0",
    "ng2-nouislider": "1.6.1",
    "ng2-select": "1.2.0",
    "ngx-chips": "1.4.6",
    "nouislider": "9.2.0",
    "rxjs": "^5.4.2",
    "twitter-bootstrap-wizard": "1.2.0",
    "typescript": "2.3.4",
    "validate": "3.0.1",
    "web-animations-js": "2.2.2",
    "zone.js": "0.8.4"
  },
  "devDependencies": {
    "@angular/cli": "1.4.2",
    "@angular/compiler-cli": "4.3.1",
    "@types/bootstrap": "3.3.32",
    "@types/chartist": "0.9.34",
    "@types/jasmine": "2.5.38",
    "@types/jquery": "1.10.31",
    "@types/node": "6.0.73",
    "codelyzer": "2.0.0",
    "jasmine-core": "2.5.2",
    "jasmine-spec-reporter": "3.2.0",
    "karma": "1.4.1",
    "karma-chrome-launcher": "2.0.0",
    "karma-cli": "1.0.1",
    "karma-coverage-istanbul-reporter": "0.2.0",
    "karma-jasmine": "1.1.0",
    "karma-jasmine-html-reporter": "0.2.2",
    "node-sass": "^4.5.3",
    "protractor": "5.1.0",
    "ts-node": "2.0.0",
    "tslint": "4.5.0",
    "typescript": "2.3.4"
  }
}

This is my first 'looking for help' post on stackoverflow, so let me know if there's anything I can improve.

I've tried to research various forms and tutorials and referenced the angular docs for routing

Edit: Adding some pictures for more clarification of the situation

Home page: http://prntscr.com/gskex5

Products [/products/intro] (navigated to using routerLink): http://prntscr.com/gskf2h (the products navbar is missing) On refresh, the navbar becomes visible, but the intro component is gone: http://prntscr.com/gskfaq

However, when I click on another component in the products nav, it works properly: http://prntscr.com/gskfhf

if I refresh, the component disappears, and the nav is left: http://prntscr.com/gskfna and now, for whatever reason, if i go back to intro, it works properly: http://prntscr.com/gskfra

So basically, whatever route is loaded when the page is initially loaded, it has a problem [component is not visible]. If the initially loaded route is not /products; the product navbar is not visible.

1
Didn't run through all this, but rendering only after refresh is normally an async problem. Thought that might help. Observables and http calls and what ever may depend on them is the likely cause. - Z. Bagley
@Z.Bagley there are no observables or async/http calls in any components in the products module. in fact, the .ts files for the components are all the default stubs generated by angular cli. I updated the post with pictures. - Jonah Seguin
Do you have any console errors? - Everest
@Everest none at all, as I stated at the top of my post. - Jonah Seguin
One thing you can do is add a .catch in your router.navigate call to see if the route is actually changing. It may also have something to do with your AuthGuard - Everest

1 Answers

1
votes

For this rather weird situation, I was able to fix this weird behavior by generating a fresh Angular 4 app with angular-cli: ng new

I'm guessing it had something to do with the version of Angular I was running.

For my new project, the @angular versions are set to **^4.0.0*.

In the old project, the versions were set to 4.3.1

I would guess it has something to do with one of the changes in the bugfixes for v4.3.1:

  • router: canDeactivate guards should run from bottom to top (1ac78bf), closes #15657
  • router: should navigate to the same url when config changes (4340bea), closes #15535
  • router: should run resolvers for the same route concurrently (ec89f37), closes #14279
  • router: terminal route in custom matcher (5d275e9)

TL;DR

use the latest stable version of ng4