Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Angularjs route work with expressJs route on html5mode

i have posted a question on stackoverflow (css and javascript didn't include on refresh) yesterday asking the question about why the css and javascript didn't include on my webpage after I refresh the webpage and found out it caused by the html5mode, so i have been searching the solution for this problem since yesterday but I can't really get an answer.

my folder structure

enter image description here

app.js

var express = require('express')
  , routes = require('./routes')
  , user = require('./routes/user')
  , http = require('http')
  , path = require('path')
  , mongoose = require('mongoose');

var app = module.exports=express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/public/views');
app.set('view engine', 'ejs');
app.use(express.cookieParser());
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);
app.use(function(request, response)
{
    console.log("catch all");
    writeFile("/public/views/master.ejs", request, response);
});
// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}
app.use(function (req,res) {
 res.status(404).render('error', {
                url: req.originalUrl
            });
});

app.get('/', routes.index);
app.get('/:name', routes.view);
app.get('*', routes.risk);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

index.js

exports.index = function(req, res){
  res.render('master', { title: 'Hello World' });
};
exports.view = function (req, res) {
  var name = req.params.name;
  res.render(name);
};
exports.risk = function(req, res){
  res.sendfile(__dirname + "/public/views/master.ejs");
};

for the exports.risk i was trying to make the expressJs to render the master page 1st before it renders other but it doesn't work.

angularJs

var SymSal = angular.module('SymSal',[]).
 config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
    $routeProvider.
      when('/', {
        templateUrl: 'main.ejs',
        controller: 'IndexCtrl'
      }).
      when('/login',{
        templateUrl: 'login.ejs',
        controller: 'IndexCtrl'
      }).
      when('/register',{
        templateUrl: 'register.ejs',
        controller: 'IndexCtrl'
      }).
      when('/about',{
        templateUrl: 'about.ejs',
        controller: 'IndexCtrl'
      }).
      otherwise({
        templateUrl: 'error.ejs'

      });
    $locationProvider.html5Mode(true);
  }]);

 SymSal.controller('IndexCtrl',function(){

 })

Your help is appreciated, THANK YOU !!

like image 375
John Lim Avatar asked Mar 21 '23 21:03

John Lim


2 Answers

for the exports.risk i was trying to make the expressJs to render the master page 1st before it renders other but it doesn't work.

Routes are matched in sequential order:

app.get('/', routes.index);
app.get('/:name', routes.view);
app.get('*', routes.risk);

If the route matches '/' render routes.index. If the route doesn't match '/' check if it matches '/:name' (e.g. /login, /register) and render routes.view. If the route doesn't match '/' and '/:name' (e.g. route is something like /user/1) routes.risk will be rendered.

To make express render the master page first you need to remove the route matcher for '/' and '/:name' and keep the universal matcher ('*') that will match every route.

Now the server will send back the master page no matter what url you provide. If you call localhost:3000/login the server will send back the master page (same page as is if you would call localhost:3000). Angular will see that a path (/login) is specified and will call the appropriate $routeProvider.when() function.

To handle api calls (like get data from db, save data to db) you need to specify a route matcher for this and place it above the universal matcher ('*'):

app.get('/api', routes.api);

It's important to mention that you don't use '/api' in your $routeProvider.when().

What's left is the correct handling of static files: Remember every url is handled by the universal matcher ('*'). So static files get render with the wrong MIME type. To fix this, you need to make all static files accessible under a specific path e.g '/static'. Simply update

app.use(express.static(path.join(__dirname, 'public')));

to

app.use('/static', express.static(path.join(__dirname, 'public')));

You need to update all paths in your master page to match the new pattern: '/js/angular.js' is now '/static/js/angular.js'

like image 164
bekite Avatar answered Mar 30 '23 00:03

bekite


Thanks to @bekite for the basis of a plausible solution. Building on his initial solution, I've found the following a little cleaner for myself, and it avoids needing to update and maintain all your paths with a /static prefix. Note that the app.get('*', routes.risk) did not work for me (Express v3.4.4), however using a regex did:

...
app.use(app.router);
app.use(express.static(path.join(__dirname, 'app')));

// Express routes - ensure these are not defined in Angular's app.js $routeProvider:
app.get('/api', routes.api);

/**
 * ANGULAR APP ROUTING
 * --------------------
 * These routes will fallback to Angular for resolution of any uri:
 * Note that the * wildcard will not work, hence the alternate regex
 * It is crucial that 'static' assets never be referenced with a leading
 * forward slash '/', else they'll match this rule and 404's will occur
 */
//app.get('*', routes.risk);
app.get('/[a-z]{0,100}', routes.risk);

////
// alternatively:
//
// app.get('/[a-z]{0,100}', function(req, res) {
//     res.sendfile('app/index.html', {title: 'Default Title', stuff: 'More stuff to send to angular'}
// });
...

As per @bekite, Angular paths are passed through to a generic route (which may provide further branching if desired), and captured by the Angular $routeProvider. Express paths are caught with the /api prefix and processed server-side as needed.

like image 40
dharmatron Avatar answered Mar 29 '23 23:03

dharmatron