Hi have created one angularJS application with yeoman, grunt and bower. I have enabled the html5Mode for the application. And its working. But, when I refresh a page (localhost:9000/login), it says
Cannot GET /login //or any url I type or refresh
Here is application structure
MainApp
|
|__app
| |
| |__bower_components
| |
| |__scripts
| | |
| | |__ app.js
| | |
| | |__contollers -- login.js, home.js, register.js
| | |
| | |__service -- js files
| | |
| | |__styles -- CSS files
| | |
| | |__views -- main.html, login.html, register.html,home.html
| |
| |__ index.html
|
|__ node_modules
|
|__ bower.json, Gruntfile.js, karma-conf.js, karma-e2e.conf.js, package.json
Here is my app.js routing
'use strict';
var superClientApp=angular.module('mainApp', ['ngCookies']);
//Define Routing for app
superClientApp.config(['$routeProvider', '$locationProvider',
function($routeProvider,$locationProvider) {
$routeProvider
.when('/login', {
templateUrl: 'login.html',
controller: 'LoginController'
})
.when('/register', {
templateUrl: 'register.html',
controller: 'RegisterController'
})
.when('/home', {
templateUrl: 'views/home.html',
controller: 'homeController'
})
.otherwise({
redirectTo: '/login'
});
$locationProvider.html5Mode(true);
}]);
Here is my part of Gruntfile.js
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
var mountFolder = function (connect, dir) {
return connect.static(require('path').resolve(dir));
};
var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
// configurable paths
var yeomanConfig = {
app: 'app',
dist: 'dist'
};
try {
yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
} catch (e) {}
grunt.initConfig({
yeoman: yeomanConfig,
watch: {
coffee: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'],
tasks: ['coffee:dist']
},
coffeeTest: {
files: ['test/spec/{,*/}*.coffee'],
tasks: ['coffee:test']
},
styles: {
files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
tasks: ['copy:styles', 'autoprefixer']
},
livereload: {
options: {
livereload: LIVERELOAD_PORT
},
files: [
'<%= yeoman.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',
'{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
},
autoprefixer: {
options: ['last 1 version'],
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
},
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost'
},
proxies: [
{
context: '/serverApp',
host: 'localhost',
port: '8080',
https: false,
changeOrigin: false
}
],
livereload: {
options: {
middleware: function (connect) {
return [
lrSnippet,
proxySnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app)
];
}
}
},
I have gone through this SO question. And based on the accepted answer, I changed my Gruntfile.js to below.
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
var mountFolder = function (connect, dir) {
return connect.static(require('path').resolve(dir));
};
var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;
var urlRewrite = require('grunt-connect-rewrite');
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
// configurable paths
var yeomanConfig = {
app: 'app',
dist: 'dist'
};
try {
yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
} catch (e) {}
grunt.initConfig({
yeoman: yeomanConfig,
watch: {
coffee: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'],
tasks: ['coffee:dist']
},
coffeeTest: {
files: ['test/spec/{,*/}*.coffee'],
tasks: ['coffee:test']
},
styles: {
files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
tasks: ['copy:styles', 'autoprefixer']
},
livereload: {
options: {
livereload: LIVERELOAD_PORT
},
files: [
'<%= yeoman.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',
'{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
},
autoprefixer: {
options: ['last 1 version'],
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
},
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
base: 'app',
middleware: function(connect, options) {
// Return array of whatever middlewares you want
return [
// redirect all urls to index.html in build folder
urlRewrite('app', 'index.html'),
// Serve static files.
connect.static(options.base),
// Make empty directories browsable.
connect.directory(options.base)
];
}
},
proxies: [
{
context: '/serverApp',
host: 'localhost',
port: '8080',
https: false,
changeOrigin: false
}
],
livereload: {
options: {
middleware: function (connect) {
return [
lrSnippet,
proxySnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app)
];
}
}
},
But still I am getting the same error when I refresh the page. How to solve this?
You must have a server side solution to redirect all non resolving traffic to index.html
I will share an htaccess version of this and a node.js version. I have also implemented this in spring-boot, but that was a little bit more involved.
HTACCESS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /the-base-from-index.html/
RewriteEngine on
RewriteCond %{HTTP:X-Requested-With} !XMLHttpRequest$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.html [L]
</IfModule>
In order for this to work your apache config must have mod_rewrite enabled and also allow override all. The rewrite will work in apache config as well.
There is also a condition that allows you to return 404 on ajax request. In order for that to work you must register a pre-flight header to all $http request signifying that they are xmlhttp requests. something like:
myapp.config(function($httpProvider) {
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
});
NODEJS
var express = require('express');
var bodyParser = require('body-parser')
var path = require('path')
var app = express();
app.use(express.static(__dirname + '/app'));
app.get('/*', function(req, res){
res.sendFile(__dirname + '/app/index.html');
});
app.listen(9090);
console.log("Node Server Listening on port 9090");
This is dirt simple, and could be improved. Namely with a check on the req headers for the xmlhttp header, similarly to what the htaccess example is doing.
These get the general idea across. Basically any traffic that would result in a 404 gets served index.html instead. This allows you to continue to serve up static content, but your routes (as long as they don't actually exist on the disk at that location) will return index.html.
Warning! without a solution like the preflight header on all ajax request, any unresolved template request will result in an infinite loop and likely lock up your browser.
By now, this must be a common issue with Angular to be dealt with for routing to work.
I found similar issues on SO, which suggest the same html5 config to workaround the hashbang method - see AngularJS: how to enable $locationProvider.html5Mode with deeplinking Also a tutorial at scotch.io https://scotch.io/quick-tips/pretty-urls-in-angularjs-removing-the-hashtag can be a start.
But most importantly, I remember that this is the one that did the trick.
https://gist.github.com/leocaseiro/4305e06948aa97e77c93
I run a Apache Web server. I don't recollect at what level (globally or per app) I did the configuration, but best would be to consult the web server docs, Apache's was good.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With