How to use NODE to change some HTML before page load? - html

Hi I have been trying to change some of my HTML elements using NODE.
So I'm using Node modules request and cheerio to get specific html elements before the page loads.
What I want to achieve is to get some data from my database and modify the html before it loads. Here is what I have:
app.js:
router.get('/goToSettings', (req, res) => {
//get session id
const id = 1;
//prepare SQL query
const sqlQuery = "SELECT first_name, last_name, username, email FROM myTable WHERE id = ?";
//Get data from DB by using the id retrieved from SESSION
getConnection().query(sqlQuery, [id], async (err, results, fields) => {
//Catch error with MySQL connection
if(err){
console.log(" > The connection have failed :( >" + err);
res.sendStatus(500);
}
var url = "http://host.com/settings.html";
request(url, async function(error, response, html){
if(!error){
var $ = cheerio.load(html);
//DEFAULT VALUES FROM HTML:
//a.value = Johnny
//b.value = Blue
//c.value = j.b#email.com
//Set new values from my DB
var a = $('input.settings_1').val(results[0].first_name);
var b = $('input.settings_2').val(results[0].last_name);
var c = $('input.settings_3').val(results[0].email);
console.log(a.val()); //myNewFirstName
console.log(b.val()); //myNewLastName
console.log(c.val()); //myNewEmail
await res.redirect('/accountSettings.html'); //VALUES of a,b,c remain (Johnny, Blue, j.b#email.com)
}else{
console.log("WRONG URL");
}
});
});
});
On the console.log data seems to be changed, everything looks okay, but when the page is redirected everything is back to default. I tried loading the page before the changes with no luck. That's where I figure out I have to use await/async so that the changes are made and then the redirect is called, but again nothing changes. Does the redirection resets the html file when loading?
Someone else suggested to use AJAX to perform this task, but I have no previous experience with it. I guess if this is impossible I will try and use it. It has been also suggested to me, that this should not be the way (NODE SHOULD NOT HANDLE THIS), let me know if you agree.
SOLUTION:
//cmd
npm i ejs
//app.js
app.set('view engine', 'ejs');
//Save Data in list of objects
let objects = [
{ value: results[0].first_name },
{ value: results[0].last_name },
{ value: results[0].email }
];
//REDIRECT PAGE WITH SOME DATA
res.render('accountSettings', {object: objects})
//REMEMBER TO CHANGE HTML TO .ejs EXTENSION.
//MOVE settings.ejs TO DIR:
//yourAppDir > views > settings.js
//settings.ejs
<input value="<%= object[0].value %>">
<input value="<%= object[1].value %>">
<input value="<%= object[2].value %>">

The core reason your approach isn't working is that you are creating a DOM from the contents of settings.html, manipulating it, but never saving it back to the file.
However, the approach itself is horribly flawed. It is inefficient and subject to race conditions. You need to change your approach.
Get rid of cheerio
Convert the HTML document to a template. There are plenty to choose from.
Replace the redirect with a call to render that passes your data into the template:
Such:
res.render('accountSettings.ejs', { settings: results[0] });

Related

Redirect issues when using MySQL as session storage

I have set up a Node.js app where I use sessions and store them in MySQL. When using MemoryStorage, redirections work fine, but when using MySQL, req.session doesn't update until you reload or you move to a different page, and I'm forced to replace every single res.redirect('/...') by res.render() of that same page to display anything in req.session immediately.
I've tried using both return res.redirect() and not, as well as using setTimeout, neither work. I can't figure it out and I need sessions to be stored in DB
router.get('/student-sign-up', function (req, res, next) {
res.render('student/signUp', {
title: 'Sign up',
errors: req.session.errors
});
req.session.errors = null; //to flush them on reload
}).post('/student-sign-up', function (req, res, next) {
//Some form checks
let errors = req.validationErrors();
if (errors) {
req.session.errors = errors;
req.session.signUpSuccess = false;
return res.redirect('/student-sign-up');
}
//...
}
The above should redirect to the same page, and display the error (I use Handlebars as my view engine) if there were one, but it simply redirects, and if you refresh manually or submit a faulty from again, then it displays it. Same thing for logins (both success not going into the platform's home, and failure not showing errors either). It's like everything's lagging behind by 1 step...
OK, I found the solution. According to the express-session docs, all I had to do was force a save and then redirect, as so:
req.session.save((err) => {
if (err) {
req.locals.error = err;
return res.redirect('/');
}
return res.redirect('/next-section');
});
I'll leave this here for anyone that might have the same issue!

How to render EJS at anchor

I want to do something (maybe there's an easier way around that i'm not seeing) but basically what i'm trying to do is have an EJS file with a Mailchimp Sign Up form, and when the signup gets filled then it redirects to the same EJS File but this time with a variable that adds text to the HTML.
Basically when the user inputs the email we redirect him to the same section but this time it adds a p tag with the text "Thank you, we've got your email, we'll get in touch with you soon", here's my approach:
app.post('/', (req, res) => {
mailchimp.post('/lists/' + process.env.MAILCHIMP_LIST + '/members', {
email_address: req.body.email,
status: 'subscribed'
}, (err) => {
if(err){
console.log(err.title);
if(err.title == 'Member Exists'){
res.redirect('/#signup', {
Status: "Thank you, we've got your email, we'll get in touch with you soon"
})
}else{
res.redirect('/#signup', {
Status: "We're having problems signing you up, if the problem persists send an email to hello#hibuddie.com"
})
}
}
});
res.redirect('/#signup', {
Status: "Thank you, we've got your email, we'll get in touch with you soon"
});
});
however when trying to do this I get an error on my console:
express deprecated res.redirect(url, status): Use res.redirect(status, url) instead app.js:45:7
I know that probably redirect wont work for EJS, but i don't know how to do it so it redirects the user to the same EJS file at a certain ID, say /#signup in this case.
Pardon my awful code, I'm getting started on this, lol.
You can do it in two way:
Instead of redirecting you can use rendering, and add the variable to the render function.
// pass a local variable to the view
res.render('user', { name: 'Tobi' }, function (err, html) {
// ...
})
and Handle the variable in your view file.
You can Also send an anchor name as variable and add some javascript to handle it. like:
res.render('user', { anchor: 'signup' }, function (err, html) { ... });
And in the template file add some javascript,
window.onload = function (event) {
window.location.hash = "#{{anchor}}";
};
Or, If you are using Ajax call to hit the api you can just return the status like,
res.json({
success: true,//or false
data: {
Status: "some text"
}
})
And handle it in the front end using Javascript.

render JSON response using Jade

i have gone back to basics to try and create a simple example of calling a REST API, receiving some JSON back and rendering the JSON data in HTML using Jade.
I have tried many approaches to this but cannot get any to work.
what code would i need to add to my main script file (below - lxrclient.js) to achieve this. I know i need to add express module, and render the view, but no matter who may approaches i have tried i cannot get it to work. I have also added the jade file i am using further down. really appreciate any help anyone can provide with this.
//this is my main script file lxrclient3.js
var http = require('http');
var express = require('express');
var app = express();
app.set('view engine', 'jade');
var options = {
host: '41.193.214.130',
port: 2510,
path: '/eiftidemo/clt_list',
method: 'GET'
};
http.request(options, function(res) {
var body = '';
//node of these statemnst excecute
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var clientsData = JSON.parse(body);
debugger;
});
}).end();
app.get("/clientlist", function(req, res){
res.render('listlxr', {clientd: clientsData});
});
var server = app.listen(3000, function() {
console.log('Our App is running at http://localhost:3000');
});
here is my Jade view
html
head
title List of Clients
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css")
body
div.row
div.col-md-3
div.col-md-6
div.panel.panel-primary
div.panel-heading.text-center
h1 Client List for Hyposure
div.panel-body
table.table
each clients in clientsData
tr
td.media
span.bg-info= clients.clientName
td.media
span.bg-info= clients.clientSurname
thx to anyone who can help
First of all, when you say Jade I hope you mean Pug, if not, you should update to the newest version of Pug instead.
Now, to send data to your template for the engine to render you send a JSON object, like so;
res.get("/", function(req, res)
{
res.render("page.html", {greet : "Hello"});
}
This is a standard way of rendering a page and sending some data along side.
Now in your Pug (Jade (Same thing)) template you can access the sent variable like so;
html
head
title List of Clients
body
h1 #{greet} <!-- This will print "Hello" -->
This should give you a basic idea on how to render data onto a site, you can also send nested objects and you just work it the same way as in this example, but you point to the correct key. So for example if you're sending the following object:
{
greet: {
message : "Hello",
who : "Adrian"
}
}
Then you can print values from that using:
#{greet.message} #{greet.who}

Call ExpressJS as Rest API for HTML page

I am creating web page with a button to load data from the server using Rest API build through ExpressJS, NodeJs.
var express=require('express');
var mysql=require('mysql');
var app=express();
var server=app.listen(3000,function(){
console.log("Express is running on port 3000");
});
app.get('/search',function(req,res){
var mysql=require('mysql');
var connection = mysql.createConnection({
connectionLimit : 100, //important
host : 'localhost',
user : 'root',
password : '',
database : 'node-test'
});
connection.connect();
connection.query('SELECT name from users', function(err, rows, fields) {
if (err) throw err;
var data=[];
for(i=0;i<rows.length;i++){
data.push(rows[i].name);
}
res.end(JSON.stringify(data));
});
});
HTML page for this application looks like below
<button >Load from server</button>
<div></div>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$(document).on('click','button', function(){
$.ajax({
url: "http://localhost:3000/search"
}).done(function() {
$('div').append("done !!! - ");
});
});
});
</script>
When I run http://localhost:3000/search in browser it gives me output with "name" from the database. But how can I see the index.html page and make it load on button click.
Update:
OP Asks:
"my question is not what code say....my question is how to change the
code so that expressjs works as RESTful API and not rendering engine"
In order to use express as a RESTful API here, you first need to serve up a static page.
Said another way, here are the steps:
1. Get your express server to serve up a static page.
2. Then get the button on that page to make a GET request to your api endpoint at /search (when clicked).
1 is explained in the 2nd part of my answer.
2 should already work, you just need to serve the page and click the button!
I explain why this doesn't work in the first part of my answer. You can't simply navigate to /search. I think that is what you mean by "not use it as a render engine".
Original Answer:
To understand what is happening here, it might be a good idea to look at how you're handling requests in your serverside code:
When I run http://localhost:3000/search in browser it gives me output with "name" from the database.
That code is:
app.get('/search',function(req,res){
var mysql=require('mysql');
var connection = mysql.createConnection({
connectionLimit : 100, //important
host : 'localhost',
user : 'root',
password : '',
database : 'node-test'
});
connection.connect();
connection.query('SELECT name from users', function(err, rows, fields) {
if (err) throw err;
var data=[];
for(i=0;i<rows.length;i++){
data.push(rows[i].name);
}
res.end(JSON.stringify(data));
});
});
This means that whenever a GET request goes to your route (in this case, the path /search on localhost:3000), the callback function executes. Essentially, when you access localhost:3000/search, your browser sends a GET request, Express checks* the request for a match with each route, and finally going "ok, that's the GET request I need to respond to, let's start searching!".
So it's behaving as expected. Of course, that is not what you want...
But how can I see the index.html page and make it load on button click
Try something like this:
app.get('/', function(req,res) {
res.sendfile('public/index.html');
});
It might not work as is, depending on where your html is defined and what you've named it. Remember to send the right file.
A simpler way to reason about this would be to let express know you're serving up static html.**
That could be done with
app.use("/", express.static(__dirname)); But again, make sure the html defined above is in a file in the proper root folder (probably named server or something similar), with the name index.html (and that there is only one of them).
(See the links on how express middleware works, and serving static HTML, at the bottom)
To wrap up, you implement the second half this answer first, so that you can go directly to localhost:3000 to load your index page. That page will have a button. Then, you'll be able to click the button and make a request to your /search route, without redirecting. The contents of name should come back to the browser now (instead of being served as a new page).
*More on how requests get checked/processed here.
**More info on serving static html. This blog on express fundamentals may also be useful.
1-You have to add routing for index.html
app.get("/index", function(req, res) {
res.render(index.html);
});
And then in your ajax code you can redirect to /index using window.location
2- you can directly render index.html.
Something like this
app.get("/search", function(req, res) {
res.render(search.html,{});
});
app.get('/index',function(req,res){
var mysql=require('mysql');
var connection = mysql.createConnection({
connectionLimit : 100, //important
host : 'localhost',
user : 'root',
password : '',
database : 'node-test'
});
connection.connect();
connection.query('SELECT name from users', function(err, rows, fields) {
if (err) throw err;
var data=[];
for(i=0;i<rows.length;i++){
data.push(rows[i].name);
}
res.render(index.html,{data:data});
});
});
then redirect to page on /index when clicking button.
The problem you have is that you are using Express as a render FrameWork. If you want to build an app with REST/API, the framework should not render the views or templates. The webpage navigation should be separate (e.g Angular JS). In your case, when you call /search you are actually only calling something in the server without any rendering instruction. That is why you see a response JSON object instead of your html template.
So, what to do?.. You need to make a navigation app on your client side, just navigating through templates with nothing out of normal, and program your button to do its request to some api url (something like: localhost:3000/api/search) and with the contents of the response do something: like filling a table, showing them somehow or whatever..
I recommend you to give a try to Angular JS. I am sure it can help you
Cheers
Here is the code I use when I am wanting to use a simple index.html page for test some front-end code.
app.get("/", function(req, res) {
res.sendFile( __dirname + '/index.html')
});
This will map the root path to your index.html. The __dirname assumes the index.html file is in the same directory as your initial server/app file for express. If you want to make it more generic you can do something like the following but then you will need to manually add the index.html to the address bar in your browser but it also lets you load any other static files you want.
app.get(/^(.+)$/, function(req, res){
res.sendFile( __dirname + req.params[0]);
});
<button >Load from server</button>
<div></div>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$(document).on('click','button', function(){
$.ajax({
url: "http://localhost:3000/search"
}).done(function(data) {
$('div').append(data);
});
});
});
</script>
you can read the documentation about $.ajax() from jquery api documentation
http://api.jquery.com/jQuery.ajax/

Edit on Express outputing JSON to database field

Trying to create my first simple CRUD in Express JS and I cant seem to find this annoying bug.
When I try to update a field, the JSON from that field, gets outputed to the view, instead of the new data.
Screenshot: http://i59.tinypic.com/wi5yj4.png
Controller gist: https://gist.github.com/tiansial/2ce28e3c9a25b251ff7c
The update method is used for finding and updating documents without returning the documents that are updated. Basically what you're doing is finding documents without updating them, since the first parameter of the update function is the search criteria. You need to use the save function to update an exiting document, after updating it's properties.
Your code below, modified (not tested):
//PUT to update a blob by ID
.put(function(req, res) {
//find the document by ID
mongoose.model('Email').findById(req.id, function (err, email) {
//add some logic to handle err
if (email) {
// Get our REST or form values. These rely on the "name" attributes
email.email = req.body.email;
email.password = req.body.password;
email.servico = req.body.servico;
//save the updated document
email.save(function (err) {
if (err) {
res.send("There was a problem updating the information to the database: " + err);
}
else {
//HTML responds by going back to the page or you can be fancy and create a new view that shows a success page.
res.format({
html: function(){
res.redirect("/emails");
},
//JSON responds showing the updated values
json: function(){
res.json(email);
}
});
}
});
}
});
})