When trying to load localhost:3000/verifyemail in my browser, I receive the error:
Error: Cannot match any routes
I've read through the other SO questions on this, and have verified the following:
Angular2: 2.3.1 uses Path Location Strategy by default, and I haven't changed it. I prefer not to use Hash Location Strategy
I've configured my Nodejs server for HTML5 pushState (server.js is below)
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
// Get API routes
const api = require('./src/server/routes/api.js');
// Set api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
const port = process.env.PORT || '3000';
app.set('port', port);
const server = http.createServer(app);
server.listen(port, () => console.log(`API running on localhost:${port}`));
I have <base href="/"> at the top of my <head> in my index.html
I have default and catch-all routes set
const appRoutes: Routes = [
{ path: 'home',
outlet: 'full-page',
component: DefaultLandingPageComponent
},
{ path: 'verifyemail',
outlet: 'full-page',
component: EmailVerificationComponent
},
{ path: '',
redirectTo: '/home',
pathMatch: 'full'
},
{ path: '**',
outlet: 'full-page',
component: DefaultLandingPageComponent
}
];
I've imported the router module into my app.module.ts, and included a router outlet in my app.component.html file
//in app.module.ts:
#NgModule({
...
imports: [
...
RouterModule.forRoot(appRoutes)
]
...
});
//in app.component.html
import { RouterModule, Routes } from '#angular/router';
Thanks a lot to whoever can give me a hand with this!
Update 1
I've removed all names from routes and the router outlet, thanks to Günter Zöchbauer's observation in the comments below
Update 2
Everything works as expected on localhost:4200/verifyemail, which is run via ng serve.
Problem persists when using localhost:3000/verifyemail, which is run via node server.js
Each route needs a route that adds a component to an unnamed outlet.
Named outlets can only be used in addition to an unnamed outlet, not instead of.
Related
I am working to host multiple (nodejs + react) applications at different ports. As proof of concept I want http://localhost:3000/project1 to load an application running on http://localhost:4000. I created a http-proxy-middleware that runs at http://localhost:3000. the proxy looks like this.
// Dependencies
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
// Config
const { routes } = require('./config.json');
const app = express();
for (const route of routes) {
app.use(route.route,
createProxyMiddleware({
target: route.address,
pathRewrite: {
'^/project1': '/', // remove base path
},
connection:'keep-alive'
})
);
}
app.listen(3000, () => {
console.log('Proxy listening on port 3000');
});
The config.json looks like this:
{
"routes": [
{
"route": "/project1",
"address": "http://localhost:4000"
}
]
}
The application running at port 4000 is production mode and the build folder exists. This is a portion of server.js
//-----
app.use(express.static(path.resolve(__dirname, "build")));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname,"build", "index.html"));
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
The application folder tree looks like this
enter image description here
Now the issue is if I enter http://localhost:4000/ in the browser the application works perfectly fine and the pages load properly. but if I enter http://localhost:3000/project1, I see a blank page with correct title (application name). I see MIME and 404 errors if I inspect the page. see below.
enter image description here
I have tried different things (changing build path).
Sorry for making it a long read and I appreciate your help. Thanks.
I want to add a base URL to my application when I load it on my browser.
My expressJs config is:
App.js
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSanitizer());
app.use(express.static(__dirname + '/webapp'));
app.engine('html',require('ejs').renderFile);
app.set('view engine', 'html');
AngularJs router config:
index.js
$urlRouterProvider
.when('','login')
.when('/','login')
.otherwise(function($injector, $location){
var state= $injector.get('$state');
state.go('error', { url: $location.path() }, { location: true });
});
//define states
$stateProvider
.state('/',{
templateUrl: 'public/login.html',
controller: 'login',
authenticated:false
})
.state('login',{
url: '/login',
templateUrl: 'public/login.html',
authenticated:false
// controller: 'login'
// template: '<h1> Routing to login page </h1>'
})
I want it similar to that when we deploy application in Apache Tomcat.
Currently when I start the server the url is like localhost:8080/login but I want it as localhost:8080/CRM/login
So, I figured out the solution in this way.
I added a base tag in my index.html and added '/CRM' in all my api's in my App.js
index.html
<base href="/CRM/">
App.js
app.use('/CRM/users',appRoutes);
I have checked several tutorials and walkthroughs on how to add SSR (server-side rendering) to existing angular CLI & Express projects.
I was able to run node with SSR but I'm unable to make API calls.
I wish to run one node process that can serve static file (Angular with SSR) and dynamic content with API calls which access SQLITE DB using KNEX.
This is my server.js file
// These are important and needed before anything else
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import { join } from 'path';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 3000;
const DIST_FOLDER = join(process.cwd(), 'bin/dist');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./bin/dist/server/main');
// Express Engine
import { ngExpressEngine } from '#nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '#nguniversal/module-map-ngfactory-loader';
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, ''));
// import * as apiS from './server/index';
// apiS.load(app);
// TODO: implement data requests securely
// app.get('/api/*', (req, res) => {
// res.status(404).send('data requests are not supported');
// });
import * as LoadRoutes from './server/boot/routes';
LoadRoutes.load(app);
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, '')));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});
Is it even possible to accomplish SSR + API calls from the same node process and if so can someone post here answer?
It's already there. Just uncomment these lines and extend the API routes with Express router:
app.get('/api/*', (req, res) => {
res.status(404).send('data requests are not supported');
});
Use the router file instead:
app.use('/api/*', appRoutes);
Every URL starting with api/ will be served by backend APIs.
How do I prevent Angular routing from interferring with routes from an Express Node Server?
I'm setting up some routes ( to node-RED middleware) in my Express server:
server.js
// Serve the editor UI from /red
app.use(settings.httpAdminRoot,RED.httpAdmin);
// Serve the http nodes UI from /api
app.use(settings.httpNodeRoot,RED.httpNode);
// Angular DIST output folder
app.use(express.static(path.join(__dirname, 'dist')));
// Send all other requests to the Angular app
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.js'));
});
but in my router module they get always overridden ( with our without the default path redirection)
routing.module.ts
const appRoutes: Routes = [
{
path: "example-page",
loadChildren: "../modules/example/example.module#ExampleModule?sync=true"
},
// Last routing path specifies default path for application entry
/* {
path: "**",
redirectTo: "/example-page",
pathMatch: "full"
},*/
];
I'm only providing this little code because I want to ask in general how do I prevent Angular from interferring with routes defined in an Express server and what is the best practice for routing in an Express/ Node.js + Angular + AspNetCore + Webpack app.
If you're using Angular, then let Angular handle all pages. This code takes care of that and hence angular is handling all routes.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.js'));
});
If you want express to handle some routes instead of angular, handle that route before handling angular route as follows:
app.get('/some-non-angular-path', (req, res) => {
//code to handle this path. once it is handled here, angular won't be involved
//non-angular-paths should come first in the order
});
app.get((req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.js'));
});
Goal: Modify Open Graph meta tags when web crawlers visit different routes.
I know Angular2 4.0.0 has a MetaService, and there is always jQuery, but web crawlers except Googlebot don't execute Javascript, so it is kind of useless to my purpose. While Angular Universal sounds like an overkill for changing a couple meta tags.
So far my solution is to copy and paste the compiled index.html in /dist to index.ejs, and modify the necessary fields. Is it possible to integrate the workflow with the angular-cli compilation process, by having the entry point changed from index.html to index.ejs? If not, what are the alternatives that I should explore?
In my index.ejs :
<meta property="og:url" content="<%= url %>" />
In my Express route index.js :
res.render('index', {
url: site_url,
});
In my server.js:
app.set('views', path.join(__dirname, '/dist'));
app.set('view engine', 'ejs');
Please refrain your answer to the current #angular/cli version (v1.0.1 compatible).
Some related discussions:
Add customization options for HtmlWebpackPlugin #3338
Conditional template logic in index.html for beta.11-webpack #1544
Provide config to rename index.html in dist folder #2241
I was able to solve this by using Nunjucks to render templates served via Angular Universal. I'm sure it's possible to use other engines such as EJS as well. Here are the relevant portions of my server.ts:
import * as dotenv from 'dotenv';
dotenv.config();
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import * as nunjucks from 'nunjucks';
import { renderModuleFactory } from '#angular/platform-server';
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 4201;
const DIST_FOLDER = join(process.cwd(), 'dist');
// Our index.html we'll use as our template
const template = readFileSync(join(DIST_FOLDER, 'index.html')).toString();
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist-server/main.bundle');
const { provideModuleMap } = require('#nguniversal/module-map-ngfactory-loader');
nunjucks.configure(DIST_FOLDER, {
autoescape: true,
express: app,
});
app.engine('html', (_, options, callback) => {
renderModuleFactory(AppServerModuleNgFactory, {
// Our index.html
document: template,
url: options.req.url,
// DI so that we can get lazy-loading to work differently (since we need it to just instantly render it)
extraProviders: [
provideModuleMap(LAZY_MODULE_MAP)
]
}).then(html => {
callback(null, html);
});
});
app.set('view engine', 'html');
// Server static files from dist folder
app.get('*.*', express.static(DIST_FOLDER));
// All regular routes use the Universal engine
// You can pass in server-side values here to have Nunjucks render them
app.get('*', (req, res) => {
res.render('index', { req, yourVar: 'some-value' });
});
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});