Angular and Sails routing configuration - node.js

Is there any Sails.js (or Node) configuration which can prevent Angular routing from working?
No matter what approach I take, every rout apart from ones in sails' routes.js return 404.
I've tried both 1.2 and 1.3 Angular versions, and I'm using Sails v 0.9.15.
All scripts are loaded in correct order (for example):
<script src="/linker/js/libs/angular/angular.js"></script>
<script src="/linker/js/libs/angular/angular-resource.js"></script>
<script src="/linker/js/libs/angular/angular-route.js"></script>
<script src="/linker/js/libs/angular/angular-sanitize.js"></script>
...
<script src="/linker/js/app.js"></script>
I'm using the ngRoute correctly:
var myApp= angular.module('myApp', ['ngRoute']);
Here are my routes in Angular's app.js:
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/profile',
{ templateUrl: 'linker/views/profile.html', controller: 'MainCtrl' })
.when('/profile-detail',
{ templateUrl: 'linker/views/profile_detail.html', controller: 'MainCtrl' });
}]);
And I'm also using location provider:
myApp.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
Additional details:
ng-app and ng-view are correctly placed, and my paths are correct. I can correctly show profile.html with Angular (and also to include data from Sails' Restful API).
Problem is, that I can only do that for routes defined in Sails' routes.js.
Example:
module.exports.routes = {
'/' : {
controller: 'main',
action: 'index'
},
'/signup' : {
controller: 'main',
action: 'signup'
},
'/login' : {
controller: 'main',
action: 'login'
},
'/profile': {
controller: 'users',
action: 'profile'
} //......
So basically, in order to show some html content with Angular, I have to define EXACTLY THE SAME route in Sails' configuration, which makes no sense.
Any ideas? P.S. I'll provide additional data if needed.

Try removing the html5 mode to see what happens:
$locationProvider.html5Mode(false);
If you are using your sails application only to provide an API for your Angular app, but you are using the same backend to serve your angular code, then you could prefix all API routes with 'api' in order to prevent having conflicts with angular routes.
Instead of /profile you would have /api/profile
EDIT:
I've taken a look into the Sails.js framework and made a small app to test it.
I was able to successfully have routes in angular work that were not defined by sails.
I think there is a misunderstanding of how angular routing works.
If you change the path with window.location or type the url manually, the browser will send a get request to the server. So in your case, there will be a request for /profile or /profilee and the server will look at the available routes and will throw a 404 if nothing matches.
To prevent that, you should actually change the path using angular methods. Angular uses the '#' symbol in the path to prevent the browser of sending requests to the server when the url changes. Browsers ignore changes after the '#' symbol. Or in your case, a similar effect is achieved using the html5 mode. Beware though that using html5 mode can cause troubles when users refresh the page, since then a request will be made to the server (more on how to fix that below).
So, what you should be using to change the paths with javascript is the $location service. In your angular views, you can also use normal anchor tags like, because angular parses those:
Go to profile
Since what you have is a single page application, alls views are handled by the client. All the paths beyond the root (/) are virtual paths created by angular. They usually don't exist in the server. Only the root is available. When using html5 mode that can be a problem.
A way to fix that is to rewrite the routing of the server to serve everything else as if it was a request to the root path. In sails they even suggest how to do that in the config/routes.js:
// What about the ever-popular "vanity URLs" aka URL slugs?
// (you might remember doing this with `mod_rewrite` in Apache)
//
// This is where you want to set up root-relative dynamic routes like:
// http://yourwebsite.com/twinkletoez
//
// NOTE:
// You'll still want to allow requests through to the static assets,
// so we need to set up this route to ignore URLs that have a trailing ".":
// (e.g. your javascript, CSS, and image files)
'get /*(^.*)': 'UserController.profile'
Regarding the API in sail, you can configure a prefix inside the config/controllers.js file:
/**
* `prefix`
*
* An optional mount path for all blueprint routes on a controller, including `rest`,
* `actions`, and `shortcuts`. This allows you to continue to use blueprints, even if you
* need to namespace your API methods.
*
* For example, `prefix: '/api/v2'` would make the following REST blueprint routes
* for a FooController:
*
* `GET /api/v2/foo/:id?`
* `POST /api/v2/foo`
* `PUT /api/v2/foo/:id`
* `DELETE /api/v2/foo/:id`
*
* By default, no prefix is used.
*/
prefix: '',

Related

How to pass routing control from Node server to client?

Let's say this is the part where all routes are handled on a Node server with Angular front end. When the user enters the site URL, the server sends all the static files for the browser to render. However, I would like the client to handle certain routes instead of it going directly to the server.
For example, if I enter www.exemple.com/page2, a Get request is sent to the server but the route doesn't exist so the request just hangs there and ultimately resulting an error.
I want Angular to handle the routing instead of it going automatically to the server. I've only successfully got this to work on localhost where the Angular app is served from a different port than the one that the server listens to. Can anyone one tell me how to achieve this? Thanks a lot.
module.exports=function(app,dir){
app.use(express.json());
app.use(express.static(dir+'/dist/probeeshq'));
app.get('/',(req,res)=>{res.sendFile(path.join(dir));});
app.use('/auth', userAuth);
app.use('/api/me',userData);
app.use('/api/org',organization);
app.use('/api/messaging',messaging);
app.use('/api/search',search);
app.use(error);
}
This is what I have in Angular
const routes: Routes = [
{ path:'', component: HomeComponent },
{ path:'project_gaea', component:ProjectGaeaComponent},
{ path:'dashboard', component: DashboardComponent ,canActivate:[AuthGuardService]},
{ path:'explore', component:ExploreComponent, canActivate:[AuthGuardService]},
{ path:'create', component: CreateComponent },
{ path:'user/:id', component:UserProfileComponent},
{ path:'**', component: PageNotFoundComponent}
];
You can achieve this by implementing the Route feature that Angular has out of the box. After you implement this, you can then just use your back-end as an API.
So it turns out that I was supposed to serve the application like this:
app.use('/', express.static(dir));
And Express will let Angular handle all the routing after failing to match all the routes at the server side. dir is just the path were the Angular app is.
I have the same issue, with wildcard routes we can fix this. And client routes handling any unrecognised urls. Good from the user experience perspective no issues on that
like so
app.get("*", (req, res) => {
// send HTML files
});
But what about auditing wise, like a simple question like "if I send a unrecognised URL to server, it should give a 404 status code, instead of redirecting me to client and showing a 404 page or something"
Made a valid point, which doubted my knowledge on the web. But to resolve this we need to manually whitelist the client URL's in server, still figuring out myself, if any better solution please let me know.

How to handle angular2 route path in Nodejs?

I am working on a NodeJS app with Angular2. In my app, I have a home page and search page. For home page I have an HTML page that will render for the localhost:3000/ and from home page user navigate to search i.e localhost:3000/search page that I handled by angular2 routes.
I don't have the page for the search page its view render by the angular2. But when I directly hit localhost:3000/search as I don't have this routing in my node app it gives the error.
I don't know How to handle this in node app?
If you enter localhost:3000/search directly in the browser navigation bar, your browser issues an request to '/search' to your server, which can be seen in the console (make sure you check the 'Preserve Log' button).
Navigated to http://localhost:3000/search
If you run a fully static server, this generates an error, as the search page does not exist on the server. Using express, for example, you can catch these requests and returns the index.html file. The angular2 bootstrap kicks-in, and the /search route described in your #RouteConfig is activated.
// example of express()
let app = express();
app.use(express.static(static_dir));
// Additional web services goes here
...
// 404 catch
app.all('*', (req: any, res: any) => {
console.log(`[TRACE] Server 404 request: ${req.originalUrl}`);
res.status(200).sendFile(index_file);
});
You need to use HashLocationStrategy
import { LocationStrategy, HashLocationStrategy } from "angular2/router";
bootstrap(AppComponent, [
ROUTER_PROVIDERS,
provide(LocationStrategy, { useClass: HashLocationStrategy })
]);
In your bootstrap file.
If you want to go with PathLocationStrategy ( without # ) you must setup rewrite strategy for your server.
I've been digging this topic for quite a time , and try a lot of method that don' work .
If you are using angular-cli and with express , I found a solution if the chosen answer doesn't works for you .
Try this node module : express-history-api-fallback
[ Angular ] Change your app.module.ts file , under #NgModule > imports as following :
RouterModule.forRoot(routes), ...
This is so called : " Location Strategy "
You probably were using " Hash Location Strategy " like this :
RouterModule.forRoot(routes, { useHash: true }) , ...
[ Express & Node ]
Basically you have to handle the URL properly , so if you want call "api/datas" etc. that doesn't conflict with the HTML5 Location Strategy .
In your server.js file ( if you used express generator , I've rename it as middleware.js for clarity )
Step 1. - Require the express-history-api-fallback module.
const fallback = require('express-history-api-fallback');
Step 2 . You may have a lot of routes module , sth. like :
app.use('/api/users, userRoutes);
app.use('/api/books, bookRoutes); ......
app.use('/', index); // Where you render your Angular 4 (dist/index.html)
Becareful with the order you are writing , in my case I call the module right under app.use('/',index);
app.use(fallback(__dirname + '/dist/index.html'));
* Make sure it is before where you handle 404 or sth. like that .
This approach works for me : )
I am using EAN stack (Angular4 with cli , Express , Node )

Use dynamic express routes within another Express route

For a school project, I created a plugin system for an Express.js-Application in Node.js. The main page of the application should display a dashboard, where every root page of each plugin should be displayed in a div. Every root page is accessible over the pluginName/-route.
What I would like to do is the following: I wanna include the HTML-string of every home-route in the dashboard. But for this, I need to call the routes inside Node.js (like partials) and for some plugins I even have to provide some properties.
Does someone have an idea, how this could be implemented?
e.g. I have following route:
router.get('/pluginName', function(req, res) {
res.render(__dirname + '/views/home.handlebars', {
layout: __dirname + '/views/layouts/main.handlebars',
markup: markup // Pass rendered react markup
});
});
Now I want to pass the resulting HTML from this route into another route.
So far I had the following ideas:
Simply add the URLs of the plugins to a "data-ajax-url" attribute of the divs and load the stuff via AJAX.
Make an HTTP-Call to every route and append the result on server side (pretty nasty...).
Create a renderDashboard-function for every plugin, where I get the HTML using app.render(...) and then I append the result.
But I'm not really sure, which approach (if any) would be the nicest.

Ember and Express: let Ember handle routes instead of Express?

This might be a dumb question, but I'm serving an Ember app I made using ember-cli on an Express server, but when I try to access various routes, my Express app errors, saying that no route exists (which is true, because I defined the routes in Ember, not Express). How should I resolve this, and is this normal behavior?
My Ember router:
Router.map(function() {
this.route('index', {path: '/' });
this.route('portkey');
this.route('login');
});
My Express routes are just an API that do not serve any of the Ember routes, since localhost:1234 will automatically load index.html.
I've never had a problem using the Ember Router instead of the Express router. All I do is have 1 express route (for '/') which displays my Ember application index.html (well actually index.ejs) page. Not promising this is the right way to do it, but it's how I do it and it works for me.
So start with this.
app.get('/', function(req, res) {
res.render('index', {});
});
That's your express route. Now your ember routing.
App.Router.map( function() {
this.route("about", { path: "/about" });
this.route("favorites", { path: "/favorites" });
});
So as of now you have a routing structure that looks like the following:
yourdomain.com/ --> index.ejs displayed via express routing
/#/ --> this is the ember index route
/#/about --> this is the ember about route
/#/favorites --> this is the ember favorites route
Within the index.ejs file you have the basic ember file linking to your ember application.
Now onto your linking problems...
If you use the ember router, then make sure you are linking to your different routes the correct way. (Remember, ember routes start with /#/someroute).
So your links in handlebars should be something like:
{{#link-to 'some_page'}}Go to some page{{/link}}
NOT
Go to some page
Using the second, express would be trying to handle the routing but by using the first, ember is handling the routing.
So if you really think about it, you can have as many ember applications as your little heart disires because each ember application is linked to that current page in the express routing.
For example on my website, I use two routes (plus a bunch of REST routes obviously): login.ejs and index.ejs.
So for my site, I have the following routes:
mysite.com/
/#/
/#/budget
/#/history
/#/profile
/#/logout
mysite.com/login#/
#/register
#/forget
I hope this helps you a little bit.
EDIT
/#/ is a convention to tell ember you are routing via its router.
Think of it like this: Ember is a single-page framework. So when you link from page to page in ember, you aren't truely changing pages. You are just removing dom elements and replacing them with new ones. But if you go to /budget on the server, you are now going to a whole new page, not just the /#/budget section of the ember application.
I think you are just confusing what the ember router really is.
I had similar issues when trying to directly access any part of my Ember project other than index.html. From there I could easily navigate where I wanted, but it meant that providing someone a link or refreshing the page would fail.
Example: /accounts would fail.
/#/accounts would successfully redirect to /accounts however refreshing still would not work.
Solution:
Router.map(function() {
this.route('accounts');
});
Router.reopen({
location: 'hash'
});
Now all of my links are prefixed with # such as /#/accounts, refreshing and direct-linking works as expected.

server-side changes for $locationProvider.html5Mode(true);

server code:
app.get('/', function(req, res){
console.log('executed "/"')
res.render('home');
});
app.get('/partials/:name', function (req, res) {
console.log('executed partials:name');
var name = req.params.name;
console.log(name);
res.render('partials/' + name);
});
the code works perfectly before i put $locationProvider.html5Mode(true); to convert '/#/' url into regular / url.
After which, the console.log('executed partials:name'); fails to execute. Documentation says:-
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 changes I have tried are not working. What are changes to be made?
Edit: The following is the angular code:
var myApp = angular.module('myApp', []);
myApp.config(['$routeProvider', function($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: 'partials/partial',
controller: 'homePage'
}).
otherwise({redirectTo: '/login'});
$locationProvider.html5Mode(true);
}]);
I again mention, the routing works perfectly well, till i add $locationProvider.html5Mode(true); to angular.
Not quite sure if this matches your problem but when we did this we needed to create a few rules on the server(in Nginx with our setup) to make sure a link to /page didn't just return 404 because it didn't exist on the server.
First we needed to make sure that a link to an Angular route was treated as such. So we do a rewrite ourdomain.com/page to ourdomain.com/#/page. /page doesn't exist on the server, only in Angular, so it would return 404. It needs to go to the index so that Angular can handle the route (this is relevant from incoming links, not so much for when you are on the site since then Angular handles the links anyway).
Secondly we needed to make an exceptions for partials, since those actually actually exists on the server and are not Angular routes. So going to ourdomain.com/partials/partial actually needs to find the partial on the server and can't be rewritten by the above rule.
Hope that helps somewhat at least.
Error is not with routing in this case. myApp.config(['$routeProvider', function($routeProvider, $locationProvider) is defined wrongly.
it should include $locationProvider thus :-
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider,
$locationProvider) {
works perfectly.

Resources