I am trying to learn EJS and make a blog but I cant seem to understand this error
What I am trying to do is try to write some db response as an Object to an array then push it to the file.
I am using replit DB
const fs = require("fs")
const Database = require("#replit/database")
const db = new Database()
exports.load = async function(){
db.set("hello", {
"author": "Some author 1",
"title": "Blog Post 1",
"content": "First post content",
"date_posted": "Dec 17, 2021"
})
var posts = new Array()
db.list().then(keys => {
keys.forEach(key => {
posts.push(` <article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="/p">Anonymous</a>
<small class="text-muted">${db.get(key).date_posted}</small>
</div>
<h2><a class="article-title" href="#">${ db.get(key).title }</a></h2>
<p class="article-content">${ db.get(key).content }</p>
</div>
</article`
)
})
});
posts = posts.join()
fs.writeFileSync("public/posts.ejs", posts)
}
Error that I am getting when I run the code:
UnhandledPromiseRejectionWarning: TypeError: posts.push is not a function
First, you declare var posts = new Array(). So posts is an array. Next line (in execution order) : posts = posts.join(). So now posts is an empty string. You are changing the type of the variable, which is a bad practice (Typescript wouldn't let you do that). Now next line in execution order : .then(keys =>. You start pushing stuff into posts, but posts is now a string, remember? Not an array anymore.
You use the async keyword for no reason, since there is no await in it. You might as well leverage it :
exports.load = async function(){
db.set("hello", {
"author": "Some author 1",
"title": "Blog Post 1",
"content": "First post content",
"date_posted": "Dec 17, 2021"
})
let postsArray = new Array();
const keys = await db.list();
keys.forEach(key => {
postsArray.push(`<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="/p">Anonymous</a>
<small class="text-muted">${db.get(key).date_posted}</small>
</div>
<h2><a class="article-title" href="#">${ db.get(key).title }</a></h2>
<p class="article-content">${ db.get(key).content }</p>
</div>
</article`
)
})
const posts = postsArray.join()
fs.writeFileSync("public/posts.ejs", posts)
}
OR with .map() in one line :
exports.load = async function(){
db.set("hello", {
"author": "Some author 1",
"title": "Blog Post 1",
"content": "First post content",
"date_posted": "Dec 17, 2021"
})
const keys = await db.list();
const posts = keys.map( key => `<article class="media content-section">....</article`).join();
fs.writeFileSync("public/posts.ejs", posts)
}
Related
I have been struggling with this for almost a year, my problem is that I can't delete a single object from an array of firebase.
This is the object model:
"gigs": {
"z0Dk4aQodPhJk7DwgJZS0kWYAaH2": {
"-Mk_WtpwUp7LJq7jOzYX": {
"companydescription": "dfsdfsd fsd fsdf sdf s",
"companyname": "sdasdasdas as asdas",
"gigbenefits": "sadsadasd ada ",
"gigdate": "2021-09-26",
"gigdescription": "sd fdsf sdf sdf sdf sdf sf s",
"giglocation": "All we need",
"gigskills": "sadsadasd adasda ",
"gigtitle": "Abraham Felix",
"userID": "z0Dk4aQodPhJk7DwgJZS0kWYAaH2",
"vacanttype": "Contract"
},
"-MkhJdmWIAmat5q0cxCO": {
"companydescription": "adsadsa dasdasda asdasd",
"companyname": "a sadsadasd asd ",
"gigbenefits": "asdasdas ",
"gigdate": "2021-09-28",
"gigdescription": "asdsadas asdasda as",
"giglocation": "asdsada ",
"gigskills": "sdasda asdasd ",
"gigtitle": "adasda da sdsa a",
"userID": "z0Dk4aQodPhJk7DwgJZS0kWYAaH2",
"vacanttype": "Contract"
}
}
},
Currently, as you can see, I have two objects that have a key as the property title nested inside an object that has the user.uid as the property title inside a
gigs object. What I want is to delete a single entry from the user.uid object using a delete button.
I can get the object I want to delete when I click on the delete button.
here is my working code I simplified the template to just show the title of the object :
<template>
<v-container id="tutorials">
<h1>Gigs Available!</h1>
<!-- loop over the tutorials -->
<div
v-for="(gig, key) in authUser.allGigs"
:key="key"
>
<a #click.prevent="deleteGigs(gig)">
<v-icon>mdi-delete</v-icon>
</a>
<h5>{{ gig.gigtitle}}</h5>
</div>
</v-container>
</template>
<script>
import firebase from '#/plugins/firebase'
let db = firebase.database();
//let usersRef = db.ref('users');
let gigRef = db.ref('gigs');
import vue from 'vue'
export default {
name: 'EditGigs',
data: () => ({
authUser: {},
allGigs: null, // initialise an array
}),
methods: {
deleteGigs(gig) {
//.remove()
console.log(gig)
//I get the object I want to delete on the console
}
},
created: function() {
//data => console.log(data.user, data.credential.accessToken)
firebase.auth().onAuthStateChanged(user => {
if (user) {
gigRef.child(user.uid).once('value', snapshot => {
if (snapshot.val()) {
this.allGigs = snapshot.val()
vue.set(this.authUser, 'allGigs' , this.allGigs , snapshot.key )
}
const key = snapshot.key
//console.log(key)
return key
})
}
})
}
}
</script>
I appreciate the help, I just want to understand this to learn more about js and firebase.
Browser Console Error
Uncaught ReferenceError: getNewCars is not defined at HTMLDivElement.onclick
Code Explained
I'm building a car selector form. How it works is there's categories (known as seasons) that can be clicked on to bring up a list of specific cars that pertain to that category (season).
Html
<div class="chooseCar">
<div class="chooseCarTabs">
</div>
<div class="chooseCarWrapper">
<div id="chooseCarSelection">
</div>
</div>
</div>
<script src="/public/theme/scripts/car-selection.js"></script>
car-selection.js
alert('new car-selection js')
let cars;
let seasons = [
{
"name": "Seasonal Cars",
"path": "seasonal.json"
},
{
"name": "Modern Cars",
"path": "modern.json"
},
{
"name": "Classic Cars",
"path": "classic.json"
},
{
"name": "Flag Cars",
"path": "flags.json"
}
];
let seasonTab = "Seasonal Cars";
const chooseCarBody = document.getElementById('chooseCarSelection');
const seasonsTabs = document.getElementsByClassName('chooseCarTabs')[0];
function loadCars(){
chooseCarBody.innerHTML = '';
cars.forEach(car => {
chooseCarBody.innerHTML += `
<div class="singleCar">
<div class="singleCarInner">
<img src="/public/images/avatars/${car.filename}" alt="${car.name}">
</div>
</div>
`
})
}
//Ajax Request
async function setSeasons() {
seasonsTabs.innerHTML = ''
await seasons.forEach(season => {
seasonsTabs.innerHTML += `
<div ${seasonTab == season.name ? 'class="activeSeasonTab"' : ''} onclick="getNewCars('${season.name}', '${season.path}' )">
${season.name}
</div>
`
});
}
//Will be replaced with AJAX Request
async function getNewCars(seasonName, season = seasons[0].path){
cars = null;
await fetch(`/public/data/cars/${season}`)
.then(response => response.json())
.then(data => {
console.log(data)
seasonTab = seasonName;
cars = data; console.log(cars)
})
.catch(error => console.log(error));
await loadCars()
}
async function initData(){
await setSeasons();
await getNewCars(seasons[0].name);
}
initData();
Extra code explanation
let cars; and let seasons work as a state of sorts. When a seasons tab is clicked on an ajax request is sent to get fill the cars state with cars for the category which is then looped through and populated on the page.
My Problem
When I reload the page the cars and category (season) tabs appear on the page just fine including the getNewCars(). But when I go to click on:
<div
${seasonTab == season.name ? 'class="activeSeasonTab"' : ''}
onclick="getNewCars('${season.name}', '${season.path}' )"
>
${season.name}
</div>
I get this error:
Uncaught ReferenceError: getNewCars is not defined at HTMLDivElement.onclick
Note, inline scripts I don't seem to get this error:
<div class="chooseCar">
<div class="chooseCarTabs">
</div>
<div class="chooseCarWrapper">
<div id="chooseCarSelection">
</div>
</div>
</div>
<script> /* All scripts in here*/</script>
How do I fix this and what's going wrong with my code that when import from another js file this happens?
I need to be able to append every even object on the left side of a vertical line, and every odd object on the right side. I am not sure how I can achieve this.
Sample of the JSON, (This is just dummy data for now) (url)
[{
"Year": 2010,
"Title": "Cadillac",
"Description": "Escalade ESV"
},
{
"Year": 1998,
"Title": "Volvo",
"Description": "V70"
},
{
"Year": 1992,
"Title": "Toyota",
"Description": "Camry"
},
{
"Year": 2012,
"Title": "Ford",
"Description": "Explorer"
}]
Heres my code:
fetch(url)
.then(result => {
return result.json();
})
.then (data =>{
console.log(data);
data.forEach( (point) => {
const appendToHtml = `
<div class="container left">
<div class="content">
<h2>${point.Title}</h2>
<p>${point.Description}</p>
<p>${point.Year}</p>
</div>
</div>
<div class="container right">
<div class="content">
<h2>${point.Title}</h2>
<p>${point.Description}</p>
<p>${point.Year}</p>
</div>
</div>
`;
$(".timeline").append(appendToHtml);
});
})
The issue is it append on the left AND the right.
Click here for an image of the issue.
I need to alternate
for example
Cadillac on the left, Volva on the right etc...
I cant seem to figure out what to do..
Thanks in advance!
You can calculate the odd or even of car index and based on that apply class left and right.
data.forEach( (point, index) => {
const isOdd = index % 2;
const appendToHtml = `
<div class="container ${isOdd ? 'right' : 'left' }">
<div class="content">
<h2>${point.Title}</h2>
<p>${point.Description}</p>
<p>${point.Year}</p>
</div>
</div>`;
$(".timeline").append(appendToHtml);
});
Here you go with a solution
fetch(url)
.then(result => {
return result.json();
})
.then (data =>{
console.log(data);
data.forEach( (point, i) => {
const appendToHtml = `
<div class=`container ${i%2 === 0 ? "right" : "left" }`>
<div class="content">
<h2>${point.Title}</h2>
<p>${point.Description}</p>
<p>${point.Year}</p>
</div>
</div>
`;
$(".timeline").append(appendToHtml);
});
})
Use template literal for finding odd even using mod function
I am trying to make a zomato random restaurant generator, so whenever you put your city it gives a random restaurant in that city. I made api for it and it works perfectly, this is a sample output of the api call
{
"name": "Natural Ice Cream",
"url": "https://www.zomato.com/ncr/natural-ice-cream-rajouri-garden-new-delhi?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1",
"location": {
"address": "J 2/10, BK Dutt Market, Rajouri Garden, New Delhi",
"locality": "Rajouri Garden",
"city": "New Delhi",
"city_id": 1,
"latitude": "28.6474674597",
"longitude": "77.1195488423",
"zipcode": "",
"country_id": 1,
"locality_verbose": "Rajouri Garden, New Delhi"
},
"price": 1,
"thumbnail": "https://b.zmtcdn.com/data/pictures/8/313368/da7c191473cdc9701aa97a8cbcd51255.jpg?fit=around%7C200%3A200&crop=200%3A200%3B%2A%2C%2A",
"rating": "4.7"
}
the backend linking frontend to backend looks like this
searchForm.addEventListener('submit', async e => {
e.preventDefault();
resultArea.innerHTML = '';
const query = e.target.querySelector('#restaurant-name').value;
if (query === '') {
return
}
e.target.querySelector('#restaurant-name').value = '';
const res = await fetch(`${hostname}/locations/${query}`, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: 'POST',
})
const json = await res.json();
populateData(json);
});
function populateData(results) {
results.forEach(result => {
const newResult = rTemp.content.cloneNode(true);
newResult.querySelector('.result-title').innerText = result.name;
newResult.querySelector('.result-neighborhood').innerText = result.location.locality;
newResult.querySelector('.result-address').innerText = result.location.address;
newResult.querySelector('.result-price').innerText = '$'.repeat(result.price);
newResult.querySelector('.result-thumbnail').src = result.thumbnail;
newResult.querySelector('.result-website').href = result.url;
resultArea.appendChild(newResult);
});
}
here rTemp is querySelector('template') and resultArea is querySelector('#restaurant-results') and hostname is this.location.origin
And lastly this is the frontend
<body>
<div class="wrapper">
<main>
<h1>Restaurant <span>Random</span></h1>
<form action="">
<div class="form-wrapper">
<label for="restaurant-name">Search</label>
<input name="restaurant-name" type="text" id="restaurant-name" placeholder="City Name">
</div>
<input type="submit">
</form>
</main>
<hr>
<section id="restaurant-results">
</section>
</div>
<template>
<div class="result-card">
<div class="result-header">
<h2 class="result-title">${title}</h2>
<h3 class="result-location result-neighborhood">${neighborhood}</h3>
<h3 class="result-location result-address">${address}</h3>
<p class="result-price">${price}</p>
</div>
<div class="result-body">
<img src="" alt="restaurant-photo" class="result-thumbnail">
</div>
<div class="result-footer">
<button class="result-footer-button">Call</button>
<button class="result-footer-button">Visit Website</button>
<button class="result-footer-button">Make Reservation</button>
</div>
</div>
</template>
<script src="index.js"></script>
</body>
When I run this I get the following error
POST http://127.0.0.1:5500/locations/delhincr 405 (Method Not Allowed)
(anonymous) # index.js:16
index.js:16 is
const res = await fetch(`${hostname}/locations/${query}`, {
and
Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at HTMLFormElement. (index.js:22) which is
const json = await res.json();
I am unable to locate the error. How do I solve these?
I'm building a custom search, as of now if I enter "The R" I get the result list with The Fellow ship of the Ring first, because the phrase "the ring" it's in its .text. I want The Return of the King to be first. Is there a way I can give more relevance to the .name field or sort the match array based on the name .field and the input text?
HTML
<section class="container-fluid px-0 justify-content-center">
<div class="row no-gutters">
<div class="col d-flex justify-content-center search">
<form class="form-inline position-relative">
<input id="search" class="form-control form-control-search" type="text" placeholder="Search..." aria-label="Search">
</form>
<div id="match-list" class="d-none"></div>
</div>
</div>
</section>
JAVASCRIPT
const searchIndex = async searchText => {
const res = await fetch('/data/index.json');
const index = await res.json();
matchList.classList.remove("d-none");
// Get matches to current text input
let matches = index.filter(index => {
const regex = new RegExp(`${searchText}`, 'gi');
return index.name.match(regex) || index.text.match(regex);
});
// Clear when input or matches are empty
if (searchText.length === 0) {
clearSearch();
}
outputHtml(matches);
};
function clearSearch(){
matches = [];
matchList.classList.add("d-none");
}
// Show results in HTML
const outputHtml = matches => {
if (matches.length > 0) {
const html = matches.map(function(match){
return `<a href="${match.url}">
<div class="media mb-2">
<div class="component-icon-slot my-auto" style="background-image: url('/img/${match.url}/icon.png"></div>
<div class="media-body pl-2">
<h3 class="mt-0 mb-0">${match.name}</h3>
<b>${match.type}</b><br/>
<i>Found in <b>${match.product}</b></i><br/>
${match.text}
</div>
</div></a>`
}
}).join('');
matchList.innerHTML = html;
}
};
index.JSON
[
{
"name": "The Fellowship of the Rings",
"type": "book",
"text": "Bilbo reveals that he intends to leave the Shire for one last adventure, and he leaves his inheritance, including the Ring, to his nephew Frodo. Gandalf investigates...",
"url": "books/the-fellowship-of-the-rings",
"product": "Books"
},
{
"name": "The Two Towers",
"type": "book",
"text": "Awakening from a dream of Gandalf fighting the Balrog in Moria, Frodo Baggins and Samwise Gamgee find themselves lost in the Emyn Muil near Mordor and discover they are being tracked by Gollum, a former bearer of the One Ring.",
"url": "books/the-two-towers",
"product": "Books"
},
{
"name": "The Return of the King",
"type": "book",
"text": "Gandalf flies in with eagles to rescue the Hobbits, who awaken in Minas Tirith and are reunited with the surviving Fellowship.",
"url": "books/the-return-of-the-king",
"product": "Books"
}
]
You could map your data to include relevance points:
const index = await res.json();
const searchTextLowercased = searchText.toLowerCase();
const rankedIndex = index.map(entry => {
let points = 0;
if (entry.name.toLowerCase().includes(searchTextLowercased)) {
points += 2;
}
if (entry.text.toLowerCase().includes(searchTextLowercased)) {
points += 1;
}
return {...entry, points};
}).sort((a, b) => b.points - a.points);
This way, you have ranked results in rankedIndex const.
Keep in mind that your code probably needs some refactoring, because you're fetching data on each search. I'm assuming your searchIndex() is called with every key press or something like that.