7

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>
Davtho1983
  • 3,827
  • 8
  • 54
  • 105

2 Answers2

23

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.
Andrei Matracaru
  • 3,511
  • 1
  • 25
  • 29
  • Ok I tried using redirects but it's still not redirecting properly! This doesn't seem to do anything: { path: '404', redirectTo: 'home', pathMatch: 'full' }, { path: '**', redirectTo: 'home', pathMatch: 'full' }, – Davtho1983 Aug 21 '17 at 16:53
  • 5
    You didn't really understand me - you need to configure your web server (as in IIS, nginx, apache, whatever you use) to return the index.html when it receives a request that would result in a 404 HTTP response to be sent back. You don't need to do anything to your Angular routing configuration. – Andrei Matracaru Aug 21 '17 at 16:55
  • ok I consulted the 1&1 guide and created a .htaccess file but nothing works - seems a lot of people on SO have the same problem! Any suggestions? – Davtho1983 Aug 21 '17 at 18:07
  • Andrei Matracaru answered the question in a way that prompts a new question. No the problem isn't solved - but I didn't realise the problem was the .htaccess file and not the Angular routing – Davtho1983 Aug 21 '17 at 18:31
  • 1
    With my answer I aimed primarily to make you understand what your problem actually is, and gave you 2 possible ways to pursue a fix. As you decided to go for the web server configuration, it now depends on what web server you use for a more detailed explanation on how to configure it. So - what web server do you use? – Andrei Matracaru Aug 21 '17 at 18:41
  • @Andrei Matracaru : I tried with Hashing and its working well. But is there any example to do it without hashing? – Sangwin Gawande Oct 17 '17 at 04:52
  • @Andrei Matracaru: I'm using IIS. What would be the settings in this case? – Alex Jan 06 '18 at 18:26
  • 3
    @Alex, check out my other answer to a similar question [here](https://stackoverflow.com/a/45844151/6139866) – Andrei Matracaru Jan 08 '18 at 08:43
  • @AndreiMatracaru I'm using JBoss, what would be the settings in this case? – MightGod Mar 01 '20 at 12:48
1
RouterModule.forRoot(routes, {useHash: true})

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

lakewood
  • 607
  • 1
  • 5
  • 21