Node/Express with Angular 2 - node.js

I am trying to build the ToDo app with the MEAN stack and I can't get the Express server to connect to Angular 2. I believe it has something to do with where I have my index.html view relative to the Angular installation, but I can't figure it out.
The error I am getting is the HTML on index.html is rendering but not picking up the logic behind the selector so my assumption is my tags are wrong or something. I have tried every which way to adjust the tags, but I can't get it to work when running server.js. I know it is something silly but been working on this for a while.
Server.js
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var index = require('./routes/index');
var todos = require('./routes/todos');
var app = express();
// View Engine
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);
app.use(express.static(path.join(__dirname,'client'))); //folder where angular will be
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/', index);
app.use('/api/v1/', todos);
app.listen(3000, function(){
console.log('Server started on port 3000...');
});
Index.html (in Views folder)
<!DOCTYPE html>
<html>
<head>
<title>Angular QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="src/styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="src/systemjs.config.js"></script>
<script>
System.import('src/main.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading AppComponent FROM SERVER SIDE content here ...</my-app>
</body>
</html>
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
#NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>`,
})
export class AppComponent { name = 'Angular'; }
Below are the two errors I am getting in the console:
GET http://localhost:3000/src/app/app.module 404 (Not Found)
scheduleTask # zone.js:1960 ZoneDelegate.scheduleTask # zone.js:349
(404 Not Found) loading http:…pp.module" from
http://localhost:3000/src/main.js", originalErr:
ZoneAwareError}
Any help would be much appreciated, I can't get past this.
It is not liking something about the reference in this line and getting lost somewhere in zone.js, but I can't get it right. I am using the starter kit from angular.io and using their file layout.
System.import('src/main.js').catch(function(err){ console.error(err); });

I was able to fix by adding two more static routes to the express server so it looked in every folder.
app.use(express.static(path.join(__dirname, 'client'))); // folder where angular will be installed
app.use(express.static(path.join(__dirname, 'client', 'src')));
app.use(express.static(path.join(__dirname, 'client', 'src', 'app')));
This fixed the issue.

I have encountered the same problem with the new version of Quickstart. The fact that it has a different structure (src folder added) affects how express will behave. In my scenario I have NOT altered this portion.
System.import('src/main.js').catch(function(err){ console.error(err); });
Instead I left it as default (I believe angular handles where to look for it).
System.import('main.js').catch(function(err){ console.error(err); });
I have added one more static route. Make sure you have both, one of them will not suffice.
app.use(express.static(path.join(__dirname, 'client')));
app.use(express.static(path.join(__dirname, 'client/src')));

if you are following the TRAVERSY MEDIA: (original Source is EDUONIX)
https://www.youtube.com/watch?v=PFP0oXNNveg&t=2304s
after creating the 'client' folder. Skip the whole JSON Part.
Open Terminal
git clone https://www.github.com/angular/quickstart client
npm install
npm start (this will give you the complete start of the angular front-end)
ctrl + C (close the webserver)
npm run build
server.js
var express = require ('express');
var path = require ('path');
var bodyParser = require ('body-parser');
var index = require ('./routes/index');
var todos = require ('./routes/todos');
var app = express();
//View Engine
app.use(express.static( path.join(__dirname, 'client') ) );
app.use(express.static( path.join(__dirname, 'client/src')));
app.use(express.static( path.join(__dirname, 'client/bower_components')));
app.set('views', path.join(__dirname, 'client/src') );
app.set('view engine', 'ejs');
app.engine ('html', require('ejs').renderFile );
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false} ));
app.use('/', index);
app.use('/api/v1', todos);
app.listen(3000, function() {
console.log ('server started on port 3000');
/routes/index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index.html');
});
module.exports = router;
Last but not the least:
make sure this route would execute:
http://localhost:3000/api/v1/todos
yes? (you did it)

Related

When express and vue js are connected, the default address is accessed

Express and vue.js are linked.
I have set up a route and when I run Express, it will go to an address different from the vue setting.
Vue index router
import Vue from 'vue';
import Router from 'vue-router';
import Editor from '#/components/Editor';
Vue.use(Router);
export default new Router({
mode: 'history',
routes:[
{
path : '/board',
name : 'Editor',
component : Editor
}
]
})
Vue main.js
import Vue from 'vue';
import App from './App.vue';
import {router} from './routes/index.js';
import axios from 'axios';
import ElementUI from 'element-ui';
import { ElementTiptapPlugin } from 'element-tiptap';
import 'element-tiptap/lib/index.css';
Vue.use(ElementUI);
Vue.use(ElementTiptapPlugin, {
// lang: "zh",
// spellcheck: false,
});
Vue.config.productionTip = false;
Vue.prototype.$http = axios;
new Vue({
render: h => h(App),
router
}).$mount('#app');
Express index router
const express = require('express');
const router = express.Router();
const mssql = require('mssql');
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>Index</p>
<a href='/board'> Go to Vue Page </a>
</body>
</html>
app.js
const createError = require('http-errors');
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const ejs = require('ejs');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const apiRouter = require('./routes/api');
const port = 8000; //changed port
app.use(express.static('public'));
app.use(express.static(path.join('node_modules', 'devexpress-richedit', 'dist', 'custom')));
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
// view engine setup
app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'ejs');
app.use(cors());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// app.use(express.static(path.join(__dirname, '/public')));
app.use(express.static(path.join(__dirname, '/')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/api', apiRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`));
my project tree
Express is 8000 port
Connect to'localhost: 8000 /'and Vue's page will appear.
I want express's page to appear when connecting to'localhost:8000/'
And 'localhost:8000/board' doesn't work
I have one more question. How to call express's html file in Vue?
Thanks for your answer
We first need to know how the request flow works in whole express and vue.js.
In your project, you're not running express and vue with same port at the same time.
More clearly, you're using express with 8080 port to host vue.js content.
Express Router Flow
In express, your request match the rule app.get("/", (req, res)= >{res.render("index")})
then express will render the index page for you when you visit the http://localhost:{port}/
And after you add "other" word in your rule app.get("/other", (req, res)= >{res.render("other")})
express will render other page for you when you visit the http://localhost:{port}/other
What if request doesn't match rule?
Express will give you a not found page.
Vue.js Router Flow
When you host the vue output js (we call build.js here) in express server instead of webpack-server.
In your vue project, you setup the vue-router with / and /other-path.
In these paths, vue will render content of / and /other-page.
We assume you use express to host the build.js
And we need the html to include this build.js with following code.
<!-- put this html in project_root/public/{here}-->
<div id="app"></div>
<script src="/build.js"><script>
And we setup the following code to host this html.
app.use("/", express.static(path.resolve(__dirname, "public")))
When we type http://localhost:{port}/, express will give the index.html to browser.
Then you could use website, click link which is <router-link> go to "other-page" which is provide by vue.
BUT, what if you type http://localhost:{port}/other-page in the browser, then push enter button?
Express will give you not found page.
Because express will give you content when you first visit the website.
That means when a request is coming to express, express will give you content by your rule.
If rule doesn't match, express give you not found page.
If rule match, express give you content which you setup.
In the above example, express give you index.html because you setup app.use("/", express.static(path.resolve(__dirname, "public"))).
Then you see the content of index.html in browser.
And then there is a build.js to render the content of vue for you.
But, there is no rule about "other-page" so express give you not found page.
Sum Up
When request is coming ...
express decide the content by your rule.
Not Matched: express give you not found page.
Matched: express give you content by your rule
When request matched, it give you html (at above example).
bowser render the html
html include the build.js
build.js render the content of vue
When you click vue-router link go to other page
There is no real request sent by browser.
It's controlled by your build.js.
When you type url in browser, then push enter bottom.
It goes to step1.
So if your vue.js router and express router have same router path
your express will not give you the vue content when you type url in browser.
Example
We assume your vue output which is called build.js is located in project_root/public/{here}.
There are two routes path "/" and "/vue-content" in your vue-router.
In your express router, you setup like this.
app.use("/", express.static(path.resolve(__dirname, "public")))
app.use("/express-page", (req, res) => {res.render("express-page")})
Then in webpack.config.js
output.publicPath: "/"
output.path: path.resolve(__dirname, "public")
And don't forget the you must have a index.html in project_root/public.
index.html must have script tag including the build.js
Then try to put <a href="/express-page">go to express<a> into your vue content.
Finally, go to http://localhost:8080/, you will see html page which in public.
The build.js render the content for you.
After you click "go to express", browser send the request to express.
Express will render "express-page" for you instead of index.html page.
But, you will find express give you not found page after you type http://localhost:{port}/vue-router in browser and push enter button.
So, how to fix it?
Add the new rule in the end.
app.use("/", express.static(path.resolve(__dirname, "public")))
app.use("/express-page", (req, res) => {res.render("express-page")})
app.use(express.static(path.resolve(__dirname, "public"))) // here
According our sum up step flow, express doesn't have "vue-router" rule.
But, there is a rule which match any request.
app.use(express.static(path.resolve(__dirname, "public")))
So, express will give you index.html
Then build.js will render "vue-router" content for you because vue will get the url to render content by vue-router setting.

Serve pre-made gzip files

I use compression-webpack-plugin to make gzip files durring my bundle process, so when bundling is done I have files like this in my dist folder.
bundle.eefaef91f71795847e74.js
bundle.eefaef91f71795847e74.js.gz
vendor.jduu4f4lj71759dj7e74.js
vendor.jduu4f4lj71759dj7e74.js.gz
stylesLocal.b7ac53d721aca93e4e65099cf74dc90f.css
stylesLocal.b7ac53d721aca93e4e65099cf74dc90f.css.gz
Inside server I use express-static-gzip to serve my gzip files. Why isn't this working. My page doesnt even wanna load? If I put Express.static instead of expressStaticGzip it works normally.
import Express from 'express'
import path from 'path'
import conf from './conf'
import appRenderer from './appRenderer'
import webpackUtils from './webpackUtils'
var expressStaticGzip = require('express-static-gzip')
const APP_PORT: number = conf.APP_PORT
const PORT: any = process.env.PORT || APP_PORT
const app: Express = new Express()
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
app.use(expressStaticGzip(path.join(__dirname, '../', 'dist')))
/* check with the server before using the cached resource */
app.use((req: Object, res: Object, next: () => void): void => {
res.set('Cache-Control', 'no-cache')
return next()
})
/* Use server side rendering for first load */
app.use(appRenderer)
/* Use CommonChunks and long term caching */
app.use(webpackUtils)
// Routes
app.get('*', (req: Object, res: Object) => {
res.render('index', {app: req.body})
})
app.listen(PORT, () => {
console.log(`
Express server is up on port ${PORT}
Production environment
`)
})
And I refernce them in my index.ejs file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>D</title>
<link rel='stylesheet' type='text/css' href="stylesLocal.b7ac53d721aca93e4e65099cf74dc90f.css">
</head>
<body>
<div id="app"><%- app %></div>
<script src="vendor.jduu4f4lj71759dj7e74.js"></script>
<script src="bundle.eefaef91f71795847e74.js"></script>
</body>
</html>
As explained in the readme of express-static-gzip, the syntax is slightly different from express.static.
Instead of
app.use(expressStaticGzip(path.join(__dirname, '../', 'dist')))
try
app.use('/', expressStaticGzip(path.join(__dirname, '../', 'dist')))

Integrate #angular/cli with server-side EJS templates

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}`);
});

Angular2 Router not working correctly

I am trying to define a simple application with login functionality in Angular2 with Typescript. I have defined my Router but am getting an error when attempting to access the url in a browser. This is the error:
Cannot GET /login
And the url I am using:
http://localhost:3012/login
It seems as if router is not correctly routing the URL to correct component and I am not sure why. Here is my home.component.ts which instantiates the app and router:
import {Component} from 'angular2/core';
import {ROUTER_DIRECTIVES, RouteConfig} from 'angular2/router';
import {LoginComponent} from './login/login.component';
import {DashboardComponent} from './dashboard/dashboard.component';
#Component({
selector: 'home',
templateUrl: '<router-outlet></router-outlet>',
directives: [ROUTER_DIRECTIVES]
})
#RouteConfig([
{path: '/login', name: 'Login', component: LoginComponent, useAsDefault: true},
{path: '/dashboard', name: 'Dashboard', component: DashboardComponent},
{path: '/*other', name: 'Other', redirectTo: ['Login']}
])
export class HomeComponent {
}
Both the Login and Dashboard components are defined correctly and PHPStorm has not picked up any errors.
Does anyone have any idea as to why this may be happening?
Here is my server side code. server.ts (NodeJS entry point)
import express = require('express');
import path = require('path');
let port: number = process.env.PORT || 3012;
let app = express();
app.set('views', path.join('./src/Client/views'));
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);
app.use("/node_modules", express.static(path.resolve(__dirname, '../../node_modules')));
app.use("/app", express.static(path.resolve(__dirname, '../Client/app')));
app.use("/*.html", function(req, res) {
res.render(req.params[0] + ".html");
});
app.get('/', function(req: express.Request, res: express.Response) {
res.render('index.html');
});
let server = app.listen(port, function() {
let host = server.address().address;
let port = server.address().port;
});
And my index.html file which includes all required Angular2 scripts and starts SystemJS
<html>
<head>
<title>Test</title>
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
<script src="node_modules/angular2/bundles/http.dev.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/bootstrap')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<home>Loading...</home>
</body>
</html>
And my file structure:
Thanks
I think this:
app.use("/*.html", function(req, res) {
res.render(req.params[0] + ".html");
});
should be
app.use("/*", function(req, res) {
res.render(req.params[0] + ".html");
});
because I am not sure why you would +.html again to a html request.
Does that solve the issue ?
I believe the router needs to have the base href set so it knows what you're routing relative to.
Angular 2 router no base href set

less-middleware not compiling, getting 404

I have a node.js server running with less-middleware. From my understanding, it compiles on the fly and places the css file in the destination/same(if not specified) folder.
My problem is I'm getting a 404 error on the get request for the css file:
Err: GET http://webserver/public/less/blog-reset.css 404 (Not Found)
Here is what I'm working with:
web.js
//requiring dependencies
var express = require("express");
var logfmt = require("logfmt");
var lessMiddleware = require('less-middleware');
var hogan = require('hogan-express');
var path = require('path');
//all environments
var app = module.exports = express();
var port = Number(process.env.PORT || 5000);
app.use(logfmt.requestLogger());
app.use(lessMiddleware(path.join(__dirname,'public')));
app.use(express.static(path.join(__dirname,'public')));
app.set('layout',path.join(__dirname,'src','views','blog-layout'));
app.enable('view cache');
app.engine('.html',hogan);
//page routing called after env loads
require('./src/router');
//listening port
app.listen(port, function() {
console.log("Listening on " + port);
});
blog-layout.html
<head>
<title>EpiBlog</title>
<link href='/public/less/blog-reset.css' rel='stylesheet' type='text/css'/>
</head>
<body>
{{{yield}}}
</body>
directory layout
ROOT
public
less
src
web.js
Versions
less-middleware v0.2.1-beta
express v4.0.0
What I've tried:
using app.use(lessMiddleware)({ src: __dirname + '/public' })); (apparently the old way of doing it)
using app.use(lessMiddleware(path.join(__dirname,'public','less')));
moving app.use(express.static(path.join(__dirname,'public'))); from web.js to router.js
toying with different paths
moving contents of router.js to web.js
specifying the destination using
this:
app.use(lessMiddleware(path.join(__dirname, 'source', 'less'), {
dest: path.join(__dirname, 'public')
}));
the problem was:
<link href='/public/less/blog-reset.css' rel='stylesheet' type='text/css'/>
should have been:
<link href='/less/blog-reset.css' rel='stylesheet' type='text/css'/>
i read that:
link(rel='stylesheet', type='text/css', href='css/styles.css')
was paired with directory structure:
myapp
+-public
+-css
+-styles.less
which led me to believe that this call:
app.use(express.static(path.join(__dirname,'public')));
makes the request assume /public/ is the parent so i was being redundant calling /public/less/blog-reset.css
reference was found here: express.js less compiler: can not get work

Resources