How to pass variable from Jade to .JS file? - javascript

In my code below I have created an array of items in my .JS file. I was then able to pass this array to the .Jade and use each value in the array as an item in a dropdown list. I now want to pass the user input of which item they will click in the dropdown back to the server side (.js) so that I can use the user input to find more data.
My problem is that I don't know how to send the .jade variables to the server side. I want to send the "this.selectedIndex"/selected "val" so I can use it as a variable in the javascript file.
.JS
router.get('/', function(req, res) {
var projectPathArray = [];
async function main() {
var projects = await _db.listProjects();
projects.forEach(async (item) => {
var pathy = item.path;
projectPathArray.push(pathy)
})
res.render('index', { title: 'Projects', projectPathArray:projectPathArray});
}
main();
.jade
extends layout
script(src="libs/jquery-1.11.3.min.js")
link(rel='stylesheet', href='/stylesheets/style.css')
block content
h1= title
p To start, please select a project
html
body
form#test-form(action='', method='get')
select#menu1(name='menu1', size=projectPathArray.length)
each val in projectPathArray
option=val

Without understanding exactly what you want this should at least get you closer to what you are asking for.
1) Add the route to handle the post where you can retrieve the values posted back in the form using req.body.
2) In your Pug/Jade template I indented the form elements so they are under the form, added a submit button, and changed the method of the form to post.
.JS
router.post('/', function(req, res) {
console.log(req.body);
res.redirect('/');
});
router.get('/', function(req, res) {
var projectPathArray = [];
async function main() {
var projects = await _db.listProjects();
projects.forEach(async (item) => {
var pathy = item.path;
projectPathArray.push(pathy)
})
res.render('index', { title: 'Projects', projectPathArray:projectPathArray});
});
main();
.jade
extends layout
script(src="libs/jquery-1.11.3.min.js")
link(rel='stylesheet', href='/stylesheets/style.css')
block content
h1= title
p To start, please select a project
html
body
form#test-form(action='', method='post')
^
select#menu1(name='menu1', size=projectPathArray.length)
each val in projectPathArray
option=val
button(type='submit') Submit

You will need to use some mechanism for communicating from the frontend back to the server. This includes, but is not limited to, websockets and/or AJAX.

Related

Appending fetched ejs items

Objective:
I have a button that runs a function to load more items from my Mongoose DataBase and add them to a table row. I use get to get and return data from my server side. And am following pointers from this post, but I am still unable to render what I need.
Client side code:
<main role="main">
<div class="container-fluid ">
<div class="card-columns" id="test" value="<%=deals%>">
<tbody>
<tr id="content"><% include partials/row.html %></tr>
</tbody>
</div>
</div>
<div class="container" style="max-width: 25rem; text-align: center;">
<a id="8-reload" class="btn more" onclick="loadMore(8)"></a>
</div>
<script >
const loadMore = async function(x){
const response = await fetch(`/${x}`);
if (!response.ok)
throw oops;
const data =await response.text();
console.log(data);
console.log($("#content"));
await document.getElementById('content').insertAdjacentHTML('beforeend',data);
}
</script>
Server JS request:
app.get(`/:count`, (req, res) => {
const count = parseInt(req.params.count);
database.find({}, (err, found) => {
if (!err){
res.render("more",{items:found},(err,html)=>{
if(!err){
res.send(html);
}else{
console.log(err);
}
});
} else {
console.log(err);
}
}).skip(count).limit(25);
});
when running the function nothing happens and browser console log reads the long string of html. and this for the place I want to append to:
jQuery.fn.init {}
proto: Object(0)
No errors on server console log. What am I missing?
UPDATE I tried Appending instead of to content to test and lo and behold I AM appending html, just not all of my content. int only inserts the opening and closing of the entire html template none of the content between it. Weird.
Okay looks like the big problem was two things. The first issue was I was appending to a place that the content couldn't append to. The second issue was My template started with Table rows with more content in them which would not allow other stuff to render. Once I moved my jquery to a parent id and removed the table rows everything came in fine!

Update existing list without reloading entire page

I'm figuring out what's the best way to update a current list of results from an API call, with a new list of results from an API call.
I'm making API request to news API and loading them into the index page when it first loads:
app.get("/", function (req, res) {
request("https://newsapi.org/v2/top-headlines?q=" + initialQ + "&category=sports&pageSize=10&page=" + page + "&sortBy=relevance&apiKey=" + apiKey, function (error, response, body) {
if (!error && response.statusCode == 200) {
let data = JSON.parse(body);
totalResults = data.totalResults;
console.log(totalResults)
let articles = scripts.articlesArr(data);
let filteredArticles = scripts.filteredArr(articles);
res.render("index", { filtered: filteredArticles });
} else {
res.redirect("/");
console.log(response.body);
}
});
});
Then the user will toggle two buttons to get more results, or go back a page:
app.post("/", function (req, res) {
let inputValue = req.body.page;
let pages = Math.ceil(totalResults / 10)
page = scripts.iteratePages(inputValue, page, pages);
request("https://newsapi.org/v2/top-headlines?q=" + initialQ + "&category=sports&pageSize=10&page=" + page + "&sortBy=relevance&apiKey=" + apiKey, function (error, response, body) {
if (!error && response.statusCode == 200) {
let data = JSON.parse(body);
let articles = scripts.articlesArr(data);
let filteredArticles = scripts.filteredArr(articles);
res.render("index", { filtered: filteredArticles });
} else {
res.redirect("/");
console.log(response.body);
}
});
});
I'm aware of Socket io, but I was wondering if there are other means or methods of achieving this? From what I understand, I can update frontend content via the front end - but with my current set up I'd much prefer to update from the back end
EJS code:
<div id="container">
<% for(var i=0; i < filtered.length; i++) { %>
<ul>
<li><%= filtered[i].title %></li>
<li><%= filtered[i].date %></li>
<li><img src="<%= filtered[i].image%>" /></li>
<li><%=filtered[i].description%></li>
<li><%= filtered[i].link %></li>
</ul>
<% } %>
</div>
<form action="/" method="POST">
<ul>
<li>
<button type="submit" name="page" value="next">Get more results</button>
<button type="submit" name="page" value="prev">Go back a page</button>
</li>
</ul>
</form>
For bi-directional communication we can use WebSockets (with a library like Socket.IO), for uni-directional server-to-client we can use EventSource, and for uni-directional client-to-server we use good ol' HTTP, through fetch or XMLHttpRequest in the browser API (this is referred to as AJAX, though I think most devs just says "client calls the server" these days). For 99% of use cases what we want is client-to-server over HTTP. If I understand correctly then you want stuff to happen when the users pushes a button. That's a case of client-to-server.
User pushes button
Client calls our new API endpoint /articles with fetch to get more articles: const data = await fetch('localhost:8080/articles'); const articles = await data.json(). A simplified version of the code for /articles looks something like app.get('/articles', (req, res) => request("https://newsapi.org").then(articles => /* do stuff with articles here */res.send(result)). This end point returns json instead of html (which our / endpoint returns)
Our server calls newsapi. Newsapi anserrs our server. Our server answers the client.
Then we need some data binding/templating that ensures that the DOM is updated with the new articles. This is functionality that libs like React and Angular supply. But for learning purposes and to keep things simple you can do something like articles.forEach(a => {const el = document.createElement('li'); el.innerHtml = a; document.getElementById('articles').appendChild(el)}), assuming a tag <ul id="articles">... where articles are supposed to show up exists (you probably want to do something more complex with your articles, but you get the idea)
Page hasn't reloaded 🙌
Update: some code review :)
use template literals. "https://newsapi.org/v2/top-headlines?q=" + initialQ + "&category=sports&pageSize=10&page=" + page + "&sortBy=relevance&apiKey=" + apiKey -> https://newsapi.org/v2/top-headlines?q=${initialQ}&category=sports&pageSize=10&page=${page}&sortBy=relevance&apiKey=${apiKey}
Prefer const over let
Use new lines when you're lines get very long (many go by 80 columns as preferred max width)
It looks like you do one ul for each article and one li for each property on the article. ul is a list (unordered list) and li is a list item. So one ul should contain many li, and each li should contain one item (in this case an article). You can read more about semantics in web development here

how to handle multiple parameters in express routes

I am developing a website using express and routing to handle http requests.
I am populating my html div elements using handlebars:
<div class="popup center" style="height: 15em;">
<h3 name="name" id="name">{{ active_name }}</h3>
<p name="description" id='description'>{{ active_desc }}</p>
</div>
Now I let the user to modify that elements using contenteditable="true".
With a button I would like to call a route passing all the fields that has been modified.
For now I was able to pass only one element (i.e. {{session.recipe}} that is the id of the recipe) but I would like to handle the modified name and description too.
<a href="/save-recipe/{{session.recipe}}" style="color: black;">
<button name="button" class="greyishBackground width45 right">
<p>Save</p>
</button>
</a>
In the index.js I'm doing this:
router.get('/save-recipe/:id/', function(req, res, next) {
var recipe_id = req.params.id;
console.log(req.body.name); // doesn't work - undefined
}
Since req.body.name is not working (it is undefined) I'm trying to find a way to pass to the route more parameters in order to handle them in the same way as I handled the recipe_id (i.e. req.params.id).
Can someone explain me how to do it?
Can I creat an object with name and description fields and pass only the object? How can I do it?
Or there is a way to make that req.body.name work?
router.get('/save-recipe/:id/:name/:desc', function(req, res, next) {
var recipe_id = req.params.id;
var recipe_name = req.params.name;
var recipe_name = req.params.desc;
}
req.body.name is undefined because you are not posting anything to the server.

Updating text in markdown

Coding a blog website, using SimpleMDE (Markdown editor) for writing Markdown.
In app.js ,
//EDIT BLOG - FORM
app.get("/blog/:id/edit", function(req,res) {
Blog.findById(req.params.id, function(err, foundBlog) {
if(err) {
res.redirect("/blog");
} else {
res.render("editBlog", {blog : foundBlog});
}
})
})
//UPDATE BLOG
app.put("/blog/:id", function(req,res) {
req.body.blog.body = req.sanitize(req.body.blog.body);
var id = req.params.id;
Blog.findByIdAndUpdate(req.params.id, req.body.blog,{new: true}, function(err,updatedBlog) {
if(err) {
res.redirect("/blog");
} else {
res.render("showBlog", {blog : updatedBlog});
})
On using SimpleMDE, it works fine.
But on clicking the "Edit" button, the changes are saved as such:
In editBlog.ejs,
<div class="field">
<label>Text</label>
<textarea id="MyID" type="text" name="blog[body]"><%= blog.body %> </textarea>
</div>
In showBlog.ejs,
<div id="main-blog-content"><%- blog.body %></div>
Am I missing something here?
SimpleMDE is designed for writing Markdown. As such, the editor will provide Markdown when a form is submitted or when the content is accessed. If you're looking to convert it to HTML, you'll need to do so with a parsing library. SimpleMDE uses marked, if you want to maintain consistency with the preview window.
Here's how to use SimpleMDE's built-in parser to convert Markdown to HTML:
var simplemde = new SimpleMDE();
var html = simplemde.markdown("**Example** text");

onsubmit breaks Jade view

I have a form page that takes some inputs, stores the data in mongo and returns the data to the view underneath the form. However, when a user submits new data on the form, the jade view crashes and displays the following error:
TypeError: /Users/rhysedwards/Downloads/insightful/food/views/index.jade:29
27| label
28| input.btn.btn-default(name='submit', type='submit', onsubmit="this.submit(); this.reset(); return false;")
> 29| each Entry, i in entries
30| div.title= Entry.title
31| div.url= Entry.url
32| div.selectedDate= Entry.selectedDate
Cannot read property 'length' of undefined
The expected behaviour is that when a user submits new data through the form, it displays underneath the form. Why does Entry become undefined onsubmit but the data still displays prior to a submit?
Router
router.get('/', function(req, res, next){
Entry.find({}, function (err, entries) {
res.render('index', {
"entries": entries
});
});
});
Jade
block content
.container
h1 London Plans
form(method='post' action='post', class='plans', id='plans')
.form-group
label Do you have a link?
input.form-control(name='search', id='search' type='url', required='required')
h2#title
.form-group
label What looks cool?
input.form-control(name='title', type='text', required='required' class='title')
.form-group
label When is it?
label
input(type='checkbox' name='week' value='week')
span This week
label
input(type='checkbox' name='month' value='month')
span This Month
label
input(type='checkbox' id='date')
span Date
label
input(type='textbox' class='datepicker' name='date' value='')
label
input.btn.btn-default(name='submit', type='submit', onsubmit="this.submit(); this.reset(); return false;")
each Entry, i in entries
div.title= Entry.title
div.url= Entry.url
div.selectedDate= Entry.selectedDate
Post function
router.post('/post', function(req, res, next) {
var url = req.body.search;
var title = req.body.title;
var week = req.body.week;
var month = req.body.month;
var date = req.body.date;
console.log(url + ' ' + title + ' ' + week + ' ' + month + ' ' + date);
//FIND WHICH DATE WAS SELECTED BY USER AND ASSIGN THAT TO selectedDate
if (typeof week != 'undefined' ){
var selectedDate = 'week';
} else if (typeof month != 'undefined') {
var selectedDate = 'month';
} else {
var selectedDate = date;
};
//CREATE NEW OBJECT
var data = new Entry ({
url: url,
title: title,
selectedDate: selectedDate
})
//STORE NEW OBJECT TO THE DB
Entry.createEntry(data, function(err, entry){
if (err) throw err;
console.log(entry);
})
//RENDER THE HOMEPAGE TO CLEAR THE FORM
res.render('index');
return false;
});
The reason why entries is undefined upon resubmission is because in your router.post handler you're not supplying the Jade template with a value for that variable.
Here is the code in your router.post handler which causes this:
res.render('index'); // telling the page to render without variables
Now, compare this to the successful code in the router.get handler:
res.render('index', {
"entries": entries // here is where you assign variables for Jade
});
As you can see, in the router.get handler you're supplying the Jade template engine with a value for the "entries" variable with entries, which is a value returned by the call you make to mongodb.
It might be worth having a look at the express docs for res.render() for more information. Here's the excerpt in question which sheds some light on the situtation:
res.render(view [, locals] [, callback])
Renders a view and sends the rendered HTML string to the client.
Optional parameters:
locals, an object whose properties define local variables for the
view.
From the definition of the locals argument, you can see where you went wrong! Happy coding :)

Categories

Resources