Angular routeProvider without "#" [duplicate] - angularjs

I want to enable HTML5 mode for my app. I have put the following code for the configuration, as shown here:
return app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix = '!';
$routeProvider.when('/', {
templateUrl: '/views/index.html',
controller: 'indexCtrl'
});
$routeProvider.when('/about',{
templateUrl: '/views/about.html',
controller: 'AboutCtrl'
});
As you can see, I used the $locationProvider.html5mode and I changed all my links at the ng-href to exclude the /#/.
The Problem
At the moment, I can go to localhost:9000/ and see the index page and navigate to the other pages like localhost:9000/about.
However, the problem occurs when I refresh the localhost:9000/about page. I get the following output: Cannot GET /about
If I look at the network calls:
Request URL:localhost:9000/about
Request Method:GET
While if I first go to localhost:9000/ and then click on a button that navigates to /about I get:
Request URL:http://localhost:9000/views/about.html
Which renders the page perfectly.
How can I enable angular to get the correct page when I refresh?

From the angular docs
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)
The reason for this is that when you first visit the page (/about), e.g. after a refresh, the browser has no way of knowing that this isn't a real URL, so it goes ahead and loads it. However if you have loaded up the root page first, and all the javascript code, then when you navigate to /about Angular can get in there before the browser tries to hit the server and handle it accordingly

There are few things to set up so your link in the browser will look like http://yourdomain.com/path and these are your angular config + server side
1) AngularJS
$routeProvider
.when('/path', {
templateUrl: 'path.html',
});
$locationProvider
.html5Mode(true);
2) server side, just put .htaccess inside your root folder and paste this
RewriteEngine On
Options FollowSymLinks
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /#/$1 [L]
More interesting stuff to read about html5 mode in angularjs and the configuration required per different environment https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode
Also this question might help you $location / switching between html5 and hashbang mode / link rewriting

I had a similar problem and I solved it by:
Using <base href="/index.html"> in the index page
Using a catch all route middleware in my node/Express server as follows (put it after the router):
app.use(function(req, res) {
res.sendfile(__dirname + '/Public/index.html');
});
I think that should get you up and running.
If you use an apache server, you might want to mod_rewrite your links. It is not difficult to do. Just a few changes in the config files.
All that is assuming you have html5mode enabled on angularjs.
Now. note that in angular 1.2, declaring a base url is not recommended anymore actually.

Solution for BrowserSync and Gulp.
From https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643
First install connect-history-api-fallback:
npm --save-dev install connect-history-api-fallback
Then add it to your gulpfile.js:
var historyApiFallback = require('connect-history-api-fallback');
gulp.task('serve', function() {
browserSync.init({
server: {
baseDir: "app",
middleware: [ historyApiFallback() ]
}
});
});

You need to configure your server to rewrite everything to index.html to load the app:
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#wiki-how-to-configure-your-server-to-work-with-html5mode

I wrote a simple connect middleware for simulating url-rewriting on grunt projects. https://gist.github.com/muratcorlu/5803655
You can use like that:
module.exports = function(grunt) {
var urlRewrite = require('grunt-connect-rewrite');
// Project configuration.
grunt.initConfig({
connect: {
server: {
options: {
port: 9001,
base: 'build',
middleware: function(connect, options) {
// Return array of whatever middlewares you want
return [
// redirect all urls to index.html in build folder
urlRewrite('build', 'index.html'),
// Serve static files.
connect.static(options.base),
// Make empty directories browsable.
connect.directory(options.base)
];
}
}
}
}
})
};

If you are in .NET stack with MVC with AngularJS, this is what you have to do to remove the '#' from url:
Set up your base href in your _Layout page: <head> <base href="/"> </head>
Then, add following in your angular app config : $locationProvider.html5Mode(true)
Above will remove '#' from url but page refresh won't work e.g. if you are in "yoursite.com/about" page refresh will give you a 404. This is because MVC does not know about angular routing and by MVC pattern it will look for a MVC page for 'about' which does not exists in MVC routing path. Workaround for this is to send all MVC page request to a single MVC view and you can do that by adding a route that catches all url
routes.MapRoute(
name: "App",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" }
);

IIS URL Rewrite Rule to prevent 404 error after page refresh in html5mode
For angular running under IIS on Windows
<rewrite>
<rules>
<rule name="AngularJS" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
NodeJS / ExpressJS Routes to prevent 404 error after page refresh in html5mode
For angular running under Node/Express
var express = require('express');
var path = require('path');
var router = express.Router();
// serve angular front end files from root path
router.use('/', express.static('app', { redirect: false }));
// rewrite virtual urls to angular app to enable refreshing of internal pages
router.get('*', function (req, res, next) {
res.sendFile(path.resolve('app/index.html'));
});
module.exports = router;
More info at: AngularJS - Enable HTML5 Mode Page Refresh Without 404 Errors in NodeJS and IIS

As others have mentioned, you need to rewrite routes on the server and set <base href="/"/>.
For gulp-connect:
npm install connect-pushstate
var gulp = require('gulp'),
connect = require('gulp-connect'),
pushState = require('connect-pushstate/lib/pushstate').pushState;
...
connect.server({
...
middleware: function (connect, options) {
return [
pushState()
];
}
...
})
....

I am using apache (xampp) on my dev environment and apache on the production,
add:
errorDocument 404 /index.html
to the .htaccess solve for me this issue.

For Grunt and Browsersync use connect-modrewrite here
var modRewrite = require('connect-modrewrite');
browserSync: {
dev: {
bsFiles: {
src: [
'app/assets/css/*.css',
'app/*.js',
'app/controllers/*.js',
'**/*.php',
'*.html',
'app/jade/includes/*.jade',
'app/views/*.html',
],
},
options: {
watchTask: true,
debugInfo: true,
logConnections: true,
server: {
baseDir :'./',
middleware: [
modRewrite(['!\.html|\.js|\.jpg|\.mp4|\.mp3|\.gif|\.svg\|.css|\.png$ /index.html [L]'])
]
},
ghostMode: {
scroll: true,
links: true,
forms: true
}
}
}
},

I solved to
test: {
options: {
port: 9000,
base: [
'.tmp',
'test',
'<%= yeoman.app %>'
],
middleware: function (connect) {
return [
modRewrite(['^[^\\.]*$ /index.html [L]']),
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static('app')
];
}
}
},

I'm answering this question from the larger question:
When I add $locationProvider.html5Mode(true), my site will not allow pasting of urls. How do I configure my server to work when html5Mode is true?
When you have html5Mode enabled, the # character will no longer be used in your urls. The # symbol is useful because it requires no server side configuration. Without #, the url looks much nicer, but it also requires server side rewrites. Here are some examples:
For Express Rewrites with AngularJS, you can solve this with the following updates:
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/app/views/index.html'));
});
and
<!-- FOR ANGULAR ROUTING -->
<base href="/">
and
app.use('/',express.static(__dirname + '/public'));

I believe your issue is with regards to the server. The angular documentation with regards to HTML5 mode (at the link in your question) states:
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)
I believe you'll need to setup a url rewrite from /about to /.

We had a server redirect in Express:
app.get('*', function(req, res){
res.render('index');
});
and we were still getting page-refresh issues, even after we added the <base href="/" />.
Solution: make sure you're using real links in you page to navigate; don't type in the route in the URL or you'll get a page-refresh. (silly mistake, I know)
:-P

Finally I got a way to to solve this issue by server side as it's more like an issue with AngularJs itself I am using 1.5 Angularjs and I got same issue on reload the page.
But after adding below code in my server.js file it is save my day but it's not a proper solution or not a good way .
app.use(function(req, res, next){
var d = res.status(404);
if(d){
res.sendfile('index.html');
}
});

I have resolved the issue by adding below code snippet into node.js file.
app.get("/*", function (request, response) {
console.log('Unknown API called');
response.redirect('/#' + request.url);
});
Note : when we refresh the page, it will look for the API instead of Angular page (Because of no # tag in URL.) . Using the above code, I am redirecting to the url with #

I have found even better Grunt plugin, that works if you have your index.html and Gruntfile.js in the same directory;
https://npmjs.org/package/grunt-connect-pushstate
After that in your Gruntfile:
var pushState = require('grunt-connect-pushstate/lib/utils').pushState;
connect: {
server: {
options: {
port: 1337,
base: '',
logger: 'dev',
hostname: '*',
open: true,
middleware: function (connect, options) {
return [
// Rewrite requests to root so they may be handled by router
pushState(),
// Serve static files
connect.static(options.base)
];
}
},
}
},

I solved same problem using modRewrite.
AngularJS is reload page when after # changes.
But HTML5 mode remove # and invalid the reload.
So we should reload manually.
# install connect-modrewrite
$ sudo npm install connect-modrewrite --save
# gulp/build.js
'use strict';
var gulp = require('gulp');
var paths = gulp.paths;
var util = require('util');
var browserSync = require('browser-sync');
var modRewrite = require('connect-modrewrite');
function browserSyncInit(baseDir, files, browser) {
browser = browser === undefined ? 'default' : browser;
var routes = null;
if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) {
routes = {
'/bower_components': 'bower_components'
};
}
browserSync.instance = browserSync.init(files, {
startPath: '/',
server: {
baseDir: baseDir,
middleware: [
modRewrite([
'!\\.\\w+$ /index.html [L]'
])
],
routes: routes
},
browser: browser
});
}

I had the same problem with java + angular app generated with JHipster.
I solved it with Filter and list of all angular pages in properties:
application.yml:
angular-pages:
- login
- settings
...
AngularPageReloadFilter.java
public class AngularPageReloadFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.getRequestDispatcher("index.html").forward(request, response);
}
}
WebConfigurer.java
private void initAngularNonRootRedirectFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering angular page reload Filter");
FilterRegistration.Dynamic angularRedirectFilter =
servletContext.addFilter("angularPageReloadFilter",
new AngularPageReloadFilter());
int index = 0;
while (env.getProperty("angular-pages[" + index + "]") != null) {
angularRedirectFilter.addMappingForUrlPatterns(disps, true, "/" + env.getProperty("angular-pages[" + index + "]"));
index++;
}
angularRedirectFilter.setAsyncSupported(true);
}
Hope, it will be helpful for somebody.

Gulp + browserSync:
Install connect-history-api-fallback via npm, later config your serve gulp task
var historyApiFallback = require('connect-history-api-fallback');
gulp.task('serve', function() {
browserSync.init({
proxy: {
target: 'localhost:' + port,
middleware: [ historyApiFallback() ]
}
});
});

Your server side code is JAVA then Follow this below steps
step 1 : Download urlrewritefilter JAR Click Here
and save to build path WEB-INF/lib
step 2 : Enable HTML5 Mode $locationProvider.html5Mode(true);
step 3 : set base URL <base href="/example.com/"/>
step 4 : copy and paste to your WEB.XML
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
step 5 : create file in WEN-INF/urlrewrite.xml
<urlrewrite default-match-type="wildcard">
<rule>
<from>/</from>
<to>/index.html</to>
</rule>
<!--Write every state dependent on your project url-->
<rule>
<from>/example</from>
<to>/index.html</to>
</rule>
</urlrewrite>

I have this simple solution I have been using and its works.
In App/Exceptions/Handler.php
Add this at top:
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
Then inside the render method
public function render($request, Exception $exception)
{
.......
if ($exception instanceof NotFoundHttpException){
$segment = $request->segments();
//eg. http://site.dev/member/profile
//module => member
// view => member.index
//where member.index is the root of your angular app could be anything :)
if(head($segment) != 'api' && $module = $segment[0]){
return response(view("$module.index"), 404);
}
return response()->fail('not_found', $exception->getCode());
}
.......
return parent::render($request, $exception);
}

Related

How to fix 404 when reloading page in reactjs

I'm having trouble with reactjs in production, in my development machine i can easily refresh/reload without redirecting to 404 (not found page) but during production every time i reload or refresh in http://example.com/subdirectory/dashboard it will always go to 404 page of http://example.com but when I'm in development machine http://localhost:3000/subdirectory/dashboard when I refresh/reload the page it will reload as expected.
Note: In production I uploaded my static data into subdirectory so i use basename in my route.
If your serving your application on a standard webserver you can add a .htaccess using the following which enables rewrite rules:
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
It's likely that the 404 issue you're facing will be solved by simply just serving the above alongside the root of the project.
Alternately you could use the following as well which would serve it as a node server:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(9000);
The above can be used in conjunction with a .htaccess.
The create react app guys also promote serve which you can find here and the official react deployment documentation here
1: https://github.com/zeit/serve
2: https://create-react-app.dev/docs/deployment#docsNav
In your http server you have to rewrite all urls to your index.html file. If you are using nginx you can follow this: React-router and nginx
You might have to create a proxy server for your React.js application in production. You can do this in different ways but to do it with Express Node.js, you would have to create a file that looks like this:
const express = require('express');
const path = require('path');
const port = 7700;
const app = express();
app.use(express.static(path.join(__dirname, '../public')));
app.all('*', (req, res) => {
res.sendFile(path.join(__dirname, '../public/index.html'));
});
app.listen(port, () => {
console.log(`Listening on ${port}`);
});
The snippet above assumes you have built your React files and your index.html is in a public directory. This proxies all requests from the express server to your React routes with the * wildcard.
With that done, all that's left is to run this express app on your server and it handles your history API routes just like regular server routes.
add basename inside your src/index.js like this
(you can also add <base href="/subdirectory" target="_blank"> in your index.html file's head tag)
<Router
basename={'/subdirectory'}
>
<App />
</Router>
In your public folder make a file _redirects and write
/* /index.html 200 in it

Angular routing to redirect on new server with query string

Can someone help me understand how I can redirect an Angular state to a new URL with the query string parameters same as old ones?
Example
If my Angular application is on http://example.com, then all the requests to http://example.com/dashboard?id=123&view=test should be redirected to http://test.com/dashboard?id=123&view=test.
How should I define my state using the following code?
.state('dashboard', {
url: 'domain.com/dashboard',
controller: 'DashboardCtrl'
})
I know few tricks to transfer the state to controller and then handle it from there. But I'm wondering if there's a way to redirect it through the router only to avoid repetitive code in different controllers?
A. Angular 1
1. main.route.js:
.state('stateName', {
url: 'dashboard',
controller: ['$location', '$window', function ($location, $window) {
var url = 'domainB.com';
url += $location.$$url;
$window.location.href = url;
}]
})
B. Angular2/4
1. app.module.ts: Declare a custom provider like:
#NgModule({
providers: [
{
provide: 'externalUrlResolver',
useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
window.location.href = `${route.data.externalUrl}${state.url}`;
}
}
]
})
2. app-routing.module.ts: Now using the custom provider we can redirect externally.
const APP_ROUTES: Routes = [
{ path: 'dashboard', component: AnyComponent,
pathMatch: 'prefix',
resolve: { url: 'externalUrlResolver' },
data: { externalUrl: 'domainB.com'}
}
]
Input
http://domainA.com/dashboard?view=list&id=123&visible=yes
This will be redirected to:
http://domainB.com/dashboard?view=list&id=123&visible=yes
I think the best solution for this is not in Angular -- which runs in the browser after the code has already been loaded -- but on the server. If you're using Apache, add an .htaccess rule that will redirect all requests for example.com to test.com.
Create a file named .htaccess and place it in the application root directory:
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ http://test.com/$1 [R=302,L]
If you want the redirect to be permanent (meaning browsers and search engines will remember and won't bother going to the old domain), change 302 to 301
If this isn't a solution, you could use a CanActivateChild guard to indicate code that should be run before a route loads. Add it to the routing configuration for your base route and this code will always run before components etc... are initialized. Within it, you can read the current route and redirect the user with standard JavaScript to the new domain.

Routing working locally, not on Heroku servers

My AngularJS application won't route to /login when accessing /login directly. It will route to /login if I first access / then route from / to /login.
It is working on my local environment but not with Heroku servers. Are there some settings I have to configure on the Heroku server?
I am using angular-ui-router to route to different states throughout my application.
My app.js config snippet looks like this:
angular.module('app', [
angularUiRouter
])
.config(($stateProvider) => {
"ngInject";
$stateProvider
.state("home", {
url: "/",
template: "<home></home>"
})
.state("login", {
url: "/login",
template: "<login></login>"
});
})
Answering my own question.
Since the setup on this application is using MEAN stack, we have to also add routing from server side to client.
For all templates in AngularJS there has to be a routing from Express to AngularJS to index.html
for all templates and routings created in angular we need to get the request and send index.html in response
when we use angularjs stateProvider to route in Angularjs.., we have to also add routing to /dist/index.html from server side.
$locationProvider.html5Mode(true).hashPrefix('!'); has to be defined in app.js on AngularJS side
A snippet of my Server.js file:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/dist'));
app.get('/', function(req, res){
res.sendFile(__dirname + '/dist/index.html');
});
app.get('/login', function(req, res){
res.sendFile(__dirname + '/dist/index.html');
});

html5mode wrong mime type

I've got my redirect working correctly, the only problem is now all my style sheets are being served as text/html because it's being piped through core.index It only gives me the error for style sheets too not JS. How do I resolve this?
Error:
Resource interpreted as Stylesheet but transferred with MIME type text/html:
application.js
angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
function($locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
$locationProvider.hashPrefix('!');
}
])
express.js
app.use(express.static(path.resolve('./public')));
// Globbing routing files
config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
require(path.resolve(routePath))(app);
});
var core = require('../app/controllers/core.server.controller.js');
app.get('/*', core.index);
core.server.controller.js
exports.index = function(req, res) {
res.render('index', {
user: req.user || null,
request: req
});
};
core.client.routes.js
// Setting up route
angular.module('core').config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
// Redirect to home view when route not found
$urlRouterProvider.otherwise('/404');
$stateProvider
.state('admin', {
url: '/admin',
templateUrl: 'modules/core/views/home.client.admin.view.html',
});
}
]);
That would not be the correct way to serve static content - images, CSS and javascript files that run on the browser.
Take a look at this article
Basically, assuming that your directory structure is as follows:
-- public
|-- css
|-- img
`-- js
where public is the folder that contains all the sub folders for hosting stylesheets, images etc.
Then, in your nodejs code, where you have the var app = express() code, have the following code after it:
app.use(express.static('public'));
Thus, when the browser encounters a stylesheet declaration such as:
<link rel="stylesheet" href="css/style.css/>
it will make a request to /css/style.css and your express server will then correctly serve the stylesheet.
Have the code app.get("/*", core.index) at the end of all the above code to ensure that it is the last option that the server tries when attempting to match a request path to a request handler.

Reloading the page gives wrong GET request with AngularJS HTML5 mode

I want to enable HTML5 mode for my app. I have put the following code for the configuration, as shown here:
return app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix = '!';
$routeProvider.when('/', {
templateUrl: '/views/index.html',
controller: 'indexCtrl'
});
$routeProvider.when('/about',{
templateUrl: '/views/about.html',
controller: 'AboutCtrl'
});
As you can see, I used the $locationProvider.html5mode and I changed all my links at the ng-href to exclude the /#/.
The Problem
At the moment, I can go to localhost:9000/ and see the index page and navigate to the other pages like localhost:9000/about.
However, the problem occurs when I refresh the localhost:9000/about page. I get the following output: Cannot GET /about
If I look at the network calls:
Request URL:localhost:9000/about
Request Method:GET
While if I first go to localhost:9000/ and then click on a button that navigates to /about I get:
Request URL:http://localhost:9000/views/about.html
Which renders the page perfectly.
How can I enable angular to get the correct page when I refresh?
From the angular docs
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)
The reason for this is that when you first visit the page (/about), e.g. after a refresh, the browser has no way of knowing that this isn't a real URL, so it goes ahead and loads it. However if you have loaded up the root page first, and all the javascript code, then when you navigate to /about Angular can get in there before the browser tries to hit the server and handle it accordingly
There are few things to set up so your link in the browser will look like http://yourdomain.com/path and these are your angular config + server side
1) AngularJS
$routeProvider
.when('/path', {
templateUrl: 'path.html',
});
$locationProvider
.html5Mode(true);
2) server side, just put .htaccess inside your root folder and paste this
RewriteEngine On
Options FollowSymLinks
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /#/$1 [L]
More interesting stuff to read about html5 mode in angularjs and the configuration required per different environment https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode
Also this question might help you $location / switching between html5 and hashbang mode / link rewriting
I had a similar problem and I solved it by:
Using <base href="/index.html"> in the index page
Using a catch all route middleware in my node/Express server as follows (put it after the router):
app.use(function(req, res) {
res.sendfile(__dirname + '/Public/index.html');
});
I think that should get you up and running.
If you use an apache server, you might want to mod_rewrite your links. It is not difficult to do. Just a few changes in the config files.
All that is assuming you have html5mode enabled on angularjs.
Now. note that in angular 1.2, declaring a base url is not recommended anymore actually.
Solution for BrowserSync and Gulp.
From https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643
First install connect-history-api-fallback:
npm --save-dev install connect-history-api-fallback
Then add it to your gulpfile.js:
var historyApiFallback = require('connect-history-api-fallback');
gulp.task('serve', function() {
browserSync.init({
server: {
baseDir: "app",
middleware: [ historyApiFallback() ]
}
});
});
You need to configure your server to rewrite everything to index.html to load the app:
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#wiki-how-to-configure-your-server-to-work-with-html5mode
I wrote a simple connect middleware for simulating url-rewriting on grunt projects. https://gist.github.com/muratcorlu/5803655
You can use like that:
module.exports = function(grunt) {
var urlRewrite = require('grunt-connect-rewrite');
// Project configuration.
grunt.initConfig({
connect: {
server: {
options: {
port: 9001,
base: 'build',
middleware: function(connect, options) {
// Return array of whatever middlewares you want
return [
// redirect all urls to index.html in build folder
urlRewrite('build', 'index.html'),
// Serve static files.
connect.static(options.base),
// Make empty directories browsable.
connect.directory(options.base)
];
}
}
}
}
})
};
If you are in .NET stack with MVC with AngularJS, this is what you have to do to remove the '#' from url:
Set up your base href in your _Layout page: <head> <base href="/"> </head>
Then, add following in your angular app config : $locationProvider.html5Mode(true)
Above will remove '#' from url but page refresh won't work e.g. if you are in "yoursite.com/about" page refresh will give you a 404. This is because MVC does not know about angular routing and by MVC pattern it will look for a MVC page for 'about' which does not exists in MVC routing path. Workaround for this is to send all MVC page request to a single MVC view and you can do that by adding a route that catches all url
routes.MapRoute(
name: "App",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" }
);
IIS URL Rewrite Rule to prevent 404 error after page refresh in html5mode
For angular running under IIS on Windows
<rewrite>
<rules>
<rule name="AngularJS" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
NodeJS / ExpressJS Routes to prevent 404 error after page refresh in html5mode
For angular running under Node/Express
var express = require('express');
var path = require('path');
var router = express.Router();
// serve angular front end files from root path
router.use('/', express.static('app', { redirect: false }));
// rewrite virtual urls to angular app to enable refreshing of internal pages
router.get('*', function (req, res, next) {
res.sendFile(path.resolve('app/index.html'));
});
module.exports = router;
More info at: AngularJS - Enable HTML5 Mode Page Refresh Without 404 Errors in NodeJS and IIS
As others have mentioned, you need to rewrite routes on the server and set <base href="/"/>.
For gulp-connect:
npm install connect-pushstate
var gulp = require('gulp'),
connect = require('gulp-connect'),
pushState = require('connect-pushstate/lib/pushstate').pushState;
...
connect.server({
...
middleware: function (connect, options) {
return [
pushState()
];
}
...
})
....
I am using apache (xampp) on my dev environment and apache on the production,
add:
errorDocument 404 /index.html
to the .htaccess solve for me this issue.
For Grunt and Browsersync use connect-modrewrite here
var modRewrite = require('connect-modrewrite');
browserSync: {
dev: {
bsFiles: {
src: [
'app/assets/css/*.css',
'app/*.js',
'app/controllers/*.js',
'**/*.php',
'*.html',
'app/jade/includes/*.jade',
'app/views/*.html',
],
},
options: {
watchTask: true,
debugInfo: true,
logConnections: true,
server: {
baseDir :'./',
middleware: [
modRewrite(['!\.html|\.js|\.jpg|\.mp4|\.mp3|\.gif|\.svg\|.css|\.png$ /index.html [L]'])
]
},
ghostMode: {
scroll: true,
links: true,
forms: true
}
}
}
},
I solved to
test: {
options: {
port: 9000,
base: [
'.tmp',
'test',
'<%= yeoman.app %>'
],
middleware: function (connect) {
return [
modRewrite(['^[^\\.]*$ /index.html [L]']),
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static('app')
];
}
}
},
I'm answering this question from the larger question:
When I add $locationProvider.html5Mode(true), my site will not allow pasting of urls. How do I configure my server to work when html5Mode is true?
When you have html5Mode enabled, the # character will no longer be used in your urls. The # symbol is useful because it requires no server side configuration. Without #, the url looks much nicer, but it also requires server side rewrites. Here are some examples:
For Express Rewrites with AngularJS, you can solve this with the following updates:
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/app/views/index.html'));
});
and
<!-- FOR ANGULAR ROUTING -->
<base href="/">
and
app.use('/',express.static(__dirname + '/public'));
I believe your issue is with regards to the server. The angular documentation with regards to HTML5 mode (at the link in your question) states:
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)
I believe you'll need to setup a url rewrite from /about to /.
We had a server redirect in Express:
app.get('*', function(req, res){
res.render('index');
});
and we were still getting page-refresh issues, even after we added the <base href="/" />.
Solution: make sure you're using real links in you page to navigate; don't type in the route in the URL or you'll get a page-refresh. (silly mistake, I know)
:-P
Finally I got a way to to solve this issue by server side as it's more like an issue with AngularJs itself I am using 1.5 Angularjs and I got same issue on reload the page.
But after adding below code in my server.js file it is save my day but it's not a proper solution or not a good way .
app.use(function(req, res, next){
var d = res.status(404);
if(d){
res.sendfile('index.html');
}
});
I have resolved the issue by adding below code snippet into node.js file.
app.get("/*", function (request, response) {
console.log('Unknown API called');
response.redirect('/#' + request.url);
});
Note : when we refresh the page, it will look for the API instead of Angular page (Because of no # tag in URL.) . Using the above code, I am redirecting to the url with #
I have found even better Grunt plugin, that works if you have your index.html and Gruntfile.js in the same directory;
https://npmjs.org/package/grunt-connect-pushstate
After that in your Gruntfile:
var pushState = require('grunt-connect-pushstate/lib/utils').pushState;
connect: {
server: {
options: {
port: 1337,
base: '',
logger: 'dev',
hostname: '*',
open: true,
middleware: function (connect, options) {
return [
// Rewrite requests to root so they may be handled by router
pushState(),
// Serve static files
connect.static(options.base)
];
}
},
}
},
I solved same problem using modRewrite.
AngularJS is reload page when after # changes.
But HTML5 mode remove # and invalid the reload.
So we should reload manually.
# install connect-modrewrite
$ sudo npm install connect-modrewrite --save
# gulp/build.js
'use strict';
var gulp = require('gulp');
var paths = gulp.paths;
var util = require('util');
var browserSync = require('browser-sync');
var modRewrite = require('connect-modrewrite');
function browserSyncInit(baseDir, files, browser) {
browser = browser === undefined ? 'default' : browser;
var routes = null;
if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) {
routes = {
'/bower_components': 'bower_components'
};
}
browserSync.instance = browserSync.init(files, {
startPath: '/',
server: {
baseDir: baseDir,
middleware: [
modRewrite([
'!\\.\\w+$ /index.html [L]'
])
],
routes: routes
},
browser: browser
});
}
I had the same problem with java + angular app generated with JHipster.
I solved it with Filter and list of all angular pages in properties:
application.yml:
angular-pages:
- login
- settings
...
AngularPageReloadFilter.java
public class AngularPageReloadFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.getRequestDispatcher("index.html").forward(request, response);
}
}
WebConfigurer.java
private void initAngularNonRootRedirectFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering angular page reload Filter");
FilterRegistration.Dynamic angularRedirectFilter =
servletContext.addFilter("angularPageReloadFilter",
new AngularPageReloadFilter());
int index = 0;
while (env.getProperty("angular-pages[" + index + "]") != null) {
angularRedirectFilter.addMappingForUrlPatterns(disps, true, "/" + env.getProperty("angular-pages[" + index + "]"));
index++;
}
angularRedirectFilter.setAsyncSupported(true);
}
Hope, it will be helpful for somebody.
Gulp + browserSync:
Install connect-history-api-fallback via npm, later config your serve gulp task
var historyApiFallback = require('connect-history-api-fallback');
gulp.task('serve', function() {
browserSync.init({
proxy: {
target: 'localhost:' + port,
middleware: [ historyApiFallback() ]
}
});
});
Your server side code is JAVA then Follow this below steps
step 1 : Download urlrewritefilter JAR Click Here
and save to build path WEB-INF/lib
step 2 : Enable HTML5 Mode $locationProvider.html5Mode(true);
step 3 : set base URL <base href="/example.com/"/>
step 4 : copy and paste to your WEB.XML
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
step 5 : create file in WEN-INF/urlrewrite.xml
<urlrewrite default-match-type="wildcard">
<rule>
<from>/</from>
<to>/index.html</to>
</rule>
<!--Write every state dependent on your project url-->
<rule>
<from>/example</from>
<to>/index.html</to>
</rule>
</urlrewrite>
I have this simple solution I have been using and its works.
In App/Exceptions/Handler.php
Add this at top:
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
Then inside the render method
public function render($request, Exception $exception)
{
.......
if ($exception instanceof NotFoundHttpException){
$segment = $request->segments();
//eg. http://site.dev/member/profile
//module => member
// view => member.index
//where member.index is the root of your angular app could be anything :)
if(head($segment) != 'api' && $module = $segment[0]){
return response(view("$module.index"), 404);
}
return response()->fail('not_found', $exception->getCode());
}
.......
return parent::render($request, $exception);
}

Resources