Why Angular 2 doesn't load default app-root component? - node.js

I'm trying to start Angular app generated via angular CLI, but it seems like the default app-root component doesn't load. Need to say, that I'm using proxy for connection between angular app and express server, and I'm running two scripts at the same time: node bin/www for express/node.js start and ng serve --proxy-config proxy.config.json for starting Angular and creating proxy connection, it looks like this (the part of package.json):
"scripts": {
"start": "concurrently --kill-others \"node bin/www\" \"ng serve --proxy-config proxy.config.json\""
}
The index page loads fine, but it seems that app-root component (the default component, which was created from angular CLI ng new) doesn't loading:
Here is my node.js/express uses and a route:
var express = require('express');
var router = express.Router();
var app = express();
var path = require('path');
app.use(express.static('./src/client/'));
app.use(express.static('./'));
app.use(express.static('./tmp'));
app.use('/*', express.static(path.resolve('src/client/index.html')));
router.get('*', function(req, res) {
res.sendFile(path.resolve('src/client/index.html'));
});
module.exports = router;
And the structure of my project (if needed):
What did I miss? Why the default app-root component doesn't loading? (need to say, when I run ng serve, it starts the angular homepage as needed and the component is OK, so I think the problem is somewhere in express).
Thanks in advance

You should serve the contents of the dist/ folder after calling ng build --prod (the --prod is important, as the default is --dev). So, it would be something like this:
"scripts": {
"start": "ng build --prod && node bin/www"
}
And, more or less adapting your express script:
app.use(express.static('./dist'));
app.use('/*', express.static(path.resolve('dist/index.html')));
router.get('*', function(req, res) {
res.sendFile(path.resolve('dist/index.html'));
});

Related

What is the best way to get a Vue app to correctly read the env variables that have been set on Heroku?

I have a vue app that is being hosted on Heroku. Now, because heroku doesn't do static apps, I wrote a simple express server to serve this app
//server.js
const express = require('express')
const serveStatic = require('serve-static')
const path = require('path')
app = express()
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
req.headers['x-forwarded-proto'] !== 'https'
? res.redirect(301, 'https://' + req.hostname + req.originalUrl)
: next()
} else {
next()
}
})
app.use(serveStatic(path.join(__dirname, 'dist')))
const port = process.env.PORT || 80
app.listen(port)
And hooked the build process of the app to Heroku existing workflow through package.json
.
.
.
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"postinstall": "if test \"$NODE_ENV\" = \"production\" ; then yarn run build ; fi",
"start": "node server.js"
},
.
.
.
This whole thing works and technically so do the environment variables. However, I see that some env variables that I set on heroku (in their dashboards) yesterday are still undefined when I access them in vue using process.env.VUE_APP_XXX.
In development I use .env and that works as intended. My idea here is that because there's the express server in the middle things don't work as I would have expected.
Is there any way to make this thing work smoothly or am I better off using a static site hosting a-la Netflify?
Using ENV variables in Vue CLI (or Webpack build app in general) is done using Webpack DefinePlugin which replaces the values like process.env.VUE_APP_XXX in your code at build time
So if you define (or change) your variables directly in Heroku dashboard instead of .env file committed to repo, you need to rebuild you app on Heroku to pick up the changes...
If you are using ENV to configure any secrets (API keys etc), don't !! These values will be included directly in JS bundle of your app and easily accessible to anyone...
You can use heroku cli to set your env variables - documentation
$ heroku config:set DATABASE_URI=database_uri_here
$ heroku config:set SESSION_SECRET=session_secret
... and so on for each variable,
OR
if you have the env variables set in your .env file then you can do this as well
heroku config:set $(cat .env | sed '/^$/d; /#[[:print:]]*$/d')

How to deploy Angular Universal app to Node.js production server?

I have an Angular 8 application with Universal that I want to deploy to a shared web host production server. I checked with the web host in advance and they told me that hosting an angular universal web app is possible on their shared web hosting. However, whatever I do, I can't get the website to work. When I go to the website I keep seeing the message: "This site can't be reached"
Things I have done so far:
Build project with npm run build:ssr which created a dist folder with a browser and server folder and a server.js file
Move the dist folder to the server inside the public_html folder.
Then accessed the server through SSH and did these:
install Node.js and npm
npm install
npm install pm2 -g
pm2 start dist/server.js
pm2 starts without problems.
These are some of the project files. If any are missing please ask and I'll add them to the question.
Part of the package.json with the scripts:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"compile:server": "webpack --config webpack.server.config.js --progress --colors",
"serve:ssr": "node dist/server",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"build:client-and-server-bundles": "ng build --prod && ng run ProjectName:server:production --bundleDependencies all"
},
server.js from the build (only the express part, since it's 25000+ lines):
const app = express__WEBPACK_IMPORTED_MODULE_1__();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(process.cwd(), 'dist/browser');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = __webpack_require__(144);
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express__WEBPACK_IMPORTED_MODULE_1__["static"](DIST_FOLDER, {
maxAge: '1y'
}));
// 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 Express server listening on http://localhost:${PORT}`);
});
According to several answers on SO or elsewhere you "simply" copy paste the dist folder to the server, run pm2 and your website supposedly works. I feel like there's a lot missing to get it working though.
Does someone know how to properly deploy an Angular Universal website to a production server?
I ended up using a tool in my DirectAdmin console called 'NodeJS Selector', because PM2 kept causing problems for me. It's a tool to install your application, do npm install and start the app. So basically what you'd do using SSH, but in a fancy UI. The setup looks like this:
With my folder structure looking like this:
domains
- appname.com
- public_html
- browser (=> this set as the document root in Apache, because index is located here)
- index.html
- .htaccess
- other files...
- server
- server.js
- package.json
I had my web host do 2 things, because I'm not a root user on the server. They set the documentroot to the browser folder in Apache's httpd.conf file. In the same file they also added proxy settings to port 4000 because that's where my app is running on. So in the httpd.conf file will be something like this:
DocumentRoot "/domains/appname.com/public_html/browser"
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / https://localhost:4000/
ProxyPassReverse / https://localhost:4000/
Next in the NodeJS selector you set the root to where the startup file is located (in my case server.js). The package.json needs to be in de public_html folder as well so you can do npm install in the Selector.
To start the app you click 'Run JS script' and choose the serve:ssr option and it runs the node server command.
The Angular docs describe how you have to rewrite some rules on the server. Apache is installed on my web server so I added an .htaccess file with the rewrite rules from the docs. However this app is served by Node and Node can serve the actual paths. So in my case I didn't have to add the .htaccess file with rewrite rules. On the contrary, when I added the .htaccess file it caused the Universal side of my app not to fully render. See this question for more info.
In server.ts file change
const DIST_FOLDER = join(process.cwd(), 'dist/browser');
to
const DIST_FOLDER = join(process.cwd(), 'browser');
Now build the application by npm run build:ssr and copy everything inside of dist folder to your public folder (on host). Remember, instead of pm2 start dist/server.js, you should run pm2 start server.js.

Node.JS doesn't refresh the Nunjuck template

I'm using Node.JS and Nunjuck to make my templates.
When I change something on my .njk files, I have to stop the Node.JS server and start it back again so that the changes reflect on my browser (localhost:80).
Here is how my app.js (server) looks like:
var app = require("express")(),
nunjucks = require('nunjucks'),
server = require("http").createServer(app),
io = require("socket.io").listen(server)
path = require("path");
nunjucks.configure('views',
{
autoescape: true,
express: app
});
app.get("/", function (req, res)
{
res.render(__dirname + "/views/index.njk");
});
server.listen(80);
And in my index.njk, I have two lines:
{% set x = 5 %} and {{ x }}.
For example, when I change the value of 5 to 7, I have to restart my node.JS server and then refresh the page in order to get a 7. If I just refresh the page without restarting the server, I still get a 5.
Are you using nodemon?
If not do this:
npm i -D nodemon
than on your package.json do the following:
"scripts": {
"start": "nodemon LOCATION/OF/index.js"
}
now you have to run npm rum start to build you project.
On "LOCATION/OF/index.js" should be something like: nodemon src/index.js
Nodemon https://www.npmjs.com/package/nodemon
The problem is that nodemon doesn't watch for changes in .njk files.
You can use nodemon
By default, watching extensions for changes: js,json,md
Here's how to add .njk
Then run these commands:
npm install nodemon -g
nodemon app.js

Deploy Angular 4 App on windows

I am trying to deploy my angular application on windows server for that i am using the express server .
I have configured my server.js file like this:
var express = require('express');
var app = express();
const path = require('path');
app.use(express.static(__dirname + '/dist'));
app.listen(process.env.PORT || 4200);
app.get('*/', function(req,res){
res.sendFile(path.join(__dirname + '/dist/index.html'));
})
console.log('App is listing on ' + 4200);
Also, i have deployed the content that was produced using the ng build command inside dist.
I can successfully able to access the application when i enter http://localhost:4200 in my browser.
But, the problem what i am facing here is the proxy server is not getting created which was used to be created when i was using :
"start": "ng serve --proxy-config local-proxy.config.json",
But, now since i am not calling ng serve instead of this i am calling node server.js which is serving my application on server.
"start": "node server.js --proxy-config local-proxy.config.json",
Can someone can help me please.

AngularJs route reaload fails in nodeJs

I managed to get an example app working following several examples, but theres a problem i cant seem to solve. In the angular app routes work ok like this:
- http://localhost:8888/#/login works
but when i reload the url i get the Cannot GET /login error from nodeJs.
i tried doing this:
app.get('/*', function (req, response) {
response.write(__dirname + '/../Client/index.html');
response.end();
});
and this
app.get('/login', function (req, response) {
console.log('*****************************************');
response.sendfile(path.resolve(__dirname + '/../Client/'));
});
but none work, i always get the same error and in the first one it just writes the url.
I think the problem is that while in the app angular knows the routing and handle the reloads because the file is served with
app.use('/', express.static(__dirname + '/../Client/'));
but when the url doesnt have the angular hashtag nodejs tries to look for the url but fails. is there a way i fix this? maybe redirecting to the correct url or something, i searched for hours trying stuff but none worked, thanks for taking your time.
The error is generated from the fact that Angular will rewrite the window.location at runtime while you are using your application starting from the root localhost/ and routing you to localhost/login (without calling the server with a GET).
So, when you refresh, the browser didn't know that the url was written by javascript, so he calls the node server that reject the request.
I other word: you need to start your navigation always from the root.. but this is unusable.
So I figure out how to develop a single project with Angular (4 in my case) for the front-end and nodejs-express for the back-and.
I've tested this solution for redirect with fragment from different source like auth0 and the use of JSON Web Token.
This let me to deploy this application easily to a single heroku instance.
NodeJs Configuration
The first middlewere have to be the path where the Angular's app is build
app.use(express.static(__dirname + './dist'));
Then I have configured express-jwt (the regular expression is for all the url that not starts with "/api")
app.use(expressJwt({secret: appConfig.SecurityJwt}).unless({
path: [
'/api/v1/login',
/^(?!\/api).*/gm
]
}));
Then the business logic of front end:
app.post('/api/v1/login', ...
app.get('/api/v1/myOtherServices' ....
And in the very end of routing configuration, even after the custom error handler:
app.use(appHandleError);
app.get('*', function (req, res) {
res.sendFile('index.html', {root: './dist'});
});
app.listen(4200);
Angular routing
I did not do anything particular here:
const routes: Routes = [
{path: 'home', component: LoginComponent},
{path: 'authenticated', component: ActivatePageComponent},
{path: '', redirectTo: '/home', pathMatch: 'full'}
];
#NgModule({
imports: [RouterModule.forRoot(routes, {useHash: false, enableTracing: false})],
exports: [RouterModule]
})
export class AppRoutingModule {
}
Then in the package.json I have made a custom script:
"scripts": {
"ng": "ng",
"start": "node server.js",
"build": "ng b --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"postinstall": "ng b --prod",
"buildAndStart": "ng build && npm start",
"i18n": "ng-xi18n --i18nFormat=xlf"
},
That I can launch with the command npm run buildAndStart so the angular project is builded and the server node is up and running.
My angular app will be listening to http://localhost:4200/ and all the rest service at the /api/* path as well.
So, for example, url like:
http://localhost:4200/authenticated#access_token=123
http://localhost:4200/authenticated?access_token=123
will be evaluated by Angular, and Angular can call all the /api/v1/myOtherServices without any problem, even the refresh/reload will not generate any error this time.

Resources