Here is my front-end AngularJS routing:
$urlRouterProvider.otherwise('/');
$locationProvider.html5Mode(true).hashPrefix('!');
$stateProvider
.state('main', {
url: '/',
templateUrl: '/views/main.html',
controller: 'MainCtrl'
})
.state('review', {
url: '/review',
templateUrl: '/views/review.html',
controller: 'NewReviewCtrl'
})
.state('model', {
url: '/:make/:model',
templateUrl: '/views/model.html',
controller: 'ModelPageCtrl'
});
And here is my server-side routing (node.js):
//...some routes
app.use('/*', function(req, res){
res.sendFile(config.rootPath + '/public/index.html');
});
With /review route, everything works fine, but not with /:make/:model:
HTML:
See more details...
If I go to this page through that link, everything works fine, but if I refresh or go directly to localhost:3030/lenovo/thinkpad-t430 I get this error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module app to:
Error: [$injector:nomod] Module 'app' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
It is probably because of the html5mode. And I suggest it not working with this URL because, it is a 2 level URL.
How can I fix this?
Edit: Express.js config:
app.set('view engine', 'ejs');
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.use(bodyParser());
app.use(session({secret:"some_secret"}));
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(config.rootPath+"/public"));
I created working plunker, which does use the same UI-Router setting as mentioned above. Everything is working. So, we should know now, that the Client side should be set properly.
The main and only difference is, more explicit setting, which allows us to omit <base href="/" /> in the head
$locationProvider
.html5Mode(
{
enabled: true,
requireBase: false,
})
Or we can use the <base> element (check the index.html <head>) int that plunker
Seems like the issue is on the server side, please check twice:
How to: Configure your server to work with html5Mode
Related
I have written 2 ng-app in one project, one is user and the other admin.
To remove the # from the url I had used the below code in both app.config function
$locationProvider.html5Mode(true);
and in app/index.html
<base href="/">
and in app/admin/index.html
<base href="/admin/">
user app.js
app.config(['$routeProvider','$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider.
when('/properties', {
templateUrl: 'views/modules/properties.html'
})
.when('/pages', {
templateUrl: 'views/pages/pages.html'
})
.when('pages/editpages', {
templateUrl: 'views/pages/editPages.html',
controller: 'editPagesController',
});
$locationProvider.html5Mode(true);
}
]);
server.js
app.use('/', express.static('app', { redirect: false }));
app.get('/*', function(req, res){
res.sendFile(__dirname + '/app/index.html');
});
I'm getting the following error, if there's an extra param in the route
Uncaught SyntaxError: Unexpected token <
The above error I get when my urls are
http://localhost:8080/properties/
http://localhost:8080/properties/something
http://localhost:8080/pages/
http://localhost:8080/pages/editpages
This works fine if the url is used without the last / i.e.
http://localhost:8080/properties
http://localhost:8080/pages
I have refered to this questions too but couldn't resolve the issue
Node / Angular app Uncaught SyntaxError: Unexpected token <
How to use multiple index pages in angular
Solution found
The placement of the <base href="/"> was just before the </head> before which there were all scripts and links tags. After adding the <base href="/"> after title tag the issue was resolved
The problem is with the express middleware, take for example
app.use(express.static('./build/'));
// Any invalid calls for templateUrls are under app/* and should return 404
app.use('/app/*', function(req, res, next) {
four0four.send404(req, res);
});
// Any deep link calls should return index.html
app.use('/*', express.static('./build/index.html'));
(Update: I solved the problem. Just look at the end of the question)
I am running with this problem that seems trivial to me, but I am very frustrated because I am not able to figure it out:
I scaffolded an Angular application using yeoman generator-angular. I need to use the html5mode of Angular to get rid of the hashtag (please, see app.js below). I am using a node express server (see server.js) to run the app built with grunt build.
As required, I added the option in the server to redirect to index.html when accessing the app from any specific route. It works with one level of "routing", i.e., localhost:8080/research, but it does not work for two "levels" or more, i.e., localhost:8080/research/human. In this case, when refreshing the browser, I get this error:
The stylesheet http://localhost:8080/research/styles/vendor.8089f103.css was not loaded because its MIME type, "text/html", is not "text/css". human
The stylesheet http://localhost:8080/research/styles/main.e7eff4cf.css was not loaded because its MIME type, "text/html", is not "text/css". human
SyntaxError: expected expression, got '<' vendor.01f538ae.js:1:0
SyntaxError: expected expression, got '<'
I have searched everywhere, I have tried all sort of options, but I am not able to fix it. I would really appreciate some help, please!
app.js
angular
.module('testAngularApp', [
'ngRoute',
'ngTouch',
'ngAnimate',
'ngSanitize',
'angulartics'
])
.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/', {
templateUrl: 'views/mainFrontpage.html',
controller: 'MainFrontpageController'
})
.when('/research', {
templateUrl: 'views/research.html',
controller: 'ResearchController'
})
.when('/research/human', {
templateUrl: 'views/research-human.html',
controller: 'ResearchController'
})
.when('/research/fly', {
templateUrl: 'views/research-fly.html',
controller: 'ResearchController'
})
.otherwise ({
templateUrl: 'views/notyetready.html',
});
});
server.js
'use strict';
var express = require('express');
var path = require('path');
var morgan = require('morgan');
var fs = require('fs');
var currentDir = process.cwd();
var app = express();
var staticStats = fs.statSync( currentDir + '/dist');
if (staticStats.isDirectory()) {
app.use('/', express.static(currentDir + '/dist'));
// Here I have tried many different combinations
app.use("/styles", express.static(__dirname + "dist/styles"));
app.use("/scripts", express.static(__dirname + "dist/scripts"));
app.use("/views", express.static(__dirname + "dist/views"));
app.use("/fonts", express.static(__dirname + "dist/fonts"));
app.use("/templates", express.static(__dirname + "dist/templates"));
app.use("/images", express.static(__dirname + "dist/images"));
app.all('/*', function(req, res, next) {
// Just send the index.html for other files to support HTML5Mode
res.sendFile('dist/index.html', { root: __dirname });
});
var server = app.listen(8080);
console.log('Node Express server listening on http://%s:%d', server.address().address,8080);
}
else {
console.log('No /dist folder, did not start the server');
}
Update: Solution
Thanks to the comments of the users, I asked the question in a different way and found the solution that make it works here. That is, the <base href="/"> tag must be located before the <link rel="stylsheet"..> tags (what a hard time I got for such a stupid thing!)
Why not use nested routing in angular routing like this ?
https://github.com/angular-ui/ui-router
I wanted this to be just a comment but I can't yet, Have you tried placing the html5 mode below your routes ?
Can remove the if statement if you do not need it.
.when('/research/fly', {
templateUrl: 'views/research-fly.html',
controller: 'ResearchController'
})
.otherwise ({
templateUrl: 'views/notyetready.html',
});
if(window.history && window.history.pushState){
$locationProvider.html5Mode(true);
}
});
Maybe try using a different pattern to match against that sounds like the cause of the problem
app.all('/**/*', function(req, res, next)
However I would ask why are you serving the static files with node + express? If all you want is a static file server for local development why not try grunt-serve
Then if you want to serve the static files on a server you can use nginx which works really well with angular in html5 mode
I am using NodeJS + expressJS on server and AngularJS on client
AngularJS controller have the following routes:
app.config(function($routeProvider,$locationProvider) {
$routeProvider
.when('/field1/:id', {
controller: 'ctrl1',
templateUrl: "/view/mainpages/field1.html"
})
.when('/field2/:id', {
controller: 'ctrl2',
templateUrl: "/view/mainpages/field2.html"
})
........... many many
.when('/', {
controller: 'ctrl0',
templateUrl: "/view/mainpages/index.html"
})
.otherwise({redirectTo : '/'});
NodeJS static:
app.all('/*', express.static(__dirname + '/client'));
How to make the server redirects all the static pages on their paths relative to the root, in the case when the call goes directly through this link.
example:
server:3000/field1/23 must call server:3000/client/index.html
and index must upload JS like server:3000/js/cntr1.js
Upd: I found a solution to the problem. But it looks ugly as a crutch
app.get('/field1/([0-9]+$)', function(req, res, next) {
req.url='/index.html';
next();
});
app.use('/field1', express.static(__dirname + '/client'));
Perhaps there is a more elegant solution?
In expressjs, you can define the route using regular expression pattern matching. That means, anything under a specific set will always return the same page from the server side, and you will let angular decide what exactly to display or do which action.
Its not a great idea to redirect everything at / to same page as you need to manage your assets and they exist on the / part too , instead its a good idea to use a subdir which is easy to manage
Following snippet will redirect everything under the app path to the same page on server side
app.get('/app/*', function(req, res) {
res.send('app_index');
})
if you do not want to be limited to one app dir, instead you have a number of different root level dirs, all of which need to point to the same page, you can do something like this
app.get('/(app|server|client|form|dashboard)/*', function(req, res) {
res.send('app_index');
})
I have started an application where I use Express 4.0 server. I have set up the routes based heavily on a tutorial on scotch.io (http://scotch.io/tutorials/javascript/build-a-restful-api-using-node-and-express-4), builiding a backend api to serve the front end SPA (angularjs). Here is an extract of the backend router:
router.route('/users')
.get(function (req, res) {
User.find(function(err, users) {
if (err) {
res.send(err);
}
else {
res.json(users);
}
});
})
Further down
// home page route (http://localhost:8080)
router.get('/', function(req, res) {
res.send('API ROOT');
});
app.use('/api', router);
From the frontend i use just a get to get the users from the api:
$http.get('/api/users')
.success(function(data) {
$scope.users = data;
});
and the angular routes are set up like this:
.when('/', {
templateUrl: 'views/home.html',
controller: 'MainController'
})
.when('/users', {
templateUrl: 'views/user.html',
controller: 'UserController'
})
The index.html has a link to /users.
When starting the server and goint into localhost:8080 it loads fine, and clicking the users loads the user.html view and lists the users.
However, if i click refresh when browser is in localhost:8080/users
I get:
Cannot get /users
Is it a controller issue?
Or is it a problem with backend routing?
Any feedback/suggestions would be very welcome!
Searching some more, I see that there are several sollutions that might fix this:
Either on the frontend (angular routes part) adding this to the end:
// >>> redirect other routes to
otherwise({
redirectTo: '/'
});
Or on the backend (after all your routes):
app.get('*', function(req, res){
res.render('index.html');
});
Which sollution is better (or is it reccomended to use both...?)
You should use both.
You need to provide a catch-all / wildcard route to your express application such that any routes that are not explicitly matched will return the index page which will load Angular and allow your Angular routes to then take over. Note that this should always be your last route (they are parsed in order).
app.get('*', function(req, res){
res.render('index.html');
});
Then in your Angular app you should have a default route to catch any un-matched routes on the client side. This route will come into effect if the application is already loaded but an unknown route is encountered whereas the server side solution above will handle any direct requests.
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'home.html',
controller: 'homeCtrl'
}).
otherwise({
redirectTo: '/'
});
}]);
I have an angular app with a directory structure
app
..views
....partials
......main.jade
......foo.jade
....index.jade
and routes defined like:
'use strict';
angular.module('myApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'firebase',
'myApp.config'
])
.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/', {
templateUrl: '/partials/main',
controller: 'MainCtrl'
})
.when('/foo/:fooName', {
templateUrl: '/partials/foo',
controller: 'FooCtrl'
})
.otherwise({
redirectTo: '/'
});
});
I'm using express on the server side and the relevant code is:
// server.js
app.configure('development', function(){
app.use(express.static(path.join(__dirname, '.tmp')));
app.use(express.static(path.join(__dirname, 'app')));
app.use(express.errorHandler());
});
app.configure('production', function(){
app.use(express.favicon(path.join(__dirname, 'public/favicon.ico')));
app.use(express.static(path.join(__dirname, 'public')));
});
app.get('/', routes.index);
app.get('/partials/:name', routes.partials);
//routes.js
exports.index = function(req, res){
res.render('index');
};
exports.partials = function(req, res){
var name = req.params.name;
res.render('partials/' + name);
};
The main route "/" loads fine and when i click to "/foo/bar" the partial view foo.jade loads as expected. However, when I try visiting "/foo/bar" directly in the URL i get a 404 response from Express "cannot GET /foo/bar" which makes sense since there's no route like this defined in express. However, I thought this was the whole point of defining the angular router..i.e. it's supposed to intercept this request and actually ask for "/partials/foo".
I tried adding
//redirect all others to the index (HTML5 history)
app.get('*', routes.index);
but it didnt solve the issue and seemed to be catching even the requests for static js assets and responding with the contents of index.html which is pretty bad.
I'm sure I'm doing something wrong. How can I fix things so that I can directly visit the URLs?
The reason routing is behaving like this is html5mode turned on.
Notice the line: $locationProvider.html5Mode(true);
You need to understand that when you try to access "/foo/bar" directly your browser sends HTTP GET request to this URL. When you try to access this url via link clicking, like you said, Angular is intercepting this action and calls History.pushState that only updates browser's link.
What you need to do in order to make html5mode routing work is implementing url rewrite. Every request has to be redirected to your index file that bootstraps AngularJS application, but this needs to be done on your server. Angular will render the desired page by itself.
Check out connect mod rewrite grunt task that does exacly that.
You can also you this middleware directly.