node/express retrieving html templates from mongodb - node.js

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...

Related

How to render html file inside of .ejs file

I want to load a default header on a .ejs file.
I asked ChatGPT but it didnt give me the correct code.
This is the index.ejs file that is being send over email:
<!DOCTYPE html>
<html>
<head>
<title>My Web Page</title>
</head>
<body>
<% include header %>
<h1>Welcome to My Web Page</h1>
</body>
</html>
ChatGPT said to use the tag <% include header %> but that didnt work.
These two fields are in the same folder.
The header.html file is the following:
<header>
<nav>
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>
</header>
Is there any way to render html inside a .ejs file?
Below is the actual code that sends the email:
async function sendEmails(winningCampaigns) {
fs.readFile('backend/views/index.ejs', 'utf-8', async (err, data) => {
if (err) throw err;
let html = data.replace(/{{name}}/g, 'John Doe');
for (let campaign of winningCampaigns) {
sendEmail({
to: "jcasasmail#gmail.com",
subject: "Hello ✔", // Subject line
html: html
})
}
});
}
For starters, there's no purpose for the async keyword if the function doesn't use await. Any code that awaits sendEmails won't actually wait for readFile to resolve because it's callback rather than promise-based. Consider using fs.promises.readFile here, or better yet, use EJS's built-in renderFile.
On to the main point, your code doesn't run EJS at all. fs.readFile doesn't treat the file as a template, even with an .ejs extension.
data.replace(/{{name}}/g, 'John Doe'); defeats the purpose of EJS, which should be doing the templating. You're probably in a testing phase, but name isn't used in the template.
You can use <%- include("header.html") %> in EJS to include a .html file, which should be in the same directory as the EJS template.
Here's how I'd approach the sendEmails function:
const ejs = require("ejs"); // ^3.1.8
const path = require("path");
async function sendEmails(winningCampaigns) {
const p = path.join("backend", "views", "index.ejs");
const html = await ejs.renderFile(p, {name: "John Doe"});
for (const campaign of winningCampaigns) {
sendEmail({
to: "<Your email address>",
subject: "Hello ✔",
html,
});
}
}
Note that I'm using path.join() rather than string concatenation to build system-agnostic paths.
If sendEmail is async, be sure to await that as well.
My file structure is:
├── backend
│ └── views
│ ├── header.html
│ └── index.ejs
└── send-emails.js

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>

meteor with flow router layout is rendered twice

I don't know why but my layout is rendered two times.
Here is my index.html:
<head>
<title>title</title>
</head>
<body>
{{>layout}}
</body>
Here is my layout:
<template name="layout">
{{#if canShow}}
{{>Template.dynamic template=content}}
{{else}}
{{> loginButtons}}
{{/if}}
</template>
So here without route my template is display just one time.
Here is my route:
FlowRouter.route('/', {
action() {
BlazeLayout.render("layout", {
content: "home"
});
}
});
But with this route my template is display a second time.
This is my helpers, I think there is nothing to do with this problem but we never know.
Template.home.onCreated(function() {
this.autorun(() => {
this.subscribe('post');
});
});
Template.layout.helpers({
canShow() {
return !!Meteor.user();
}
});
Template.home.helpers({
cats() {
return Posts.find({});
}
});
you don't need to render layout in the body.
The router will take care of the rendering.
so, just have
<body>
</body>
or don't even have it at all.
Edit: Thanks to Keith, I have a better understanding of my problem. Here is his comment:
one thing to keep in mind, all the html you write in meteor isn't kept as html. It all gets converted to javascript. Things like index.html do not get pushed to the browsesr. Meteor just takes all the html you write converts it to javascript and renders what it needs to based on what your code says. This is how it knows todynamically change and rerender html
For things like changing title of the head or add meta etc, we can do it directely in the javascript.
ex: Meteor - Setting the document title

Node Express EJS Dynamic template render

Hi I am trying to create dynamic template system in express, where I will get dynamic content from database and then render output in single index.ejs file.
Here is my index.js
router.get('/', function(req, res, next) {
var dataFrmDB = {
pageContent: "<%= data.pageTitle %>",
pageTitle: "home"
};
res.render('index', {data:dataFrmDB} );
});
And index.ejs contains:
<%= data.pageContent %>
What I should do so that I can render "home" as output. Is this possible?
I was working on something similar when we migrated from drupal to nodejs, I used ect for rendering instead of jade, its faster and much easier to deal with, However, its much better to use design pattern if you have a big dynamic website
js controller file
model.homepage(function(data)
{
res.render("homepage.ect",data,function(err,html)
{
// Do something before you send the response such as minification, or error handling
res.send(html);
});
});
ECT file
<html xmlns="http://www.w3.org/1999/xhtml" lang="ar" xml:lang="ar">
<head>
<%- #page.title.body %>
<%- #page.headerScript.body %>
<style type="text/css">#homepage-container{min-height:300px;color:#353535;float:right;width:100%}</style>
</head>
<body>
<% include 'upper_bar.ect' %>
<%- #page.headerAd.ads %>
<%- #page.notifications.body %>
<%- #page.autocomplete.body %>
<%- #page.redirect.body %>
<%- #page.navigation.body %>
<%- #page.overlayAd.ads %>
</body>
</html>
why bother so much?
You can easily do this using templatesjs
without any template engine.
let me show you how your work can be done using templatesjs
html file
<html>
<head>
<title> <%title%> </title>
</head>
<body>
your content goes here
</body>
</html>
now use templatesjs in you node.js file
var tjs = require("templatsjs");
router.get('/', function(req, res, next) {
var data = fs.readFileSync("./index.html");
tjs.set(data); // invoke templatesjs
var output = tjs.render("title","home");
/* this will replace the <%title%> tag
in the html page with actual title*/
res.write(output);
res.end()
});
i have used fs.readFileSync to keep simplicity of code you can use the asynchronus function if you want (fs.readFile).
a good referrence can be found here
Installation :
$ npm install templatesjs

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

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

Resources