Serving HTML to build a simple SPA using Node.js/Express - node.js

I want to build a website as a Single Page Application.
For what I understand a SPA sends a single entry point to the application in the form of an HTML file.
So I would like to use Node and Express to serve the main page and to then serve HTML for the content between the header and footer that gets updated using AJAX calls when a user navigates the site.
I know how to create a restful API to serve data as JSON but not much on how to deal with the HTML parts of the SPA that are changing.
The question: how could this serving of HTML parts be implemented on the server using Node and Express (and eventually a template engine like handlebars, if it helps)?
And does it actually make sense? (A header and a footer are not much data to reload after all.)

You can definitely do this with node.js. First you set up an HTML template engine, for instance Swig, (but you can use others) and you configure the standard options to render html pages:
var swig = require('swig');
app.set('view engine', 'html');
app.set('view options', {
layout: false
});
app.engine('html', swig.renderFile);
app.set('view cache', false);
// To disable Swig's cache, do the following:
swig.setDefaults({ cache: false });
app.use(express.static(__dirname + '/public'));
Second you set up an endpoint which serves your main HTML page.
app.get('/',function(req, res) {
res.render('index');
};
Then you can create node.js endpoints which will get your data:
app.get('/users', function(req, res) {
// Do database stuff here
res.status(200).send(results);
}
Then you set up your HTML file:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="stylesheets/bootstrap.min.css" rel="stylesheet">
<title>fast MEAN</title>
</head>
<body ng-controller="MainController">
<div class="col-md-12" style="background-color: #006699">
</div>
{% raw %}
{{helloWorld}}
<ng-view></ng-view>
<script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script>
<script src="app.js"></script>
{% endraw %}
</body>
</html>
Where everything outside of {% raw %} and {% endraw %} will be rendered with data using the node/swig template data, and everything inside those tags can be rendered with whatever framework you are using inside (jquery, angular, react) etc.
An example of this simple single page application is on my github, there's one version for angular and one for react. Hope that helps.
https://github.com/coguy450/fastMEAN

Related

Problems running imported JS in EJS file

I made an express Nodejs server and set the static folder.
The CSS has access to the page, but the JS does not find any elements. Everything is NULL.
app.use(express.static(__dirname + '/static'))
app.get('/', (req, res) => {
res.render('index')
})
//test from clientside index.js
document.body.append('test')
<script src="js/index.js"></script>
<link rel="stylesheet" href="style/main.css">
What am I doing wrong?
Problem
DOM doesn't load yet when your JS runs. It's not related to EJS, that's just how every script works.
Solution
Add defer attribute
<script defer src="js/index.js"></script>

Azure app uploads from VSCode without linking static sources

I have an incredibly basic node.js server test app running on port 3000 in VSCode. The server appears to work via node on localhost or deployed to Azure, and hosts an index.html file. I have clients happy to talk to it.
/* Server.js */
const express = require('express');
const app = express();
app.use(express.json());
app.get("/", function(request, response){
response.sendFile(__dirname + "/index.html");
});
app.post("/", function(request, result){
console.log(request.body["itema"] + " " + request.body["itemb"]);
result.send("Pong!!");
});
app.listen(3000, function(){
console.log("Server started on port 3000");
});
<!-- Index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Simple App</title>
<link rel="stylesheet" href="style.css" type="text/css"/>
</head>
<body>
<h1>Test!</h1>
</body>
</html>
/*style.css*/
h1 {
color: tomato;
}
Azure Configuration Settings
The index.html links to a css file which doesn't appear to be linked when deployed. I've tried deploying the app to azurewebsites.net through VSCode, however I'm seeing the same results. Only index.html is present in the sources view. I get an error claiming it can't render the source it didn't find.
"Refused to apply style from https://<appname>.azurewebsites.net/style.css because its MIME
type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is
enabled."
If I view the advanced page (.scm.azurewebsites.net), again I can see only index.html.
Index only
But .scm.azurewebsites.net/wwwroot/ shows me the css file has been uploaded correctly.
Index and linked sources
I'm convinced it'll be a simple solution to someone and that the PATH variables will need to be set...somehow... but I've hit a brick wall and my Google-Fu has finally been exhausted.
Thanks!
Adding the following to server.js resolved the issue.
app.use(express.static("./"));
This is further explained here and is required in express to serve static files.
https://expressjs.com/en/starter/static-files.html

Node.js web app uploaded through A2 Hosting - pathing issue with CSS

I am currently studying node.js and pretty new still so I apologize in advance for probably having an inefficient or terribly formatted first question. I am uploading a one-page test to figure out how to use A2's node.js service.
So far I have followed the A2 tutorial "How to create a Node.js application with cPanel using Node.js Selector" (https://www.a2hosting.com/kb/cpanel/cpanel-software/create-application-with-nodejs-selector) and this tutorial on syncing git repositories (https://medium.com/#pampas93/host-your-node-js-app-on-shared-hosting-go-beyond-localhost-73ab923e6691) and I have been able to get everything working except the main page
(Located in dirname/repositories/test/views/home-visitor.ejs)
will not read the CSS file I have uploaded
(Located in: dirname/repositories/test/public/main.css)
and some of the images do not load. No file name typos or forward slash "/" syntax inconsistencies, all images share the same folder path.
(Located in: dirname/repositories/test/public/images)
So the node.js app shows up as plain HTML only. I have uploaded the exact same app/git repository to Heroku and the CSS is read and all images show up, so I am trying to figure out what pathing issues I am having when uploading specifically to A2's hosting service.
When I check 'inspect' on the browser for the non-working app page, I get these console messages:
"Refused to apply style from 'http://domainname.com/main.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled."
Failed to load resource: the server responded with a staus of 404 () nonWorkingImageExample.png:1
Failed to load resource: the server responded with a staus of 404 () anotherNonWorkingImageExample.png:1
http://domainname.com/test/main.css sends to a 404 page
app.js:
const express = require("express")
const app = express()
let port = process.env.PORT
if (port == null || port == "") {
port = 3000
}
app.use(express.static("/public/"))
app.set("views", "views")
app.set("view engine", "ejs")
app.get("/test", function (req, res) {
res.render("home-visitor")
})
app.listen(3000)
The page I am testing: home-visitor.ejs:
<!DOCTYPE html>
<html><head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="main.css" type="text/css">
</head>
<header>
<div class="forMobile mobileHeader">
<div class="mobileLogo">
<a href="#"><img src="images/workingImageExample.png" alt="Image Description">
</a>
</div>
</div>
</header>
<div class="forDesktop centerContent tempMssg">"Lorem flexitarian salvia offal umami. Sartorial swag drinking portland cray. Godard jianbing retro thundercats hella tilde. "
<br><br>
<a href="#" target="_blank" rel="noopener noreferrer">
<img src="images/nonWorkingImageExample.png" alt="Twitter Logo Link" width="35px" height="35">
</a>
</div>
<div class="forMobile mobileUpperBG">
<img src="images/anotherNonWorkingImageExample.png" alt="upper left bg image">
</div>
</html>
I am hoping someone familiar with hosting node.js apps on A2 can help, and thank you for the time reading this question regardless.
Full credit for this solution goes to VictoryFlame's video
https://www.youtube.com/watch?v=BAD5JyOymRg.
I ended up editing the way app.js pathed my public folder and updated how I linked the CSS and image files in my .ejs file:
app.js:
const express = require("express")
const app = express()
const path = require("path")
app.set("views", path.join(__dirname, "views"))
app.set("view engine", "ejs")
app.use("/test1/static", express.static("public/"))
app.get("/test1", function (req, res) {
res.render("home-visitor")
})
app.listen()
I re-formatted the style link to the CSS page and used this pathing syntax for images:
home-visitor.ejs:
<link rel="stylesheet" href="https://domainname.com/test1/static/home.css">
<a href="#" target="_blank" rel="noopener noreferrer">
<img src="/test1/static/images/imageWorksNow.png" alt="Twitter Logo Link" width="35px" height="35"></a>
Now everything is properly served/linked and showing up. I hope this helps someone else some day!

Angularjs routing ejs static files in "views" folder from "public" folder of express.js

Here is my project structure.
I am trying to call the ejs file (app > views > list.ejs) which is in "views" folder from the angular routing file (app > public > main.js) which is in "public" folder. But the list.ejs is not loading.
/************main.js****************/
var fsApp=angular.module('chart-app');
fsApp.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'list.ejs',
controller: 'listCtrl'
})
/*In my server.js server file I am calling the files in public folder this way: */
app.use('/static',express.static(__dirname + '/public'));
app.get('/',function(req,res){
res.render('index.ejs');
});
<!--In index.ejs (where my <ng-view></ng-view> is) I am including the link for static files:-->
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>
<script src="/static/angular-route.js"></script>
<script src="/static/main.js"></script>
</head>
<body ng-app="chart-app" class="container-fluid">
<div ng-controller="fairshareCtrl">
<div class="row" ng-view></div>
</div>
</body>
Is there anything more I have to do on the server file? What am I missing?
I don't know what are you using at the backend but, when you render the ejs file from express you have to define the engine for the same.
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
then you can use
app.get('*',function(req,res){
res.render('index.ejs');
});
and keep all the angular side in a public folder.
OR You can just load html file rather then ejs via ejs
app.engine('html', require('ejs').renderFile);
app.get('*', (req, res) => {
res.render('index.html');
})
public folder can not access the files that outside that folder, i.e. other folders like node_modules, views routes etc. That is why you are not able to load list.ejs from your angular main.js or angular-route.js file.
Views are used by express to render the view template(ejs) based on the request sent to the backend. They cant be used for frontend routing. They can only be accessed by the exprss app, but not the public folder.
For frontend routing, you will need to store all the templates in your public folder only.
I will suggest that you create a templates folder in your public folder, and use that address in your angular routing.
public
--templates
--list.html
--angular-route.js
--main.css
--main.js
use it like this in your angular routing.
fsApp.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'templates/list.html',
controller: 'listCtrl'
})
I think you might need to include ejs.js in your view file, to support ejs templating in frontend. Read ejs getting started doc for more information on how to do it.
I hope this helps you.
I just had to use app.set('views', path.join(__dirname, '/public')); to make ejs to lookup in publicdirectory rather than in views directory

How to add static file from `node_modules` after declare the `public` as static in express?

In my express app, I am keeping the below config:
var express = require("express");
var path = require("path");
var jsonServer = require("json-server");
var app = express( );
app.use(express.static('public'));
app.get("/", function( req, res ) {
res.sendFile( express.static( path.join(__dirname, "/public/index.html" ) ) );
});
app.use('/api', jsonServer.router('./db.json'));
app.listen( 3000 );
All this is works fine. the problem is, I am not able to add the jquery from node_modules. here is my HTML :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Promise Defined</title>
<script src="node_modules/jquery/dist/jquery.js" type="text/javascript"></script> //not working sibling folder of public
<link rel="stylesheet" href="styles/app.css"> //works fine. inside public folder
</head>
<body>
<h1>New Title</h1>
</body>
</html>
What is the correct way to fix this? or how to handle this kind of scenario?
Thanks in advance.
node.js does not serve any files by default (unlike some other web servers). So, if you want node_modules/jquery/dist/jquery.js to work, then you have to either make sure that your express.static() line of code will include that file or you have to define a route specifically for that file. Also, it is sometimes dangerous to use a relative path with no leading / because that makes it relative to the path of your page which is generally not necessary and can make things difficult if you want to serve the same file in lots of different pages on your site.
For example, you could do this in the web page:
<script src="/jquery.js" type="text/javascript"></script>
And, this:
app.get("/jquery.js", function(req, res) {
res.sendFile(path.join(__dirname, "node_modules/jquery/dist/jquery.js"));
});
Personally, I would probably move jquery.js to a public/scripts directory on my server so I'm not serving anything out of the node_modules directory, so all my publicly served files are centralized in one place so it's entirely clear what can be served to a client and what cannot and so I could design one or two express.static() statements to serve all my public files.

Resources