how to handle multiple parameters in express routes - javascript

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.

Related

Making a sort by and load more system (NodeJS routes + MySQL)

I want to make a system that limits the number of posts that get displayed and a load more button that loads them from where the limit stoped previously WITH the capability to change the ordering of those same posts.
Right now I have:
html/ejs:
<main>
<div class="container">
<%
if(results.lenght != 0){
var i = 1;
results.forEach(function(results){
%>
<div class="post">
<div class="op"><%= results.username %></div>
<h2 class="post-title"><%= results.title %></h2>
<div class="content"><p class="post-content"><%= results.content %></p></div>
</div>
<% i++; }) %>
<% } else { %>
locals.message1 = 'No posts found :(';
<% } %>
</div>
<div class="load-container"><a class="load" href="">Load more</a></div>
</main>
dropdown to select sorting:
<div class="drop">
<button onclick="drop()" class="drop-btn">Sort by</button>
<form id="dropdown-content" class="dropdown-content" method="POST">
<button type="submit" formaction="/" value="1">Newest</button>
<button type="submit" formaction="/" value="2">Oldest</button>
<button type="submit" formaction="/" value="3">Popular</button>
</form>
</div>
routes:
router.get('/', authController.isLoggedIn, (req, res, next) => {
sql = 'SELECT posts.username, time, title, content, user_file, audio FROM posts JOIN user on posts.user_id = user.id';
db.query(sql, function(err, results, fields){
if(err) throw err;
else if(results.length!=0){
res.render('index', {
user: results: results, time: moment.utc(new Date(results.time)).fromNow()
});
}
else {
res.render('index', {
user: message: 'Sorry, we don\'t have any posts :(', message1: 'Very sad...', results: results
});
}
})
});
I was thinking of having two variables in the route that would store the limit. Sort of like this:
var limit1: 0; // where to start displaying posts
var limit2: 8; // how many to display before stopping
// ADD 8 to both when button is clicked
var limit = 'LIMIT ' + limit1 + ', ' + limit2 + ';' //combining it for LIMIT in MySQL
db.query(sql + limit, function(err, results, fields)...
The problem is that I don't know how to keep track of how many I've already loaded and how to pass that data from the load more button in the EJS file to the router. The only way I currently know how to do is with a post form but I'm guessing that wouldn't be good at all.
For the sorting I would want to do basically the same thing:
// when new sorting is selected
var sorting = sorting; //selecting a sorting algorithm based on the button clicked
// reset the limits when new sorting is selected
limit1 = 0; limit 2 = 8;
db.query(sql + sorting + limit, function(err, results, fields)...
But again I don't know how I would tell the route about the changes and how I would store the limits... I've tried some things but I couldn't get the variable from the EJS on button click to the route('/'... So if I only got one thing out of this I would want it to be the variable passing.
I'm assuming you are brining back some results with the page load first correct?
With that assumption you have your main loop to display the posts
<main>
<div class="container">
<%
if(results.lenght !== 0){
results.forEach(results =>{
%>
<div class="post">
<div class="op"><%= results.username %></div>
<h2 class="post-title"><%= results.title %></h2>
<div class="content"><p class="post-content"><%= results.content%>
</p></div>
</div>
<%}) %>
<% } else { %>
No posts found :(
<% } %>
</div>
<input type="hidden" id="postCount" value="<%=results.length%>">
<div class="load-container"><a class="load" href="">Load more</a></div>
</main>
(slight edits made to use arrow function, also not sure what the count was for? But its not really needed from what I can tell, if you needed the count you could always just use the length of the results as they would be the same.)
Now, you need an api route of some kind to pass the update variables to so you can make the request. It will be similar to the original route, so in the same route file you would have something like
router.get('/update/:sort/:start', authController.isLoggedIn, (req, res, next) => {
// place relevent SQL code here that uses the passed params
// then send back JSON object back to the browser
res.json(jsonresponse)
});
So, in this sample, you would be passing the sort and start argument as part of an XHR/Fetch request, as you can see above, we have placed a hidden variable with the length of the original request so we know how many were returned, we will pass that as the 'start' value to the update endpoint and tell SQL to start at that record
(Note, you could extend that route to have other limits passed etc, Also doing dynamic queries like this can open you up for a SQLinjection so you will need to sanities those before passing them to the SQL Query)
From here you will have a change event handler on your page, that will listen for change events on the dropdown, from there you will take the value of that and pass that to an XHR request that hits the api endpoint with the passed variables, ones it returns the JSON object you will simply loop over it and append to the screen, once the loop is done, grab the total from the hidden object and add the total from the returned object so the next request knows where you left off (more for the load more option assuming)

NodeJS Pass data from current page to another

I want to make a User management system in Node. I have a page where all users are listed in a table. After I press the edit button, it should go to the user page with all the information of that user. But I don't know how to pass the username into my Node function.
This is my table for the users. the Benutzerverwaltung_Mitareiter is the page where the information from the user is getting shown
<tbody>
<%for (var i = 0; i < users.length; i++) { %>
<tr>
<td><%=users[i].username%></td>
<td><%=users[i].admin%></td>
<td>
<form action="/users/Benutzerverwaltung_Mitarbeiter">
<button id="buttonAnzeigen"
type="submit"
name="username"
href=""
value="<%users[i].username%>"
data-toggle="tooltip"
title="Anzeigen">
<i class="fas fa-search"></i>
</button>
</form>
</td>
</tr>
<%}%>
</tbody>
I passed the username of the user into the value of the button.
And this is how I get the data for the table
router.route ('/Benutzerverwaltung_Mitarbeiter').get(function (req, res) {
const username = req.body;
User.find(function (err, users) {
if (err)
return res.send(err);
res.render('Benutzerverwaltung_Mitarbeiter',{
users: users || [],
});
});
});
If someone can help me to get the function, I would be very thankful!
I suggest you to use some template engine, like ejs or jade. This form, you can use res.render and send json information for view. For more details consult the engine docs and res.render doc too.

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 access the parameters in the current url in the backend express function? (see description)

Alright so I can't figure out how to explain this. Basically I am writing a simple express app that requests the omdb api to search for a movie title and return the results. Now the omdb api send back the results as pages with 10 results per page.
This is my app.js file
let express = require("express");
let app = express();
let request = require("request");
app.set("view engine", "ejs");
let query = "";
let page = 1;
app.get("/", (req, res) => {
res.render("search");
});
app.get("/results", (req, res) => {
if(req.query.search){
query = req.query.search;
}
if(req.query.page){
page = req.query.page;
}else{
page = 1;
}
console.log(query);
console.log(page);
let url = "http://omdbapi.com/?apikey=thewdb&s=" + query + "&page=" + page;
request(url, (error, response, body) => {
if(!error && response.statusCode == 200){
let data = JSON.parse(body);
res.render("results", {data: data});
}
});
});
app.listen(process.env.PORT, process.env.IP, () => {
console.log("Server Started");
});
This is my search.ejs file
<h1>Search for a Movie</h1>
<form action="/results" method="GET">
<input type="text" placeholder="search term" name="search">
<input type="submit">
</form>
And this is my results.ejs file
<h1>Results of Search</h1>
<ul>
<% data["Search"].forEach((movie) => { %>
<li>
<strong><%= movie["Title"] %></strong> - <%= movie["Year"] %>
</li>
<% }); %>
</ul>
<form action="/results" method=GET>
<input type="text" name="page" placeholder="Page">
<input type="submit">
</form>
Search Again
This works where the user can type the page number in the results page and go to the next page. However, what happens is when I send back the page number, the search query is lost. Right now my solution is to define a global variable and keep track of the query like that, but is there a proper way to do this?
I have recently started learning backend development so I am very new to express and node.
Right now my solution is to define a global variable and keep track of the query like that
Don't do that.
You'll get race conditions. And cross-user pollution.
Just put the query in the form.
<input type="hidden" name="query" value="...">

How to pass variable from Jade to .JS file?

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.

Categories

Resources