Nodejs/Express noob update page view no reload - node.js

I am using Node v.0.10.29 on Win XP SP3.
I am new to Node/Express.
I am trying to learn Node/Express by duplicating a existing PHP project.
http://stevenjsteele.com/database/
The PHP project allows the user to build a list of items
from either the materials, tools or equipment tables needed for a project,
without a page reload.
What I am having a hard time with (don't understand) is:
With the PHP project I can change tables using the select table
drop down without a page reload. This is done with a PHP echo and:
xmlHttp.open("POST","getpage.php?tablename="+str,true);
I initialize the app with:
var express = require('express'),
request = require('request'),
requirejs = require('requirejs'),
mysql = require('mysql'), // node-mysql module
path = require('path'),
bodyParser = require('body-parser'),
app = express();
var mh_connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password: ''
});
mh_connection.connect();
var materialhandler = router.route('/materialhandler');
materialhandler.get(function(req,res){
selecttools = 'tools';
selectelectrical = 'electrical';
selectequipment = 'equipment';
mh_connection.query('use materialhandler');
var strQuery = 'SELECT * FROM materialhandler.tools ORDER BY item_id';
mh_connection.query( strQuery, function(err, rows){
if(err) {
throw err;
}else{
tablename='Tools';
res.render('materialhandler',{title:"page title",data:rows});
res.end("");
}
});
});
In my node/express template (which is html) I use:
For the select option dropdown inside of a form,
I use: onchange="getData()"
<form id="theForm" action="/materialhandler">
<div class="tablepicker two-thirds column">
<select id="selectTable" onchange="getData()" class="selectpicker" name="selectpicker">
<optgroup>
<option name="" value="">Select Table</option>
<option name="tools" value="tools"><%=selecttools%></option>
<option name="electrical" value="electrical"><%=selectelectrical</option>
<option name="equipment" value="equipment"><%=selectequipment%</option>
</optgroup>
</select>
</div>
</form>
function getData() {
//console.log('begin');
var http = new XMLHttpRequest();
var selectedTable = selectTable.options[selectTable.selectedIndex].value;
http.open("POST", 'http://localhost:3000/materialhandler?selectpicker='+selectedTable, true);
http.setRequestHeader("Content-type", "application/x-www-form urlencoded");
http.onreadystatechange = function() {
console.log('onreadystatechange');
if (http.readyState == 4 && http.status == 200) {
//alert(http.responseText);
}
else {
console.log('readyState=' + http.readyState + ', status: ' + http.status);
}
}
//console.log('sending...')
http.send(selectedTable);
console.log(selectedTable)
//console.log('end');
$('#theForm').submit();
}
In terminal window console.log shows the selected table.
I am trying to change the tables without a page reload.
As I said at beginning I am just starting to learn this paradigm.
Any help or pointing in the right direction is appreciated.
Thanks.

The $('#theForm').submit(); is what's causing your page reload, but instead of doing it that way you can make your post request directly with javascript. jQuery would be a better alternative to XMLHttpRequest.
Instead of your getData() function, you can hook up some javascript to listen for select changes, and post them to your server like so:
$('#selectTable').on('change', function() {
$.post("materialhandler?selectpicker=" + $(this).val());
}

Related

A way to persist the same/unique id generated for crypto.randomUUID in a user on nodeJS?

I'm developing a web app where I need to give the user a unique ID for him, I searched and used the crypto module to generate UUID and it works, here is the code for that (I'm sending it with my res.render of products).
import Product from '../models/Products';
const crypto = require('crypto');
const getProducts = async (req, res) => {
const products = await Product.find().lean();
const idValueUser = crypto.randomUUID();
console.log(idValueUser);
res.render('products/products', { layout: 'products-lyt',
products: products,
userId: idValueUser});
};
module.exports = getProducts;
I'm passing to handlebars file the userId by the render and in the javascript of the file I pass that Id to the local storage:
<div class="container-fluid products-container">
<div class="products-columns">
{{#each products}}
{{> products-partial }}
{{/each}}
</div>
<p id="awadetest">{{userId}}</p>
</div>
<script>
var testeo = document.getElementById("awadetest").innerHTML;
console.log(testeo);
localStorage.setItem("test", testeo);
</script>
The Id pass correctly to the localStorage (which I'll send to the database by a form after) but the problem is that every time the user reload (make a petition to the page of products) the id change (logically) but I need a way to make that Id persist so I can identify the user after on the Database, someone knows a way to do that? or if exist a better way to identify the user that doesn't imply log in or use the IP, btw thanks
If is helpful for someone I let the solution what works for me (easier that i thought):
Basically is to made a validation on the local storage (or cookie, if you're using it) like this:
window.onload = function () {
var testeo = document.getElementById("awadetest").innerHTML;
if (localStorage.getItem("userId") == null){
localStorage.setItem("userId", testeo);
}
else{
return;
}
}
With this easily the code can identify if the browser already have a userId (I tested on different browsers and it works, generate different codes for every browser). And if someone is on the same "problem" that me (identify users after in a shopping cart) you can add to your "form" that is sent to DB the userId taking it from the local storage, hope it helps someone

Is it possible to share some code between several pages in Node.js like with PHP without using pug or ejs?

I want to include a header, a menu and a footer in my web app for most of the pages but I don't want to write the same code in every pages. I know this is possible with PHP, but I'm using Node.js and I can't use jade/pug or EJS. How can I do that ?
To do this, you would create your files that contain your content like the following two file here:
header.js
module.exports = `<nav>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</nav>`
footer.js
module.exports = `<footer>
<!-- Your footer code -->
</footer>`
Next within your app, you would listen for a request and then just require the files, and concatenate the data to generate a new string header + body + footer, then you would write that out to the response. I am not sure how you will be getting your body, but here is an example:
main.js
const http = require('http');
const header = require('./header.js');
const footer = require('./footer.js');
const server = http.createServer((req, res) => {
// Get the body somehow
// We will just use a switch here and test `req.url`
let bodyFile = './home.js';
switch(req.url) {
case '/about': bodyFile = './about.js'; break;
case '/contact': bodyFile = './contact.js'; break;
}
const body = require(bodyFile);
res.write(header + body + footer);
res.end();
});
server.listen(8000);

Setting/Accessing Nunjucks global variables within a NodeJS/KeystoneJS project

I've spent all of this morning searching for how to do this, and have come up stumped.
The project I'm working on is built on KeystoneJS/NodeJS. It's using Nunjucks which I've only got a few days basic experience of.
My issue is that after loading the config vars that sets the URI's/Ports of the services, I then want to set these up as Nunjucks variables, so within the html views, I can use those as the src locations.
I can't share all the code here, as I'm working on a government (UK) project but here's enough I hope.
Keystone.js
// Require keystone
var keystone = require('keystone');
var cons = require('consolidate');
var nunjucks = require('nunjucks');
var env = new nunjucks.Environment(null);
env.addGlobal('provision_uri', 3);
This loads initially, after routing it calls:
Login.js
var keystone = require('keystone');
var nunjucks = require('nunjucks');
exports = module.exports = function (req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// locals.section is used to set the currently selected
// item in the header navigation.
locals.section = 'home';
var env = new nunjucks.Environment(null);
var provision_uri = env.getGlobal('provision_uri',3);
console.log(`Uri ${provision_uri}`); **<-- ERRORS HERE**
// Render the view
view.render('login', {
title: 'User Login',
description: 'Login to your Account',
provision_uri: provision_uri
});
};
Login.html
<br>
<form action="{{provision_uri}}/session" method="post" name="user">
<div class="container">
When I then start the project, the landing page loads, click on the login page and within console I get:
GET /provision/ 304 74.147 ms
Error thrown for request: /login
Error: global not found: provision_uri
I've checked this Question however it doesn't answer what I need but I looked up the environment.addGlobal given as an answer. That did seem to be what I wanted, but still it wouldn't work. I found this question which provided hope.
Any ideas would be great, I do have a work-around but would like to learn how to use these.
Thanks,
Colin
You don't need to create new instance of nunjucks environment on each render call. The new scope (environment) has empty global space.
var nunjucks = require('nunjucks');
var env = new nunjucks.Environment(null);
env.addGlobal('provision_uri', 3);
...
exports = module.exports = function (req, res) {
...
var provision_uri = env.getGlobal('provision_uri',3);
console.log(provision_uri);
view.render('login', {
title: 'User Login',
description: 'Login to your Account',
// provision_uri: provision_uri // it's not necessary
});
}

Using Nodejs (fs) to access files selected with input type=file

I am having trouble reading files in different locations (other than the Node project directory). I need to read a private key file (could be located anywhere on the file system) and transfer some yaml/yml files (also could be located anywhere) via sftp. These files are selected in a file input field which is accessed by a post method in my index.js (in Node). The problem is when I try to read the files I get an error that the file doesn't exist, specifically because the directory defaults to the Node project directory.
For example:
D:/path/to/Node/project
D:/this/is/where/the/keyis
It will try and read this file:
D:/path/to/Node/project/keyis
I'm not sure why I only get the filename. I know receiving the entire path is a security risk but I sort of need the path, even if it's not printed at any stage.
This is the code for the html form:
<form id="file-upload" name="uploadform" method="post" action="/upload" onsubmit="update()">
<input type='text' id='username' class="btn btn-lg btn-default" name='username' placeholder='Enter Username' style="color:#000"><br>
<h3>Select Key:</h3><input type='file' id='key-select' name='keySelect'><br>
<h3>Select Yaml:</h3><input type="file" id="file-select" name="yamlfiles[]" multiple accept=".yml, .yaml"/><br>
<button type="submit" class="btn btn-lg btn-default" id="upload">Upload Data</button>
</form>
and then in the index.js:
router.post('/upload', function(req,res) {
// Create a new connection
var fs = require('fs');
var Connection = require('ssh2');
var c = new Connection();
// Get the files to be uploaded
var files = req.body.yamlfiles;
var uname = req.body.username;
var key = req.body.keySelect;
...
...
c.connect(
{
host: 'some_host',
port: 22,
username: uname,
privateKey: fs.readFileSync(key)
}
);
Essentially readFileSync isn't working unless 'key' is in the project path. Is there something that I am missing? The same happens with 'files'.
Okay, I seem to have solved my own problem. For anyone that faces this problem in the future I will give a rough outline to my solution.
I needed to add: enctype="multipart/form-data" to my form. I had previously tried this, but I didn't understand what I needed to change on the server side. Using the nodejs module: https://github.com/andrewrk/node-multiparty I could parse the form data into a readable object. My server side code then became something like this:
router.post('/upload', function(req,res) {
// Create a new connection
var fs = require('fs');
var multiparty = require('multiparty');
var util = require('util');
var Connection = require('ssh2');
var c = new Connection();
// Get the files to be uploaded
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
// Each element of the object is an array
console.log("form parsed");
// yamlfiles is an array anyway
var yamlfiles = files.yamlfiles;
// username is just a text field, so the 0th element is username
var uname = fields.username[0];
// files is a single file, so the 0th element is my key
var key = files.keySelect[0];
...
...
// access key with key.path (will be a fakepath)
fs.readFileSync(key.path);
// access name with key.originalFilename
console.log(key.originalFilename);
After a bit of fiddling around It works perfectly. I hope this helps anyone else that faces this problem in the future and thank you very much to those who offered me assistance.

createObjectURL unsupported in Opera

Checking this adress: http://caniuse.com/#feat=bloburls you can see this is the only desktop browser with no support for this feature.
How can I find a workaround for this without to much change in my code?
var jpeg = NewBlob(imgData.array.buffer, "image/jpeg");
var url = DOMURL.createObjectURL(jpeg);
If your use case is to show a thumbnail of a picture the user selects, you can create a data uri for file she uploads. Tested under Opera 12.13 to work even with a couple meg image:
<script>
function handleFiles(files) {
var reader = new FileReader();
reader.onload = function(evt) {
document.getElementById("img1").src = evt.target.result;
}
reader.readAsDataURL(files[0]);
}
</script>
<img id="img1">
<form>
<input type="file" onchange="handleFiles(this.files)">
</form>

Resources