Is it possible to create a global Ejs component? - javascript

As you can see I included my navbar component in my index.ejs file and I am doing the same thing with every other file I have, like user page for example. Is there any way that I can use my navbar component in all of my ejs files by including it somewhere once? instead of including it separately in each ejs file?
Right now I am doing this in every one of my files:
index.ejs:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<% include components/navbar %>
rest of the code...
</body>
</html>

as you requested in comments , this is my way to do it :
i have "views" folder in project including these folders inside :
Assets
Layouts
and dynamic ejs files beside them
let's say we want to render this layout which has a header in each page , but with different body :
layout :
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<% for (let cssLink of cssLinks) { %>
<link rel="stylesheet" href="<%= cssLink %>" />
<% } %>
<title>home page</title>
</head>
<body>
<h1>THIS IS HEADER!</h1>
<div id="demo">
<%- mainView %>
</div>
</body>
</html>
you can see there is a variable mainView in the body , so let's see how can we insert our dynamic body there :
i have a function inside my utility folder which reduces the repeat of code :
const ejs = require('ejs');
const path = require('path');
const layoutsPath = path.join(__dirname, "./../views/layouts/");
const assetsPath = path.join(__dirname, "./../views/assets/");
const viewsPath = path.join(__dirname, './../views/');
const load = (basePath, filename, data, cb = null) => {
let route ;
switch (basePath) {
case 'layout': {
route = layoutsPath;
break;
}
case 'asset': {
route = assetsPath;
break;
}
case 'view': {
route = viewsPath;
break;
}
default: {
route = basePath;
}
}
ejs.renderFile(path.join(route, filename), data, (err, str) => {
if (err) {
console.log(err);
return err;
}
if (cb === null) {
return str;
} else {
return cb(str);
}
});
}
module.exports = {
load
}
now in the index.js i want to output my home.ejs inside my header.ejs to the client :
let allUsers = await usersModule.findAll();
let renderedHTML = '';
load('view', 'home.ejs', { allUsers }, (rendered) => {
load('layout', 'header.ejs', { mainView: rendered, cssLinks: ['/css/header.css'] }, (fullView) => {
renderedHTML = fullView;
});
});
res.status(200).send(renderedHTML);
so here i am getting my required data for my body from database , passing it to the home.ejs , there i loop and render my body view , e.x for home.ejs :
<div>
<% for (let user of allUsers) { %>
<ul>
<li>email : <%= user.email %></li>
<li>name : <%= user.name %></li>
</ul>
<% } %>
</div>
rendered body is passed to our callback as parameter , we can pass it to our header now with variablename mainView , so in the header, the entire rendered body will be insert in the right place in our layout .
now our layout and dynamic body have merged and are ready to be sent to the client

Related

Get elements from JSON array to display as a text

So i am trying to get a first_name from JSON object which has array of elements by iterating through it, for example if i type 'Ron' it will display as a text but for some reason I can't get display it as a text unless i send in a respond this `
playerName: nbaPlayer[0]
But it only displays one element as a text since others are not in a response
reponse in a server enter image description here
Here is a code for fetch where i use search bar from handlebars to search for a first_name
const nbaForm = document.querySelector('form')
const search = document.querySelector('input')
const messageOne = document.querySelector('#player-1')
nbaForm.addEventListener('submit', (event) => {
event.preventDefault()
const playerSearch = search.value
messageOne.textContent = 'Loading...'
fetch('http://localhost:4000/nba').then((response) => {
response.json().then(nbaData => {
if (nbaData.playerName === playerSearch) {
messageOne.textContent = nbaData.playerName
} else {
messageOne.textContent = 'not found'
}
})
})
})
request method
app.get('/nba', (req,res) => {
networkManager.nbaPlayerData((data)=>{
/
var nbaPlayer = []
for(var i=0; i<data.data.length; i++){
nbaPlayer.push(data.data[i].first_name)
}
console.log(nbaPlayer)
res.send({
playerName: nbaPlayer
})
})
})
handlebars file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NBA</title>
<link rel = "stylesheet" href="/css/style.css">
</head>
<body>
<div class="main-content">
<h1>NBA SERVER</h1>
<p>Use this site to get NBA player</p>
<form action="">
<input placeholder="Type a players name">
<button>Search</button>
</form>
<p id="player-1"></p>
<h1>{{playerName}}</h1>
</div>
<script src ="/js/fetch-app.js"></script>
</body>
</html>
try this.
fetch('http://localhost:4000/nba').then((response) => {
response.json().then(nbaData => {
var index = nbaData.playerName.indexOf(playerSearch)
if (index !== -1) {
messageOne.textContent = nbaData.playerName[index]
} else {
messageOne.textContent = 'not found'
}
})
})
You are getting an array from
res.send({
playerName: nbaPlayer // nbaPlayer is array
})
but in your fetch, you want to get data as from simple object

If I don't choose a file and press the send button, I will get a popup but it'll break any future popups

If I press Send ☞ without choosing a file, it'll say "No file selected." in a little Bootstrap warning alert but it seems to break all future alerts like file uploaded or file already exists on disk.
If I make an error alert like file already on disk, it'll work and won't break future alerts (meaning it won't stop alerts that popup in the future from showing).
So, why is the "No file selected" alert breaking other alerts?
In this clip, you can see me trying to make more than one of that alert but it just doesn't want to.
Here is my ejs file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Androp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<div class="container-fluid">
<p>
<h1>Androp - Send to [...] MBP</h1>
</p>
<div class="input-group mb-3">
<button class="btn btn-outline-primary" type="button" onclick="send()">Send ☞</button>
<input type="file" class="form-control" id="fileSelector">
</div>
<div id="alerts"></div>
<div class="text-center">
<div id="spinny" class="spinner-border" style="width: 3rem; height: 3rem; visibility: hidden;"></div>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io()
const engine = socket.io.engine
let maxSize = 0
let sending = false
socket.on("disconnect", (reason) => {
alert(`Disconnected from laptop! Check your internet or refresh the page. 💀 (${reason})`, "warning")
})
engine.on("close", reason => {
console.log(reason)
})
socket.emit("getMaxSize", (size) => {
return maxSize = size
})
const alert = (message, type) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = [
`<div class="alert alert-${type} fade show" role="alert">`,
` <div>${message}</div>`,
'</div>'
].join('')
document.getElementById("alerts").append(wrapper)
return bootstrap.Alert.getOrCreateInstance(wrapper)
}
function send() {
if (sending) {
return false
}
sending = true
const file = document.getElementById("fileSelector").files[0] || false
function close(alert) {
setTimeout(() => {
alert.close()
}, 3500)
}
if (!file) { return close(alert("No files to upload.", "warning")) }
if (file.size >= maxSize) { return close(alert(`File is bigger than ${Math.round(maxSize / (1024 ** 2))} MB`, "danger")) }
document.getElementById("spinny").style.visibility = 'visible'
socket.timeout(10000).emit("upload", file, file.name, file.size, (err, status) => {
if(err) {
return close(alert("Could not contact laptop within 10s, laptop might be in sleep mode. 💀", "warning"))
}
document.getElementById("spinny").style.visibility = 'hidden'
sending = false
return close(alert(status.message, status.success ? "success" : "danger"))
})
}
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/#popperjs/core#2.11.5/dist/umd/popper.min.js"
integrity="sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/js/bootstrap.min.js"
integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK"
crossorigin="anonymous"></script>
</body>
</html>
You should move the flag sending = true when you actually plan to send after validation. I would also place sending = false right when callback returns, even if err
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Androp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<div class="container-fluid">
<p>
<h1>Androp - Send to [...] MBP</h1>
</p>
<div class="input-group mb-3">
<button class="btn btn-outline-primary" type="button" onclick="send()">Send ☞</button>
<input type="file" class="form-control" id="fileSelector">
</div>
<div id="alerts"></div>
<div class="text-center">
<div id="spinny" class="spinner-border" style="width: 3rem; height: 3rem; visibility: hidden;"></div>
</div>
</div>
<script src="https://cdn.socket.io/4.5.0/socket.io.min.js" integrity="sha384-7EyYLQZgWBi67fBtVxw60/OWl1kjsfrPFcaU0pp0nAh+i8FD068QogUvg85Ewy1k" crossorigin="anonymous"></script>
<script>
const socket = io()
const engine = socket.io.engine
let maxSize = 0
let sending = false
socket.on("disconnect", (reason) => {
alert(`Disconnected from laptop! Check your internet or refresh the page. 💀 (${reason})`, "warning")
})
engine.on("close", reason => {
console.log(reason)
})
socket.emit("getMaxSize", (size) => {
return maxSize = size
})
const alert = (message, type) => {
const wrapper = document.createElement('div')
wrapper.innerHTML = [
`<div class="alert alert-${type} fade show" role="alert">`,
` <div>${message}</div>`,
'</div>'
].join('')
document.getElementById("alerts").append(wrapper)
return bootstrap.Alert.getOrCreateInstance(wrapper)
}
function send() {
if (sending) {
return false
}
const file = document.getElementById("fileSelector").files[0] || false
function close(alert) {
setTimeout(() => {
alert.close()
}, 3500)
}
if (!file) {
return close(alert("No files to upload.", "warning"))
}
if (file.size >= maxSize) {
return close(alert(`File is bigger than ${Math.round(maxSize / (1024 ** 2))} MB`, "danger"))
}
sending = true
document.getElementById("spinny").style.visibility = 'visible'
socket.timeout(10000).emit("upload", file, file.name, file.size, (err, status) => {
if (err) {
return close(alert("Could not contact laptop within 10s, laptop might be in sleep mode. 💀", "warning"))
}
document.getElementById("spinny").style.visibility = 'hidden'
sending = false
return close(alert(status.message, status.success ? "success" : "danger"))
})
}
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>

Passing data in array that came from an array to chartjs with django

So, I'm trying to pass some data to a chart (using chartjs and django) and I can print my data in my webpage, but can't pass it as arguments to the chart. Also, if I put data hardcoded in the chart it works, but with my own data from an array I can't see anything...
I've tried {{data | safe}} and {{labels | safe}} but I get an error, so I'm not getting what I'm doing wrong. Can anyone help me?
To explain better:
views.py
import csv
def home(request):
csvFilePath = "../data/raw_datasets/covid_confirmed.csv"
data = []
labels = []
with open(csvFilePath, "r") as csvfile:
csv_reader = csv.reader(csvfile, delimiter=',')
next(csv_reader)
for row in csv_reader:
data.append(row[1])
labels.append(row[73])
return render(request, 'home.html',
{
'data': data,
'labels': labels
})
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<title>Crypto Covid</title>
</head>
<body>
<h4>{{data | safe}}</h4>
<p>--------------</p>
<h4>{{labels|safe}}</h4>
<div class="container">
<canvas id="chart">
</canvas>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<script> src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"</script>
<script>
var config = {
type: 'pie',
data: {
datasets: [{
data: {data} ,
backgroundColor: [
'#696969', '#808080', '#A9A9A9', '#C0C0C0', '#D3D3D3'
],
label: 'Population'
}],
labels: {labels}
},
options: {
responsive: true
}
};
window.onload = function() {
var ctx = document.getElementById('chart').getContext('2d');
window.myPie = new Chart(ctx, config);
};
</script>
</html>
The result in my page:
my result page
you need to use template tag called json_script, {{ your_array|json_script:"chart_data" }} and then access this data in javascript -
var value = JSON.parse(document.getElementById('chart_data').textContent);
https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#json-scriptDjango documentation
Try this please
import csv
def home(request):
csvFilePath = "../data/raw_datasets/covid_confirmed.csv"
data_list = []
with open(csvFilePath, "r") as csvfile:
csv_reader = csv.reader(csvfile, delimiter=',')
next(csv_reader)
for row in csv_reader:
data_list.append({'label' : row[73], "y" : row[1]})
t = json.dumps(data_list)
return render(request, 'home.html',
{
'output':t
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<title>Crypto Covid</title>
</head>
<body>
<div id="pie-chart" style="width: 100%;height:370px;">
</div> <!-- edited -->
</body>
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"> <!-- edited--></script>
<!-- edited -->
data = JSON.parse("{{ output|escapejs }}")
window.onload = function() {
var chart = new CanvasJS.Chart("pie-chart", {
type: 'pie',
data: [{type: "pie",dataPoints: data}],
options: {
responsive: true
}
});
chart.render();
};
</script>
</html>
Try passing the data list by list (I use render_template on Flask) and retrieve it on javascript (as an array) with:
labels: [{% for item in families %}
"{{ item }}",
{% endfor %}]
...even if pylint may say criticize the html syntaxe. It runs well.

Stripe Checkout is not defined reference error

I am trying to put Stripe into my web page to accept payments. But whenever I load the page I get an error saying that stripeCheckout is not defined. I know this has to do with the library imported in one of my script tags, but I am not sure why it is showing up as undefined. The library imported I thought was correct. Here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- CSS Link -->
<link rel="stylesheet" href="styles.css">
<title>Store</title>
<script src="https://checkout.stripe.com/checkout.js" defer></script>
<script>
var stripePublicKey = '<%= stripePublicKey %>'
</script>
<script src="store.js" defer></script>
</head>
and this in my store.js file
const stripeHandler = stripeCheckout.configure({
key: stripePublicKey,
locale: 'auto',
token: function(token){
}
})
// Removes cart items after they are purchased
const purchaseItems = () => {
// let cartItems = document.getElementsByClassName('cart-items')[0]
// while(cartItems.hasChildNodes()){
// cartItems.removeChild(cartItems.firstChild)
// }
// updateCartTotal()
let priceElement = document.getElementsByClassName('cart-total-price')[0]
let price = parseFloat(priceElement.innerText.replace('$', '')) * 100
stripeHandler.open({
amount: price
})
}
I believe that that particular Stripe checkout API (and script) is now deprecated.
Here's a link to the docs for the new APIs: https://stripe.com/docs/payments/checkout/migration#client-products
Example from docs:
// client.html
<script src="https://js.stripe.com/v3"></script>
<button id="checkout-button">Pay</button>
// client.js
var stripe = Stripe('YOUR_API_KEY');
var checkoutButton = document.querySelector('#checkout-button');
checkoutButton.addEventListener('click', function () {
stripe.redirectToCheckout({
items: [{
// Define the product and SKU in the Dashboard first, and use the SKU
// ID in your client-side code.
sku: 'sku_123',
quantity: 1
}],
successUrl: 'https://www.example.com/success',
cancelUrl: 'https://www.example.com/cancel'
});
});

Node Js Express Iterate through array

I have split the STDOUT of a shell command into an array, and am passing this to hogan view engine with express.
The below is the from the router file - index.js
/* Test Shell Execute. */
router.get('/shell', function(req, res){
exec('ls -1', function (error, stdout, stderr) {
result = stdout.toString().split("\n");
res.render('shell', { title: "File Explorer",
array1: result[0],
array2: result[1],
array3: result[2],
array4: result[3],
array5: result[4],
error: error,
stderr: stderr
});
});
});
This is working fine, however rather than manually send through each item in the array, I would like to iterate through the items at the view end and then ouput. However I am using Hogan view engine and it doesn't seem to recognise the script tag at all.
This is from my view shell.hjs file.
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>{{ title }}</h1>
<p>Welcome to {{ title }}</p>
<p>Array1 = {{ array1 }}</p>
<p>Array2 = {{ array2 }}</p>
<p>Array3 = {{ array3 }}</p>
<p>Array4 = {{ array4 }}</p>
<script>
for(var i = 0; i > result.length; i++ ) {
document.write('<p>' + result[i] + '</p>')
}
</script>
</body>
</html>
Question: What am I doing wrong and what is the best/easiest way to do this?
Since hogan.js is based on mustache.js, it's always better try to convert your arrays into objects. Try this:
router.get('/shell', function(req, res){
exec('ls -1', function (error, stdout, stderr) {
result = stdout.split("\n"),
filesArray = [];
result.map(function (file, index) {
filesArray.push({index: ++index, file: file});
});
res.render('shell', { title: "File Explorer",
result: filesArray,
error: error,
stderr: stderr
});
});
});
And, in your template:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>{{ title }}</h1>
<p>Welcome to {{ title }}</p>
<ul>
{{#result}}
<li>Array{{index}} = {{file}}</li>
{{/result}}
</ul>
</body>
</html>

Categories

Resources