unable to pass data from js to jade in node.js - javascript

I am reading a row from a table(emp) in cassandra.
I am trying to pass the result from js file to jade file to present the data on the user interface.
I have the js function as below
router.get('/cassandra', function (req, res)
{
client.connect(function(err){
});
client.execute('SELECT * FROM monica.emp WHERE empid= 324;', function (err, result) {
var user = result.rows[0];
console.log("here is the user", user.empid, user.firstname);
res.render('cassandra',{"cassandra":user});
});
});
my log is reading the result. But i am unable to pass the same to the UI from the jade file.
Below is my Jade File
extends layout
block content
p Cassandra
for i in cassandra
.c=i.empid+" "+i.deptid
I am getting a display something like below
Can someone please help me with where I am going wrong over here?

Looks like user is not an array. So just try cassandra.empid without a loop in the jade view

You might need to use a promise. Are you trying to render the template before the response comes back from the server?

try it
-for(var i in cassandra){
.c=#{cassandra[i].empid}+" "+#{cassandra[i].deptid}
-}

Your jade file should read
extends layout
block content
p Cassandra
#{cassandra.empid} #{cassandra.deptid}

Related

How to Update HTML using Node JS from MYSQL

Question
How can i change my HTML data in the files via node JS i am not using EJS or any view engine I have a views folders where all the files are .js files returning HTML how can i change the data from the node server which selects the MYSQL data for example if i have
I have tried using res.send but it changes the whole file how can i change for example on the about page /aboutus
<h1 id='name'></h1>
How can i add data from the server to edit that file?
thanks
For those who can't quite understand what i am saying is i have a server side which is meant to retrieve an html name for example david and i have a views folder containing js files like home.js which returns html value to the index.html file i want to change the heading tag in html like the code above i want 'David' to be put in the h1 tag
I have not been able to understand perfectly what you want to do ...
But I believe that in any case to create a dynamic client server infrastructure you could use two methods:
by creating an endpoint that returns the data in json or xml format from the database, and replacing your DOM elements with the real data
example:
Express Backend example
app.get('/api/user', async function(req, res) {
const userdata = await getUserData()
res.send(JSON.stringify(userdata));
});
Front end example:
fetch('http://MYAPI.com/api/user')
.then(response => response.json())
.then(data =>{
document.getElementById("username").innerHTML = data.username;
document.getElementById("email").innerHTML = data.email;
});
by converting your html files into handlebars (for example) and replacing the handlebars tags with real data, then return html ready to be rendered.

NodeJS - AngularJS Send JSON object and render templat at same time?

I have some problems with my api.
I use NodeJS with ExpressJS Routing and AngularJS.
What I want to do is to render a template (ejs) and at the same time send a json object.
In my routes folder I have the index.js file with this (a little part) :
router.get('/data', function(req, res, next){
Planning.getPlanningM(function(resultat){
res.json(resultat);
res.render('index');
});
});
About the variable resultat I'm sure that it contains that I want. But I can't do the res.json and the res.render. Because of the two invoke of send function.
And in my angular I have this in a function :
var resultat = []
$http.get('/data')
.success(function(res){
angular.extend(resultat, res.data);
})
.error(function(res){
console.log('err');
})
return resultat;
The goal is to render my index.ejs and to show my planning in this page. But I find no solution to do this.
This is my first ask on stackoverflow, english is not my native language. Please don't be rude with me :)
I'm not familiar with EJS, I use handlebars, but you should be able to pass data in the render function like so-
...
res.render("index", { data:resultat });
...
Then access it in the template in whatever format EJS uses. For hbs it would look something like
...
<div>My data looks like this: {{data}}</div>
...
Again, EJS is sure to do it differently, refer to the doc to ensure you have the correct format.
Thanks for your answer MPawlak !It helped me !
That I want is to send the data with the render like you do yes.
But I want to grab/take this data in my angular Factory (my factory fills the controller, this part works) that I show before :
var resultat = []
$http.get('/data')
.success(function(res){
angular.extend(resultat, res.data);
})
.error(function(res){
console.log('err');
})
return resultat;
With your method, I can take this data into my view directly your right and it's works ! Thanks !
<pre> <%= data %> </pre>
So I was thinking about a dirty temporaly solution to do this:
<textarea ng-model="planning"> <%= data %> </textarea>
But when I want to show this planning it don't work and stay empty... I don't understand why.
But to get a good and clean solution I think this is not a good idea, so my ask is the same... how to take this data in my angular factory directly ?

Can't Render EJS Template on Client

I'm coding an application on express, and I'm using ejs as a view/template engine.
At path /artists, I'm rendering the view artists.ejs which has artists covers. When clicking on a cover, I want an AJAX call to retrieve the corresponding data, place it in my template/view for artist artist.ejs and display this template in my HTML under the cover.
I've seen this related question but it has not solved my use case.
Everything seems clear, but I can't render the data with the template. I would like to compile the template server-side, send it to the client ready to use, and then fill it in when needed with the data received from the AJAX call.
What I've done:
When calling /artists, compile on server-side using ejs.compile(str, opt):
router.get('/artists', function(req, res) {
// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) { // Convert template file to string
artist_template = ejs.compile(template); // Compile template
res.render('artists.ejs', {template: artist_template}); // render page with compiled template
});
I took care of converting the file into String, as ejs compiler only works with String (compared to Jade .compileFile)
Then on client-side, I grab the function:
<script>
var template = <%= template %>
</script>
Then on another script, I retrieve the data with an AJAX call:
$.get('/artists/'+artist_name, function(data) {
var html = template({artist: data});
$('#artist-page').html(html);
}
But when I make the call, I receive:
Uncaught ReferenceError: fn is not defined
When I call the template, fn, I receive:
Uncaught ReferenceError: opts is not defined.
Is the function fn hard-coded? I've read the EJS and Jade documentation but there was little relevant information in regards to my issue.
Do I perhaps need the template on client-side also?
I eventually found a workaround to my question, and I understood with your answer that you could proceed in 2 different ways:
1) What I did: read and save template as a string, then render it client-side with ejs Runtime script.
// In controller.js
var templates = {};
templates.template1 = fs.readFileSync(filePath1, 'utf-8'); // Read template as a string
templates.template2 = fs.readFileSync(filePath2, 'utf-8');
...
res.render('app.ejs', {templates: templates}); // Send templates in view
// In view app.ejs
<script type="text/javascript">
var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of strings)
</script>
<script type="text/javascript" src="/JS/ejs.min.js"></script> <!-- Load ejs RunTime -->
// In site.js - javascript client/public file
$.get('/artists', function(data) {
var html = ejs.render(templates.template1, data); // Render ejs client side with EJS script (template1 corresponds to the artists template)
$('#artists-wrapper').html(html); // Sets HTML
});
Thus, I send all my templates on first page load, and then I render the requested page on the client side. The interest, according to what I've read, is that you only send JSON object (your data) through AJAX calls, and not the entire page, making your request light. Only the first load is heavy with all your templates.
2) What I would like to do according to #RyanZim answer: compiling templates server side into functions, send them, and then call them on the client side : template(data). If I understood well, there is no need of EJS client library in this case, and my templates are no longer strings but functions:
// In controller.js
var templates = {};
templates.template1 = ejs.compile(fs.readFileSync(filePath1, 'utf-8'), {client: true}); // Get template as a function
templates.template2 = ejs.compile(fs.readFileSync(filePath2, 'utf-8'), {client: true});
...
res.render('app.ejs', {templates: templates}); // Send templates in view
However, I can't get them in my view:
<script type="text/javascript">
var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of functions)
</script>
is not working. they are functions on the server before I send them, but I don't know how to recover them. Do you have an idea ?
I tried a workaround, by changing them into String before sending them:
templates.template1 = templates.template1.toString();
Send them and then client side, transform them back in functions:
var template = new Function(templates.template1);
$.get('/artists', function(data) {
var html = template(data);
$('#artists-wrapper').html(html); // Sets HTML
});
But that won't work either.
Do you have an idea what I'm missing here?
And last, do you agree that compiling them server side before using the functions is better in terms of computation than rendering each template client-side?
Thanks for the help, and hope that will help anybody else!
You need to use the client option on the server side when you are compiling for the client. From the docs:
client When true, compiles a function that can be rendered
in the browser without needing to load the EJS Runtime
https://github.com/mde/ejs#options
Your server-side code snippet should be:
// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) {
artist_template = ejs.compile(template, {client: true}); // Use client option
res.render('artists.ejs', {template: artist_template});
});

res.sendfile in Node Express with passing data along

Is there any way to redirect to an HTML file from a Node.JS application with something like: res.sendFile of express and pass a JSON data along to the html file?
I know this is late but I wanted to offer a solution which no one else has provided. This solution allows a file to be streamed to the response while still allowing you to modify the contents without needing a templating engine or buffering the entire file into memory.
Skip to the bottom if you don't care about "why"
Let me first describe why res.sendFile is so desirable for those who don't know. Since Node is single threaded, it works by performing lots and lots of very small tasks in succession - this includes reading from the file system and replying to an http request. At no point in time does Node just stop what it's doing and read an entire from the file system. It will read a little, do something else, read a little more, do something else. The same goes for replying to an http request and most other operations in Node (unless you explicitly use the sync version of an operation - such as readFileSync - don't do that if you can help it, seriously, don't - it's selfish).
Consider a scenario where 10 users make a request for for the same file. The inefficient thing to do would be to load the entire file into memory and then send the file using res.send(). Even though it's the same file, the file would be loaded into memory 10 separate times before being sent to the browser. The garbage collector would then need to clean up this mess after each request. The code would be innocently written like this:
app.use('/index.html', (req, res) => {
fs.readFile('../public/index.html', (err, data) => {
res.send(data.toString());
});
});
That seems right, and it works, but it's terribly inefficient. Since we know that Node does things in small chunks, the best thing to do would be to send the small chunks of data to the browser as they are being read from the file system. The chunks are never stored in memory and your server can now handle orders of magnitude more traffic. This concept is called streaming, and it's what res.sendFile does - it streams the file directly to the user from the file system and keeps the memory free for more important things. Here's how it looks if you were to do it manually:
app.use('/index.html', (req, res) => {
fs.createReadStream('../public/index.html')
.pipe(res);
});
Solution
If you would like to continue streaming a file to the user while making slight modifications to it, then this solution is for you. Please note, this is not a replacement for a templating engine but should rather be used to make small changes to a file as it is being streamed. The code below will append a small script tag with data to the body of an HTML page. It also shows how to prepend or append content to an http response stream:
NOTE: as mentioned in the comments, the original solution could have an edge case where this would fail. For fix this, I have added the new-line package to ensure data chunks are emitted at new lines.
const Transform = require('stream').Transform;
const parser = new Transform();
const newLineStream = require('new-line');
parser._transform = function(data, encoding, done) {
let str = data.toString();
str = str.replace('<html>', '<!-- Begin stream -->\n<html>');
str = str.replace('</body>', '<script>var data = {"foo": "bar"};</script>\n</body>\n<!-- End stream -->');
this.push(str);
done();
};
// app creation code removed for brevity
app.use('/index.html', (req, res) => {
fs
.createReadStream('../public/index.html')
.pipe(newLineStream())
.pipe(parser)
.pipe(res);
});
You get one response from a given request. You can either combine multiple things into one response or require the client to make separate requests to get separate things.
If what you're trying to do is to take an HTML file and modify it by inserting some JSON into it, then you can't use just res.sendFile() because that just reads a file from disk or cache and directly streams it as the response, offering no opportunity to modify it.
The more common way of doing this is to use a template system that lets you insert things into an HTML file (usually replacing special tags with your own data). There are literally hundreds of template systems and many that support node.js. Common choices for node.js are Jade (Pug), Handlebars, Ember, Dust, EJS, Mustache.
Or, if you really wanted to do so, you could read the HTML file into memory, use some sort of .replace() operation on it to insert your own data and then res.send() the resulting changed file.
Well, it's kinda old, but I didn't see any sufficient answer, except for "why not". You DO have way to pass parameters IN static file. And that's quite easy. Consider following code on your origin (using express):
let data = fs.readFileSync('yourPage.html', 'utf8');
if(data)
res.send(data.replace('param1Place','uniqueData'));
//else - 404
Now for example, just set a cookie, in yourPage.html, something like:
<script>
var date = new Date();
document.cookie = "yourCookieName='param1Place';" +
date.setTime(date.getTime() + 3600) + ";path=/";
</script>
And you can plainly pull content of uniqueData from yourCookieName wherever you want in your js
I think the answer posted by Ryan Wheale is the best solution if you actually want to modify something within an HTML file. You could also use cheerio for working with complex logic.
But in regards to this particular question where we just want to pass some data to the client from the server, there's actually no need to read index.html into memory at all.
You can simply add the following script tag somewhere at the top of your HTML file:
<script src="data.js"></script>
And then let Express serve that file with whatever data needed:
app.get("/data.js", function (req, res) {
res.send('window.SERVER_DATA={"some":"thing"}');
});
This data can then easily be referenced anywhere in your client application using the window object as: window.SERVER_DATA.some
Additional context for a React frontend:
This approach is especially useful during development if your client and server are running on different ports such as in the case of create-react-app because the proxied server can always respond to the request for data.js but when you're inserting something into index.html using Express then you always need to have your production build of index.html ready before inserting any data into it.
Why not just read the file, apply transformations and then set up the route in the callback?
fs.readFile(appPath, (err, html) => {
let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));
app.get('/', (req, res) => {
res.send(htmlPlusData);
});
});
Note that you can't dynamically change data, you'd have to restart the node instance.
You only have one response you can return from the server. The most common thing to do would be to template your file on the server with nunjucks or jade. Another choice is to render the file on the client and then to use javascript to make an ajax call to the server to get additional data. I suppose you could also set some data in a cookie and then read that on the client side via javascript as well.
(Unless you want to template the html file to insert the json data into a script tag). You'll need to expose an api endpoint in express the send along the data to the page, and have a function on the page to access it. for example,
// send the html
app.get('/', (req, res) => res.sendFile('index'));
// send json data
app.get('/data', (req, res) => res.json(data));
Now on the client side you can create a request to access this endpoint
function get() {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open('GET', '/data');
req.onload = () => resolve(req.response);
});
}
// then to get the data, call the function
get().then((data) => {
var parsed = JSON.parse(data);
// do something with the data
});
EDIT:
So arrow functions probably don't work client side yet. make sure to replace them with function(){} in your real code
This is pretty easy to do using cookies. Simply do this:
On the server side -
response.append('Set-Cookie', 'LandingPage=' + landingPageCode);
response.sendFile(__dirname + '/mobileapps.html');
On client side -
<!DOCTYPE html>
<html>
<body onload="showDeferredLandingPageCode()">
<h2>Universal Link Mobile Apps Page</h2>
<p>This html page is used to demostrate deferred deeplinking with iOS</p>
</body>
<script language="javascript">
function showDeferredLandingPageCode() {
alert(document.cookie);
}
</script>
</html>

Node.js/jade -- How can I pass mysql data as a local variable into inline javascript?

My Node script has this in it:
var connection = mysql.createConnection(...);
connection.connect();
connection.query(/*sql query*/, function(err, rows, fields){
app.get('/', function(req, res){
res.render('index', { data: JSON.stringify(rows) });
});
});
Then if I do this in my Jade template:
body
p !{data}
It displays the data from the MySql query exactly as you'd expect. But if instead I do:
body
script(type='text/javascript').
console.log(!{data});
It gives me [Object, Object, Object, Object....
Why is it interpreted differently if it's part of the client Javascript? And how can I fix this?
I put JSON.stringify in the local variable assignment because if I didn't, nothing would get passed through no matter where in the template I tried to put it. Is there another way I'm supposed to be transforming the data maybe?
You should have to give an index like
console.log(!{data[1]}); to view the objects in console..
I have recently run into this issue like you. I think it would be helpful to point out a few things:
The mysql library from node already returns your data into json. you using stringify returns it as a string; I believe that is why your getting that object back in the console log. try just returning data:rows and accessing the data via dot syntax notation in your template. this is what worked for me.

Categories