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
https://i.stack.imgur.com/HaRAb.jpg
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.
https://i.stack.imgur.com/FWOme.jpg
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.
imports: [RouterModule.forRoot(routes, {useHash: true})],
. If it works this way, you need to enable HTML5 pushState on your production server.
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.
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.
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:
you can use HashLocationStrategy like mentioned above. 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.
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 :)
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.
It will happen because it goes to find a page about which is not there inside at all hence 404. Possible options:
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 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);
http-server --port 4200 -P http://someurl.com
raise an infinite loop in my case
If you, like me, want to make no code changes at all, simply use this instead:
https://www.npmjs.com/package/angular-http-server
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
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.
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"
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
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
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.
Add a dot in your base href strings.
Correct
<base href="./home">
Wrong
<base href="/home">
If you use href, change them to routerlink.
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.
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 {}
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.
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
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" }
}
Success story sharing
Hashtag
in URLs