Passing an object to client in node/express + ejs? - node.js

I have a pretty large object that I need to pass to a function in a client script. I have tried using JSON.stringify, but have run into a few issues with this approach - mostly performance related. Is it possible to do something like this in ejs?
app.get('/load', function(req, res) {
var data = {
layout:'interview/load',
locals: {
interview: '',
data: someLargeObj
}
};
res.render('load', data);
});
And in my client script, I would pass this object to a function like so
<script type="text/javascript">
load(<%- data %>); // load is a function in a client script
</script>
When I try this I get either
<script type="text/javascript">
load();
</script>
or
<script type="text/javascript">
load([Object object]);
</script>

In Node.js:
res.render('mytemplate', {data: myobject});
In EJS:
<script type='text/javascript'>
var rows =<%-JSON.stringify(data)%>
</script>
SECURITY NOTE : Don't use this to render an object with user-supplied data. It would be possible for someone like Little Bobby Tables to include a substring that breaks the JSON string and starts an executable tag or somesuch. For instance, in Node.js this looks pretty innocent...
var data = {"color": client.favorite_color}
but could result in a client-provided script being executed in user's browsers if they enter a color such as:
"titanium </script><script>alert('pwnd!')</script> oxide"
If you need to include user-provided content, please see https://stackoverflow.com/a/37920555/645715 for a better answer using Base64 encoding

That is the expected behavior. Your template engine is trying to create a string from your object which leads to [Object object]. If you really want to pass data like that I think you did the correct thing by stringifying the object.

If you are using templating, then it would be much better to get the values in the template, for example whether user is signed in or not. You can get the send local data using
<script>
window.user = <%- JSON.stringify(user || null) %>
</script>
From the server side code, you are sending user data.
res.render('profile', {
user: user.loggedin,
title: "Title of page"
});

Think there's a much better way when passing an object to the ejs , you dont have to deal with JSON.stringfy and JSON.parse methods, those are a little bit tricky and confusing. Instead you can use the for in loop to travel the keys of your objects, for example:
if you have an object like such hierarchy
{
"index": {
"url": "/",
"path_to_layout": "views/index.ejs",
"path_to_data": [
"data/global.json",
{
"data/meta.json": "default"
}
]
},
"home": {
"url": "/home",
"path_to_layout": "views/home/index.ejs",
"path_to_data": [
"data/global.json",
{
"data/meta.json": "home"
}
]
},
"about": {
"url": "/about",
"path_to_layout": "views/default.ejs",
"path_to_data": [
"data/global.json",
{
"data/meta.json": "about"
}
]
}
}
On the EJS side you can loop yourObject like this;
<% if ( locals.yourObject) { %>
<% for(key in yourObject) { %>
<% if(yourObject.hasOwnProperty(key)) { %>
<div> <a class="pagelist" href="<%= yourObject[key]['subkey'] %>"><%= key %></a></div>
<% } %>
<% } %>
<% } %>
For this example [key] can take 'index','home' and 'about' values and subkey can be any of it's children such as 'url','path_to_layout','path_to_data'

What you have is a result like this
[{'re': 'tg'}]
You actually need to loop it. See javascript while loop https://www.w3schools.com/js/js_loop_while.asp
Then, render it in your front end with ejs... i can't help on that, i use hbs

Related

How do i make the ejs variable work that i send from nodejs?

I am tring to get a variable that i send from nodejs to ejs to work. But for some reason it wont, i cant figure out why.
This is the index.js:
var newOne = "Yes"
router.get('/main', ensureAuthenticated, (req, res) =>
res.render('main', {
user: req.user,
newOneInView : newOne
})
)
And this is in the main.ejs file:
<%if (newOneInView == "Yes") { %>
document.getElementById("avatar").src = "/static/images/Apocaliptic1.png";
<% } %>
So what i am trying to achieve is that variable will be seen from the nodejs at the main.ejs page but some reason it wont change the image SRC. What am i doing wrong here?
In your template you need to make sure that you place the code for changing the src attribute in a script that is placed after the element (alternatively you can use a listener for the content to be loaded), e.g:
<body>
<img id="avatar" src="/some-other-path"/>
<script>
<% if (newOneInView === "Yes") { %>
document.getElementById("avatar").src = "/static/images/Apocaliptic1.png";
<% } %>
</script>
</body>

Client side and Server side rendering of ejs template

I always wanted to learn NodeJS to be able to run the same code on server and client side.
I am using NodeJS with Express and EJS.
So. I have a .ejs page with lot's of HTML, JS, CSS and a small bit with template. For the sake of justice let it be like this:
the_list-->some.ejs
<ul>
<% for(i=0;i>the_list.length;i++) { %>
<li>the_list[i]</li>
<% } %>
</ul>
After some rendering on the server we have a perfect list.
So.
Now I want to rerender it on the client. I made some ajax request and now I have new items in the_list. What is the right way?
As per ejs templates documentation
var template = new EJS({
text: `
<ul>
<% for(i = 0; i < the_list.length; i++) { %>
<li>the_list[i]</li>
<% } %>
</ul>
`
});
var html = template.render({ the_list: data });
document.getElementById('list-wrapper').innerHTML = html;
<div id="output"></div>
<script src="/assets/js/ejs.js"></script>
<script>
let blogPosts = [
{
title: 'Perk is for real!',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-19'),
createdAt: new Date('2016-03-19')
},
{
title: 'Development continues...',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-18'),
createdAt: new Date('2016-03-18')
},
{
title: 'Welcome to Perk!',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-17'),
createdAt: new Date('2016-03-17')
}
];
var html = ejs.render(`<% for(let i = 0; i < posts.length; i++) { %>
<article>
<h2><%= posts[i].title %></h1>
<p><%= posts[i].body %></p>
</article>
<% } %>`, {posts: blogPosts});
// Vanilla JS:
document.getElementById('output').innerHTML = html;
</script>
download ejs.js or ejs.min.js from latest version
Sure, EJS works on the client. You can trivially keep the template in a string variable or apply EJS to user-provided input, but more likely, you'll want to store a template in a script (which can be in an external file) or use fetch to grab your template from another file on demand.
Using a template in a <script> is straightforward:
const people = ["geddy", "neil", "alex"];
const template = document
.querySelector("#template")
.innerText;
document.querySelector("#output")
.innerHTML = ejs.render(template, {people});
<!-- could be an external file -->
<script id="template" type="text/template">
<%= people.join(", "); %>
</script>
<div id="output"></div>
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
For fetch, I'll mock the response so it'll be runnable in a snippet:
// mock fetch for illustrative purposes;
// its response content would be another file
fetch = async url => ({text: async () => '<%= people.join(", "); %>'});
fetch("/your-template")
.then(res => res.text())
.then(template => {
const people = ["geddy", "neil", "alex"];
document.querySelector("#output").innerHTML =
ejs.render(template, {people});
});
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
<div id="output"></div>
If this seems like too much heavy lifting, you can bury the fetch in a helper function, or go a step further and pick an attribute for each URL, then plug everything in with a call to a library function you can abstract away from the main code. A simple example:
// mock fetch for illustrative purposes;
// its response content would be in other files
const responses = {
"/template.ejs": "<%= 42 %>",
"/other-template.ejs": "<%= 43 %>",
};
fetch = async url => ({text: async () => responses[url]});
[...document.querySelectorAll("[data-template]")]
.forEach(e => {
fetch(e.getAttribute("data-template"))
.then(res => res.text())
.then(template => {
e.innerHTML = ejs.render(template);
});
});
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
<div data-template="/template.ejs"></div>
<div data-template="/other-template.ejs"></div>
Either way, keep in mind that JS will run after the static HTML is parsed and the DOM loads. This means the data won't appear all in one fully-formed piece as when using EJS on the server. Network errors are possible.
See also using ejs partials from express client side. If you want to mock the include function, the problem is that the fetch call is asynchronous but the include function isn't. EJS offers an include callback that seems like it offers an opportunity to pull in an external file, but it's purely synchronous and won't await any promises you return. How to work around this best depends on your use case.
This should work, looks like your problem was the relational operator '>' because it will never output something.
<ul>
<% for(var i=0; i<the_list.length; i++) { %>
<li>
<a>
<%= the_list[i]%>
</a>
</li>
<% } %>
</ul>

node/express retrieving html templates from mongodb

Hi I'm new to node and I'm trying to make a simple blog.
I want to route /pages/:post to search a database for "post" and return an html template file that I can use as a partial.
var posts = {
'myarticle': {
template: partial1.html
}
};
var findPost = function (post, callback) {
if (!posts[post])
return callback(new Error(
'No post matching '
+ post
)
);
return callback(null, posts[post]);
};
app.get('/pages/:post', function(request, response) {
var post = request.params.post;
findPost(post, function(error, post) {
if (error) return;
return response.render('posttemplate', post);
});
});
and posttemplate is a template file like ejs (i'm not comfortable with Jade as of yet) that looks like
<html>
<% include ../partials/header %>
<body>
<% include /thereturnedpost %>
</body>
</html>
Is this possible? I've looked through documentation/tutorials but none of them are clear.
I'l answer with codes examples from my current project
<body>
<%- body %>
<script type="text/javascript" src="/vendors.js"></script>
<script type="text/javascript" src="/main.js"></script>
</body>
and
res.render('index', {
body: content
});
So your example is almost correct. After fetching the data from the database you send it to render function (in my case I render content ).
It is for the case when you keep in the db html string.
Also I can recommend you to use markdown (e.g. markdown-js) for the blog posts. It may be simpler to write and edit well formatted content, and less information to keep (comparing to html).
As per your coding, your blog post contents are static and are in template htmls.
If you get the data from db, you should modify your code such that the template file is with the HTML markup/ejs variables and send the data values separately.
This can be accomplished through ejs.renderFile method...https://www.npmjs.com/package/ejs2
Hope it will help you...

Nested resources in railway.js: Says cannot POST

I am trying out an MVC framework called railway.js (which sits on top of Node, Express, Mongoose, and Mongo).
I'm trying to get nested resources to work. I did the following scaffolding commands:
railway g scaffold user name email description
railway g scaffold setup title description
Then I changed the routes.js file to:
exports.routes = function (map) {
map.resources('users',function(user) {
user.resources('setups');
});
});
Doing railway r gives what I hoped for:
user_setups GET /users/:user_id/setups.:format? setups#index
user_setups POST /users/:user_id/setups.:format? setups#create
new_user_setup GET /users/:user_id/setups/new.:format? setups#new
edit_user_setup GET /users/:user_id/setups/:id/edit.:format? setups#edit
user_setup DELETE /users/:user_id/setups/:id.:format? setups#destroy
user_setup PUT /users/:user_id/setups/:id.:format? setups#update
user_setup GET /users/:user_id/setups/:id.:format? setups#show
users GET /users.:format? users#index
users POST /users.:format? users#create
new_user GET /users/new.:format? users#new
edit_user GET /users/:id/edit.:format? users#edit
user DELETE /users/:id.:format? users#destroy
user PUT /users/:id.:format? users#update
user GET /users/:id.:format? users#show
When I start up the server, add a user (happens to be 4e4b61e39f0d60d834000002), then go to http://localhost:3000/users/4e4b61e39f0d60d834000002/setups/new, it says I "cannot POST".
What am I missing? What's a good debugging approach?
I also tried adding an element into the UserSchema object: setups: [SetupSchema]. (Shouldn't we have to do this?)
Thanks in advance.
in your setup_controller:
before(loaduser);
...
function loadUser () {
User.findById(req.params.user_id, function (err, user) {
if (err || !user) {
redirect(path_to.users);
} else {
// this is where we make the actual user object accessible inside the view templating
this.user = user;
next();
}
}.bind(this));
}
in your setup/form.jade:
- form_for( setup, { method: 'POST', action: path_to.user_setups(user) }, function(form){
//layout your form
- });
Look at file app/views/setups/new.ejs:
<h1>New setup</h1>
<% form_for(setup, {action: path_to.setups, method: 'POST', id: "setup_form"}, function (form) { %>
<%- partial('setups/form.ejs', {locals: {form: form, setup: setup}}) %>
<%- form.submit('Create setup') %> or
<%- link_to('Cancel', path_to.setups) %>
<% });%>
It refers to not existing route path_to.setups, you have to change it to correct route path_to.user_setups:
<h1>New setup</h1>
<% form_for(setup, {action: path_to.user_setups(user), method: 'POST', id: "setup_form"}, function (form) { %>
<%- partial('setups/form.ejs', {locals: {form: form, setup: setup}}) %>
<%- form.submit('Create setup') %> or
<%- link_to('Cancel', path_to.setups) %>
<% });%>
So, you will POST to /users/.../setups instead of POST to /users/.../setups/new.
Please note that you need pass user as first argument of path_to helper, so railway will build correct route for you.

plotting Graph with flot

I want to plot graph using flot and mysql but an exception occurs
getData.php
$sql = mysql_query("SELECT count(Msg_ID) as msgCount,From_user
FROM Messages
GROUP BY From_user");
echo "[";
while($result = mysql_fetch_array($sql))
{
//print_r($result);
echo "[".$result['msgCount'].",".$result['From_user']."]"."\n";
}
echo "]";
And for plotting
<div id="plotarea" style="width:600px;height:300px;">
<script type="text/javascript">
var options = {
lines: { show: true },
points: { show: true },
xaxis: { min:0,max:5 },
yaxis: { min:1 ,max:60},
};
$.ajax({
url:"getData.php",
type:"post",
success:function(data)
{
alert(data);
$.plot($("#plotarea"),data,options);
//alert(data);
}
})
</script>
</div>
What is wrong with this code?
Next I want to plot graph with one of the axis is time.
$sql = mysql_query("SELECT count(Msg_ID) as msgCount,From_user
FROM Messages
GROUP BY From_user");
while($result = mysql_fetch_array($sql))
{
$user_data[] = array($result['msgCount'],$result['From_user']);
}
echo json_encode($user_data);
The above will eliminate issues with comma separation (which, from what I can tell, you never resolved).
Next, the javascript:
<script type="text/javascript">
$(function () {
var options = {
lines: { show: true },
points: { show: true },
xaxis: { min:0,max:5 },
yaxis: { min:1 ,max:60},
};
$.get("getData.php", function(data){
$.plot($("#plotarea"),data,options);
},
json);
});
</script>
Notice that I changed $.ajax to $.get, since you weren't passing any data from the page to the script, a post is not necessary. And if you use $.get, all of the setting names are assumed.
Also notice that I pulled the script out of the html and put it within the jquery window.onload syntax : $(function () { . This would go in the head of your html.
From what I can tell, you aren't really in need of ajax, since you didn't define any sort of event that would trigger the $.ajax function. It looks like you are using ajax to call a script when you could just put the script into the same script that loads the page, like:
<?php
$sql = mysql_query("SELECT count(Msg_ID) as msgCount,From_user
FROM Messages
GROUP BY From_user");
while($result = mysql_fetch_array($sql))
{
$user_data[] = array($result['msgCount'],$result['From_user']);
}
?>
<script type="text/javascript">
$(function () {
var options = {
lines: { show: true },
points: { show: true },
xaxis: { min:0,max:5 },
yaxis: { min:1 ,max:60},
};
var userposts = <?php echo json_encode($user_data); ?>;
$.plot($("#plotarea"),userposts,options);
</script>
<style type="text/css">
#plotarea {
width: 600px, height: 300px;
}
</style>
</head>
<body>
.....//Put whatever before the div
<div id="plotarea"></div>
.....//Finish up the page.
Firstly it looks like the JavaScript list you are creating with your PHP code isn't separating each data point list item with a comma separator.
According to the jQuery $.ajax documentation the first argument passed to the success function is the data returned from the server, formatted according to the 'dataType' parameter. You haven't provided a dataType parameter. The docs say it will intelligently pass either responseXML or responseText to your success callback, based on the MIME type of the response if no dataType has been specified.
I'm guessing the data getting passed to the plot function is a plain old string instead of a JavaScript list object as expected by Flot. Adding a dataType: 'json' option to your $.ajax call should fix this up.
What you're trying to output is a json document in the php side, which will directly be parsed to a java script array (either manually or automatically by libraries like jquery)
So there is no need to print json in php instead you can easily feed data into a php array and use the json_encode function to easily convert it to a json string.
A small example could help
you were trying to output
echo "[".$result['msgCount'].",".$result['From_user']."]"."\n";
which in java script [] = array and you are creating [[]] = array inside array.
But when the array is big, it's cumbersome to echo in php.
What do we do.
An array structure is similar in php.
You will need to add data into php as an "array inside array"
eg: php array(array(1,2,3)) = [[1,2,3]].
How to map it to json?
easy==> echo json_encode(array(array(1,2,3));
Cheers

Resources