Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 Routing not working on live web-app

My Angular 4 web-app routing works fine in my dev environment, and the menu redirects work fine on the live version.

However, the dev version redirects to different pages by typing in the address field of the browser, but the live version doesn't. Is this an Angular problem? Or do I need to manage my redirects with my ISP?

My app.router.ts code is as follows:

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HomeComponent } from './home/home.component';
import { ArtComponent } from './art/art.component';
import { MusicComponent } from './music/music.component';
import { EventsComponent } from './events/events.component';

export const router: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full'},
{ path: 'home', component: HomeComponent },
{ path: 'art', component: ArtComponent },
{ path: 'music', component: MusicComponent },
{ path: 'events', component: EventsComponent }
];

export const routes: ModuleWithProviders = RouterModule.forRoot(router);

And in app.module.ts I have:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, } from '@angular/core';
import { RouterModule } from '@angular/router';
import { router } from './app.router';

import 'hammerjs';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms';
import { MdInputModule, MdButtonModule, MdToolbarModule, MdMenuModule, MdGridListModule, MaterialModule, MdDatepickerModule, MdNativeDateModule } from '@angular/material';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { MfToolbarComponent } from './mf-toolbar/mf-toolbar.component';
import { MfMenuComponent } from './mf-menu/mf-menu.component';
import { SplashComponent } from './splash/splash.component';
import { ArtComponent } from './art/art.component';
import { MusicComponent } from './music/music.component';
import { EventsComponent } from './events/events.component';
import { HomeComponent } from './home/home.component';
import { ImageSliderComponent } from './image-slider/image-slider.component';

// import { HTTPTestService } from './date-data.service';
import { AuthenticationService } from './authentication-service.service';

import { DataService } from './data.service';
import { SvgViewerComponent } from './svg-viewer/svg-viewer.component';
import { CalendarComponent } from './calendar/calendar.component';

@NgModule({
  declarations: [
    AppComponent,
    MfToolbarComponent,
    MfMenuComponent,
    SplashComponent,
    ArtComponent,
    MusicComponent,
    EventsComponent,
    HomeComponent,
    ImageSliderComponent,
    SvgViewerComponent,
    CalendarComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    MdInputModule,
    MdButtonModule,
    MdToolbarModule,
    MdMenuModule,
    MdDatepickerModule, 
    MdNativeDateModule,
    HttpModule,
    RouterModule.forRoot( router ),
  ],
  providers: [
    // [HTTPTestService],
    [AuthenticationService],
    [DataService],
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

I don't understand what forRoot is doing or if it's proper to use that in Angular 4?

My app.component.html is as follows and uses a hidden router outlet:

<body>
<app-mf-toolbar></app-mf-toolbar>
<router-outlet class="hidden-router"></router-outlet>
</body>

Is it this hidden router behaviour that my live web-app is not reproducing and how do I change this?

I also have a menu in menu.component.html that uses router links and this works fine:

<div class="container">
    <md-menu #appMenu="mdMenu">
        <a routerLink="home">
            <button md-menu-item>
                <md-icon class="material-icons"> home </md-icon>
                <span> Home </span>
            </button>
        </a>
        <a routerLink="art">
            <button md-menu-item>
                <md-icon class="material-icons"> format_paint </md-icon>
                <span> Art </span>
            </button>
        </a>
        <a routerLink="music">
            <button md-menu-item>
                <md-icon class="material-icons"> music_note </md-icon>
                <span> Music </span>
            </button>
        </a>
        <a routerLink="events">
            <button md-menu-item>
                <md-icon class="material-icons"> event_note </md-icon>
                <span> Events </span>
            </button>
        </a>
    </md-menu>

    <button md-icon-button [mdMenuTriggerFor]="appMenu" color="secondary">
       <i class="material-icons">menu</i>
    </button>
</div>
like image 444
Davtho1983 Avatar asked Aug 21 '17 15:08

Davtho1983


2 Answers

The issue you have here has to do with the nature of Single Page Application frameworks, such as Angular.

On the deployed version of your app, the web server hosting it knows only how to serve the one html file it sees (index.html), which corresponds to your root path. The second you try to access directly http://url-to-your-app/art for example, the server will throw a 404 not found, as it does not recognize that as the path of a resource it can serve.

When navigating through your application from within the application itself, it's Angular's routing service that manages the navigation on the client side; the hosting web server does not actually receive requests to serve other web pages than your index.html. Also, this does not happen on dev because you dev web server knows how to manage this.

You have two options:

  1. Configure your production web server to always respond with the index.html whenever it detects a 404 - that way, Angular will always load and its routing service will handle the navigation on the client side.
  2. Change your app's locationStrategy to the Hash location strategy as described here. However, that would change your app's URLs, and it's not that desirable in my opinion.
like image 95
Andrei Matracaru Avatar answered Oct 30 '22 21:10

Andrei Matracaru


RouterModule.forRoot(routes, {useHash: true})

See the hashLocationStrategies usage here: https://codecraft.tv/courses/angular/routing/routing-strategies/#_hashlocationstrategy

like image 33
lakewood Avatar answered Oct 30 '22 20:10

lakewood