Angular 2 Routing Does Not Work When Deployed to Http Server
Asked Answered
C

19

66

I am going to develop a simple Angular 2 application. I have created a project with routing, using Angular CLI and added several components to the app using 'ng generate component ' command. Then I specified routing in the app-routing.module.ts as following.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { UserComponent } from './user/user.component';
import { ErrorComponent } from './error/error.component';
import { SpecialpageComponent } from './specialpage/specialpage.component';

const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'about',
    component: AboutComponent
  },
    {
    path: 'user',
    component: UserComponent
  },
  {
    path: 'specialpage',
    component: SpecialpageComponent
  },
  {
    path: '**',
    component: ErrorComponent
  }

];

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

app.module.ts is as following.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ErrorComponent } from './error/error.component';
import { UserComponent } from './user/user.component';
import { SpecialpageComponent } from './specialpage/specialpage.component';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent,
    ErrorComponent,
    UserComponent,
    SpecialpageComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

I have not added any modifications for the other components. Then I deployed the application using 'ng serve' command and the app works fine with the links. Eg: http://localhost:4200/about

enter image description here

But when I deploy the project in http-server, the links do not work as expected. I deployed the app using 'http-server ./dist' command and the app gets deployed fine, but the links do not work. When I go to 'http://localhost:4200/about', it gives 404 error.

enter image description here

Am I doing anything wrong? Why 'ng-serve' works and 'http-server' does not work?

Any help would be appreciated. Thanks in advance.

I have uploaded my project to github.

Ceto answered 21/4, 2017 at 6:3 Comment(13)
Try imports: [RouterModule.forRoot(routes, {useHash: true})],. If it works this way, you need to enable HTML5 pushState on your production server.Submissive
@GünterZöchbauer, I tried, but no luckCeto
Also try adding pathMatch: 'full' to this route path: '',Submissive
@GünterZöchbauer thanks for your comment, but I'm unable to get get work yetCeto
Have you tried 127.0.0.1:8081/about with useHash: true or 127.0.0.1:8081/#/about (or whatever the URL looks like exactly after you navigate to the about page)?Submissive
Hi @GünterZöchbauer, Yes you are correct. When I use 'useHash:true' and then when i go to the links like '172.16.3.77:8081/#/about', they work without an issue. Is there a way to remove the # here?Ceto
Yes, do not use useHash:true :D. As I said in my initial comment. You have to configure the server to support HTML5 pushState. useHash:true was only to debug the cause of the issue.Submissive
@GünterZöchbauer Ah.. my bad. Actually I'm pretty new to all this. Anyway, according to your answer, what i feel is that I can't host my angular cli app in a normal hosting service. What is your opinion on that? I did some reading on pushstate supported servers. So ngserve supports push state and it's because my project works fine with ngserve. But http-server does not support pushstate.Ceto
If the server doesn't support it then it's probably not worth trying. Usually it's quite easy for a server to support it. It just needs to return index.html for any request that points to a non-existing resource. I have seen client-only solutions but never had a closer look and don't know about disadvantages or if this really works.Submissive
This is going to sound like a silly question, but did you actually run ng build before trying all of this?Malebranche
@R.Richards.. :D yeah I did. But couldn't get it working yet.Ceto
@GünterZöchbauer, thank you for your invaluable information regarding this matter. I will try to find a solution for this. Thanks againCeto
Did you find out the solution. i have same issue.Himyaritic
I
54

This problem is solved by implementing HashLocationStrategy which adds # to all your routes. You achieve this by adding HashLocationStrategy to AppModule providers.

providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}],

and add the corresponding import

import { HashLocationStrategy, LocationStrategy } from '@angular/common';

This will solve your problem.

And if you don't want to use the HashLocationStrategy, you can use the PathLocationStrategy, and so your Angular app will not show Hash in the URL.

Issuance answered 19/6, 2017 at 13:11 Comment(9)
I have same issue and I also have this in my app.modules.ts. I am not getting any 404 error. After app component init method nothing is loading as if my routes are not working. ng serve works fine but my localhost not..Himyaritic
Thank you! helped me. But is it ok to always show a hashtag in the deployed websites urls? HmmDiddle
@ninjaxlite I Updated my answer if you don't want to show Hashtag in URLsIssuance
I feel like this shouldn't have to be a thing. Fix this Google. Change the construct of the entire internet to appease me.Myriapod
This. I've tried all kinds of things to get it to run both on my nginx and IIS - to no avail. Until I came across this one. Wouldn't we all want angular to use the HashLocationStrategy by default? Or, even farther, it shouldn't even be an option NOT to use it?Auk
Thank You...So muchGyimah
just a small thing edit PahtLocationStrategy to PathLocationStrategy on second last line :)Doodlebug
For me, HashLocationStrategy worked absolutely fine but not PathLocationStrategy.Tourneur
Thank you, This solved my issue also.Pb
M
24

This is because http-server does not support fallback like lite-server or web pack-dev server. This is why it shows 404 not found. There are two solution to resolve this issue:

  1. you can use HashLocationStrategy like mentioned above.
  2. If you are deploying it to Apache or IIS server then you can simply add configurations as mentioned here!

Note: For development you can use lite-server.

Hope this will help you.

Mapp answered 4/10, 2017 at 13:17 Comment(0)
O
14

I've resolved this problem with adding this in AppModule Providers:

providers: [{provide: LocationStrategy, useClass: PathLocationStrategy}]

and importing

import { PathLocationStrategy, LocationStrategy } from '@angular/common';

Then I created .htaccess:

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html

Everything works great without # in URLs :)

Othaothe answered 25/7, 2018 at 6:12 Comment(2)
Where i have to add .htaccess file?Spot
@jaykhatri In main directory (there where Angular app is) on your web server. It's probably public_html.Othaothe
N
10

It will be solved in this way:

Case-1: For version less than Angular 5.1

1) Use HashLocationStrategy like mentioned below: In AppModule do the following.

1.1) import { HashLocationStrategy, LocationStrategy } from '@angular/common';

1.2) providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]

Case-2: For version equal or more than Angular 5.1

2.1) Angular has introduced this property onSameUrlNavigation for overcoming refresh issue on the server.

2.2) Add onSameUrlNavigation to the RouterModule.forRoot in imports array .

a) In Application main routing module, i,e app.routing.module.ts / app.routing.ts add the property

See below:

@ngModule({

    imports: 
    [
       RouterModule.forRoot(routes, {onSameUrlNavigation: ‘reload’})
    ],

    exports: [RouterModule],
 });

Hope it help somebody.

Nevermore answered 14/3, 2018 at 11:54 Comment(0)
F
6

It will happen because it goes to find a page about which is not there inside at all hence 404. Possible options:

  1. If you want to go with http-server then use a proxy which will redirect everything to http://localhost:your-port.

    Option: -P or --proxy Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com

  2. Don't use express at all. Create your own http-server using express & Redirect everything to index.html

    Assuming public is your folder where u keep all transpiled things.

    var express = require('express');
    var app = express();
    
    var path = __dirname + '/public';
    var port = 8080;
    
    app.use(express.static(path));
    app.get('*', function(req, res) {
        res.sendFile(path + '/index.html');
    });
    app.listen(port);
    
Follmer answered 21/4, 2017 at 6:10 Comment(2)
I'm sorry I did not get you quite well. I'm really a newbie to Angular. I'm not doing anything complex here. An regarding your first option, I tried to host my server and I accessed the URL without a port. Only the root URL works. Cannot navigate into other links.Ceto
http-server --port 4200 -P http://someurl.com raise an infinite loop in my caseArtichoke
B
5

If you, like me, want to make no code changes at all, simply use this instead:

https://www.npmjs.com/package/angular-http-server

Biophysics answered 10/12, 2018 at 14:32 Comment(0)
J
3

For Apache server:

in the Production servers

Add or create ".htaccess" file into the project root, add a rewrite rule to the .htaccess file as shown

RewriteEngine On
  # If an existing asset or directory is requested go to it as it is
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
  RewriteRule ^ - [L]
  # If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
Jaunty answered 13/2, 2018 at 4:39 Comment(0)
A
2

As per the documentation at https://angular.io/guide/deployment (Apache, you could also look for nginx) You need to point the server to index.html using the .htaccess file as

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
Anetteaneurin answered 22/10, 2018 at 2:15 Comment(0)
S
2

I also faced this issue and found a solution that does not require HashLocationStrategy solution.

Issue is, a server like tomcat looks for a actual folder (for eg : /about ) which does not exist in angular app, as its a single page application. So every request needs to be redirected to index.html.

Easiest way to fix this issue is to add the following java class and override addResourceHandlers() method like below:

@Configuration
class WebMVCCustomConfiguration implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:static").resourceChain(true)
            .addResolver(new PathResourceResolver() {
                @Override
                protected Resource getResource(String resourcePath, Resource location) {
                    String path = "static/";
                    path += (resourcePath.contains(".") && !resourcePath.equals("index.html")) ? resourcePath : "index.html";

                    Resource resource = new ClassPathResource(path);
                    return resource.exists() ? resource : null;
                }
            });
    }
  }

This will fix all the issues and this solution does not exist anywhere.

Seda answered 31/12, 2019 at 6:33 Comment(1)
Good to know that there is solution like this, but I think is better to you use mechanisms provided by angular like that one which is mentioned above -> HashLocationStrategy. Cheers :)Seaden
B
1

For Apache Server

  • Create a file name .htaccess
  • Edit that file and write index.html instead of index.php
  • For those who dont have any code, can write the code below in your .htaccess file : RewriteEngine On # If an existing asset or directory is requested go to it as it is RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR] RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d RewriteRule ^ - [L] # If the requested resource doesn't exist, use index.html RewriteRule ^ /index.html

  • This code might not work for SSL certificate - contact your hosting provider OR visit https://httpd.apache.org/docs/current/howto/htaccess.html to refer to docs.

Baerl answered 3/10, 2018 at 8:58 Comment(0)
R
1

You should try specifying the url in the build from the point the app should initialize:

Ng build --prod --base-href="http://your.url.com/subdirectory/etc"
Reprehension answered 14/1, 2019 at 2:25 Comment(0)
W
1

The exact reason why this particular issue is being faced is because of your server settings.

So when you implement the application there are certain steps that you will have to take. One of the essential steps is to mark a particular segment of your path say: http://domain-name/main where main being the path segment must be used as the identifier in your server settings say your server.js or app.js when it comes to a node backend or a webconfig file for IIS and Tomcat deployment.

The reason for marking a particular segment is so that when the server receives any request with that particular segment + additional you reroute it to your application in www or public folder for the angular router to kick in.

This process is known as url rewriting. Hence if the web server or the app server depending on the application size is not under your control then please use hashLocationStratergy else it is always neat to use pathLocationStartergy as it is helpful when it comes to history tracking and other aesthetic and performance benefits.

To read more on this you can visit these links:

For IIS: https://blog.angularindepth.com/deploy-an-angular-application-to-iis-60a0897742e7

Wildlife answered 16/4, 2019 at 9:53 Comment(0)
E
0

Add a dot in your base href strings.

Correct

<base href="./home">

Wrong

<base href="/home">

http://www.wisdomofjim.com/blog/solved-problems-with-resource-loading-failures-when-deploying-angular-2-webapps-live

Extrude answered 12/6, 2019 at 11:23 Comment(0)
O
0

If you use href, change them to routerlink.

Oomph answered 21/12, 2021 at 15:45 Comment(0)
D
-1
export const routes: Routes =[
   **{ path: '', redirectTo: '/', pathMatch: 'full'},**
     { path: 'about', component: AboutComponent},
     { path: 'user',  component: UserComponent},
     { path: 'specialpage',  component: SpecialpageComponent},
     { path: '**',  component: ErrorComponent}
    ];

Look at the content of blockquote. And then you give the name of "HttpModule" at providers:[] in app.module.ts

Thanks.

Damato answered 21/4, 2017 at 7:15 Comment(1)
@C.Peterscu I did as you have mentioned. But I couldn't get it work. I uploaded my project to github.com/kinathru/multiple-page-app.Ceto
C
-1

You can do it while registering your RouterModule.forRoot, you can pass a second object with propert {useHash: true} like the below :

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent} from './app.component';
import {appRoutes} from './app.routes';

@NgModule({
   declarations: [AppComponent],
   imports: [BrowserModule],
   RouterModule.forRoot(appRoutes , {useHash: true})],
   providers: [],
   bootstrap: [AppComponent],
})
export class AppModule {}
Chiliad answered 30/6, 2017 at 8:46 Comment(3)
i have the same problem but the solution does not workHairline
I have same problem. ng serve works good but when I deploy to my local, localhost/ doesnot workHimyaritic
don't forget to use the hash in your url when you test it, like: 127.0.0.1/#/homeAutoionization
S
-1

Change your index.html

<base href="/">
 to 
<base href="./">

Because we are using Tomcat we have mentioned this(.) for Routing is based on this(/) So same like http-server

I mean running normally ng serve you have seen only http://localhost:4200/

but run in server you have put your build into(dist) in webapps

so its come like

http://localhost:8080/dist/

so you need to add <base href="./">

i think this is the problem for you may be solved.

Simone answered 10/10, 2017 at 9:55 Comment(0)
O
-1

in the project folder create .htaccess file an then write

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]

follow this link: https://angular.io/guide/deployment

Orella answered 21/1, 2018 at 18:4 Comment(0)
F
-1

I used lite-server.

npm i -D  lite-server

then in my package.json file:

"scripts": {
   "ci:serve:employee-onboarding": "ng build --prod --project=employee-onboarding && lite-server -c lite-server-config.json"
}

to run it: npm run ci:serve:employee-onboarding

my lite-server-config.json file looks like this

{
  "port": 4201,
  "server": { "baseDir": "dist/apps/employee-onboarding" }
}
Forevermore answered 4/11, 2019 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.