I've been working on a project recently and it requires me to POST the required name to my profile.js file, but when I do console.log(req.body.bookName) (because the data I'm sending is called bookName), it gives me the error of [Error: Query is required]
Here is the post part of my profile.js code
router.post('/', function(req, res){
if(req.session && req.session.user){
Book.find({ owner: req.session.user.username || req.session.user }, function(err, book){
if(err){
console.log(err);
}
books.search(req.body.search, options, function(error, results) {
if ( ! error ) {
console.log(req.body.bookName);
res.render("profile",{
authenticated: true,
info: books,
results: results
});
}
else {
console.log(error);
}
});
});
}
else{
res.status(401).send('You must be logged in to access this page');
}
})
Here is my button in my .ejs file
<form method="POST">
<input type="text" name="search" placeholder="Search..." required class="searchBook">
<input type="submit" value="Search">
</form>
<% for(var i = 0; i < results.length; i++){ %>
<div class="ui grid">
<div class="column four wide">
<div class="ui card">
<div class="image">
<img src = "<%= results[i].thumbnail %>"/>
</div>
<div class="content">
<div class="header">
<h1 class="ui small header title"><%= results[i].title %></h1>
</div>
<div class="meta">
<p>Author: <%= results[i].authors %></p>
<p>Published on: <%= results[i].publishedDate %></p>
<p>Pages: <%= results[i].pageCount %></p>
</div>
</div>
<div class="content extra">
<button id="detail" class="<%= results[i].title %>">View Detail</button>
<button class="ui button fluid" type="button" name="button">Add</button>
</div>
</div>
</div>
</div>
<div id="modaldiv" class="ui modal" style="position: relative">
<i class="close icon"></i>
<div class="header"><%=results[i].title%></div>
<div class="content"><%=results[i].description%></div>
</div><!-- Should be out side of the book info div -->
<% } %>
And here is my home.js file where I post my data
$(document).ready(() => {
$(document).on("click", "#detail", function () {
$.ajax({
type: "POST",
url: '/profile',
dataType: 'text',
data: { bookName: $(this).attr("class")},
success: function (data) {
location.reload();
alert("done");
}
});
});
});
Does anyone know why this error happens and how I can solve it?
After reading your comment, I found the issue.
What you send to the server is JSON not text.
{ bookName: $(this).attr("class")} is JSON not text. Of course, value of bookName is a text, but whole data is JSON.
you should
$.ajax({
type: "POST",
url: '/profile',
contentType: 'application/json',
data: JSON.stringify({ bookName: $(this).attr("class")}),
success: function (data) {
location.reload();
alert("done");
}
});
I believe you attached bodyParser.json() to express.
Then, console.log req.body on the '/profile' router. You will see body as JSON.
EDIT: one more thing I've found is that you did't send req.body.search
It should be something like JSON.stringify({ bookName: $(this).attr("class"), search: $('.searchBook').val() }) That's why you got Error message.
EDIT2: You are trying to send form and AJAX simultaneously. It doesn't work like that. Just choose one thing. Form or AJAX. I recommend you to use AJAX. Cancel default behavior of form by using e.preventDefault() in $(document).on('click')
Look at your query param. You are passing in this line books.search(req.body.search, if you notice, req.body.searchis the query param but search is not defined in the body that you are posting: { bookName: $(this).attr("class")}, only bookname.
I believe you intend to use: books.search(req.body.bookName....
Update:
So I see you have a form that you post with search. The problem is that when that is posted, req.body.search is defined but not req.body.bookName. When then you click #detail, it is a brand new request where search is not being posted. At that point you will need to grab the value of search and post it as part of the same request.
As individual request, one contains bookName, the other search but with the code in your current state, they aren't posted together as the nodejs endpoint expects it.
Hope this is of help.
Related
I'm currently working on The Odin Project's Library Project, but I'm utilizing MongoDB for my storage.
Currently, there is a field in my database that is called "read" and it's either true or false (as a string, not boolean).
What I want to do is with my addEventListener is to change the value from either true to false or false to true and write that with a put request. It'll then change my ejs file's class and change the color of the book based on true or false.
I've tried different const values, different read values in body, etc. I am assuming something is wrong with my app.put function?
JS:
const readButton = document.querySelectorAll(".change");
Array.from(readButton).forEach((element) => {
element.addEventListener("click", changeRead);
});
async function changeRead() {
try {
const response = await fetch("changeRead", {
method: "put",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
read: "false",
}),
});
const data = await response.json();
console.log(data);
// location.reload();
} catch (error) {
console.log(error);
}
}
Node.js:
side note: I've changed the title: request.body.title to read:request.body.read, same issue.
app.put("/changeRead", (request, response) => {
db.collection("books")
.updateOne(
{ title: request.body.title },
{
$set: {
read: "false",
},
}
)
.then((result) => {
console.log("read changed");
response.json("book status changed");
})
.catch((error) => console.log(error));
});
index.ejs
<% for(let i=0; i<books.length; i++) {%>
<div class="col-sm-6 col-md-4 col-lg-3 text-center">
<div class="card book">
<div class="row">
<div class="col-1 binding p-0 <%= books[i].read %>" id="<%= books[i].read %>"></div>
<div class="col-10 p-0">
<div class="card-body p-4">
<h5 class="cart-title p-2">
<%= books[i].title %>
</h5>
<p class="card-text"><%= books[i].author %>
</p>
<p class="card-text">Pages: <%= books[i].pages %>
</p>
<p class="card-text">Published: <%= books[i].published %>
</p>
<button type="button" class="btn text-center change m-1">Change Read
</button>
<button type='button' class="btn text-center delete my-1">Delete Book
</button>
</div>
</div>
</div>
</div>
</div>
<%}%>
Example MongDB document:
_id : redacted
title : "The Return of the Kings"
author : "J.R.R. Tolkien"
pages : "416"
published : ""
read : "true"
I can post full code too if need be.
I'm trying to implement a follower/following system in Django. In the template, all follow requests have a user and they all have user id's that can be displayed. The template is a profile page that contains several follow requests made by other users. I have created a separate form for each accept/decline and I want to uniquely identify each form so that I can submit that one and remove it subsequently.
<div class="col s12 l6 trending-panel container">
<h4>
Requests
</h4>
<div class="divider"></div>
{% for bond_request in bond_requests %}
{% if bond_request.accepted == False %}
<div>
<div class="row bond-request-row" id="{{bond_request.by_user.id}}">
<div class="col s6">
<a href="{% url 'accounts:profile' bond_request.by_user.username %}">
<div class="col s8">
<img class="profile-image-request" src="https://m.media-amazon.com/images/M/MV5BNjUxNDcwMTg4Ml5BMl5BanBnXkFtZTcwMjU4NDYyOA##._V1_.jpg" alt="">
</div>
<div class="col s4">
<h6 id="follower-username">
#{{bond_request.by_user}}
</h6>
</div>
</a>
</div>
<div class=" col s12 center accept-decline-margin">
<div class="col s12 l6">
<form action="accounts:accept_bond_request" method="POST" id="bond-request-accept-form">
<!-- <a href="#" id="bond-request-accept" class="green-text white btn">
<div>
<ion-icon name="add"></ion-icon>
<span>Accept</span>
</div>
</a> -->
<button id="bond-request-accept" class="green-text white btn" type="submit">Accept</button>
</form>
</div>
<div class="col s12 l6">
<a href="" class="grey-text white btn">
<div class="">
<ion-icon name="remove"></ion-icon>
<span>Ignore</span>
</div>
</a>
</div>
</div>
</div>
<!-- HERE -->
</div>
{% else %}
{% endif %}
<div class="divider">
</div>
{% endfor %}
</div>
$("#bond-request-accept-form").on('submit',function(){
// Cleans the username
// var each_bond_request = $();
var raw_follower_username = $("#follower-username").text().trim();
var follower_username = raw_follower_username.replace("#","");
console.log("Follower username: ",follower_username);
$.ajax({
type: "POST",
url: "/accounts/user/" + follower_username + "/accept_request",
data:{
"follower_username" : follower_username,
},
success: function(data){
console.log(data);
M.toast({html: follower_username + ' started following you',classes: 'green'}, );
},
error: function(data){
console.log("All error data: ",data);
M.toast({html: 'Failure',classes: 'red'}, );
},
});
});
You should create a standalone function to handle submit. And reference this function in each form you created.
function SubmitHandler (e) {
// Cleans the username
// var each_bond_request = $();
var raw_follower_username = $(e).find("#follower-username").text().trim();
var follower_username = raw_follower_username.replace("#","");
console.log("Follower username: ",follower_username);
$.ajax({
type: "POST",
url: "/accounts/user/" + follower_username + "/accept_request",
data:{
"follower_username" : follower_username,
},
success: function(data){
console.log(data);
M.toast({html: follower_username + ' started following you',classes: 'green'}, );
},
error: function(data){
console.log("All error data: ",data);
M.toast({html: 'Failure',classes: 'red'}, );
},
});
return false;
}
Then in your template:
...
<form id="bond-request-accept-form" onsubmit="SubmitHandler(this)">
...
Note the #follower-username should be nested within the form tag for jQuery to find the correct one.
First, I just want to say that I may be understanding your question wrong. If so, feel free to correct me.
If I am understanding this right, you have multiple copies of essentially the same form with slight variations depending on the user that is sending the request. Since IDs are meant to be unique and can cause issues in JavaScript if there are more than one instance of them, I would change the bond-request-accept-form to a class rather than an ID, and do something like this in JQuery:
$(".bond-request-accept-form").toArray().forEach(function(elem){
$(elem).on('submit', function(){
// Logic to perform when the form is submitted
});
});
Put different URLs in the action for the two forms. Then you'll have two different view functions to deal with the two different forms.
I have a list of resources which needs to be filtered based on the location. I have a form to filter and on click of a button, the data is filtered based on the location. I have an AJAX request and it sends a post request to /filterresources and the data matching that criteria is also fetched from the db and the resourcefilter.ejs is rendered using res.render() as given below:
resourcefilter.js:
router.post('/filterresources',function(req,res,next){
var category = req.body.category;
User.find({_id: {$ne: req.user._id}},(err,user) => {
if(err) throw err;
if(user)
{
db.findAll(Resource,{category:category})
.then(function(data){
res.render('resourcefilter',{title:"Steel Smiling",user:req.user,header:true,navbar:true,resources:data});
})
.catch(function(err){
next(err);
});
}
else {
throw(err);
}
});
});
The problem here is, as the records are fetched the UI doesn't get updated even when new ejs page is called. It still retains the previous page UI. But any console.log() statements in the new ejs page gets displayed.
resourcefilter.ejs: All console statements in this get printed without any issues but UI is not refreshed. Any help is much appreciated.
<% layout('layout/layout') %>
<div class="container user-form py-5">
<br>
<%if(user.role == 'Administrator'){ console.log(user.role);%>
<a href="/resourceupload" class="btn btn-outline-primary" style="float: right" ><span>Create Resource</span></a>
<%}%>
</br>
<span class="site-logo my-3">Our Resources</span>
<div class="col-12 col-lg-4 offset-lg-2" style="margin-left: 33%">
<form id="filter-resources" class="mt-5">
<div>
<select class="custom-select" name="category" id="category">
<option selected>Select a location:</option>
<option value="Pittsburgh">Pittsburgh</option>
<option value="Allegheny County">Allegheny County</option>
<option value="Pennsylvania">Pennsylvania</option>
<option value="Outside Pennsylvania">Outside Pennsylvania</option>
</select>
<input class="filter" name="filter-resources" type="submit" value="Filter">
</div>
</form>
</div>
</form>
<div class="container" style="margin-top: 2%;">
<div class="row">
<% for(var i=0;i<resources.length;i++){ console.log("Hello"+resources.length); %>
<div class="col-xs-12 col-sm-6 col-md-4">
<div class="image-flip" ontouchstart="this.classList.toggle('hover');">
<div class="mainflip">
<div class="frontside">
<div class="card-custom">
<% console.log("Image"+resources[i].image);%>
<div class="card-body text-center">
<img src="<%= resources[i].image %>" alt="" class="img-resources">
<div class="card-title"><b><%= resources[i].name%></b></div>
<div id="greetings" class="card-title"><textarea readonly class="resourceDesc"><%= resources[i].description%></textarea></div>
<a href = <%= resources[i].website%> id="singlebutton" name="singlebutton" class="btn btn-primary">
Read More!</a>
<br></br> </div>
</div>
<br></br>
</div>
</div>
</div>
</div>
<% } %>
</div>
</div>
</div>
AJAX function to call to /filterresources:
function filter_resources(e) {
e.preventDefault();
var category = $('#category :selected').text();
console.log(category);
const button = this.children[this.children.length - 1];
//Form Handling with ajax
$.ajax({
url: '/filterresources',
type: 'post',
data: {category: category},
dataType: 'json',
});
function refreshDiv() {
document.getElementById("getelebyid").innerHTML = "Some <strong>HTML</strong> <em>string</em>" ;
}
}
Your ejs, js and html code are correct, the problem is that your AJAX function does not refresh the page's content, it only retrieves the content. There are 2 solutions: Either, in the EJS, change from "render" to "send" and then in the AJAX callback use the value returned as innerHTML for some element, or do a form submit, and not a jquery post. The form submit will cause a page reload.
If you don't have any errors from your server you can do a workaround with the front end:
$.ajax({
url: '/filterresources',
type: 'post',
data: {category: category},
dataType: 'json',
}).then(() => location.reload());
That will refresh your page when the request finishes.
location.reload() didn't work in this context because the filtered data needs to be passed on to the page. Hence, instead of using res.render(), i used res.send as suggested. Please find the below code:
filterresources.js
router.post('/filterresources',function(req,res,next){
var category = req.body.category;
User.find({_id: {$ne: req.user._id}},(err,user) => {
if(err) throw err;
if(user)
{
var user = req.user._id;
console.log(user);
db.findAll(Resource,{category:category})
.then(function(data){
res.send({msg: data, success: true,user: user });
})
.catch(function(err){
next(err);
});
}
else {
throw(err);
}
});
});
AJAX function:
function filter_resources(e) {
e.preventDefault();
var category = $('#category :selected').text();
console.log(category);
const button = this.children[this.children.length - 1];
//Form Handling with ajax
$.ajax({
url: '/filterresources',
type: 'post',
data: {category: category},
dataType: 'json',
success: function (response) {
if (!response.success) {
window.alert(response.msg);
}
if (response.success) {
var resource = response.msg;
var userInfo = response.user;
$('#resfilter').html(""); // reset the contents in the div
var html = `<div class="row">`;
for(var i=0;i<resource.length;i++) {
html += `<div class="col-xs-12 col-sm-6 col-md-4">
<div class="image-flip" ontouchstart="this.classList.toggle('hover');">
<div class="mainflip"> <div class="frontside"> <div class="card-custom">
<div class="card-body text-center">`;
html += `<img src="${resource[i].image}" alt="Mental Health Resource" class="img-resources">`;
html += `<div class="card-title"><b>${resource[i].name}</b></div>`;
html += `<div id="greetings" class="card-title"><textarea readonly class="resourceDesc">${resource[i].description}</textarea></div>`;
html += ``;
html += `Read More!`;
html += `<br>`;
html += `</br></div></div><br></br></div></div></div></div>`;
}
html += `</div></div></div></div>`;
}
document.querySelector('#resfilter').innerHTML = html; // add the html content to the div which was earlier reset
}
})
}
I am using AJAX and Handlebars to get data from my MongoDB without refreshing the page and represent it in an easier manner.
I have created a div with a search button where a user places his/her inputs. These inputs need to passed to my routes and then the results from my DB should be sent back from my routes, to the handlebar which will help me display the products.
index.hbs
<div id="search-form">
....
<input id="bhkInput" name="bmin" type="number">
<input id="priceMin" name="pf" type="number">
<input id="priceMax" name="pt" type="number">
<input type="submit" class="btn searchButton" id="submit" value="Search" onclick="showResults()">
</div>
<div class="row col-sm-12 searchResults" id="sr">
//Results need to be displayed here
</div>
<script id = "result-template" type="text/x-handlebars-template">
{{#each searchResults }}
<div class="row">
{{#each this }}
<div class="col-sm-6 col-md-4">
...
<img src="{{this.imagePath}}" alt="..." class=" card-img-top">
<div class="card-body">
<h4>Flat No: {{this.flatNo}}</h4>
...
<p class="description">
{{this.description}}
</p>
...
...
</div>
</div>
</div>
</div>
{{/each}}
</div>
{{/each}}
</script>
<script>
function showResults(){
let bmin = document.getElementById('bhkInput').value;
let pf = document.getElementById('priceMin').value;
let pt = document.getElementById('priceMax').value;
$.ajax({
type: 'GET',
data: {
bmin: bmin,
pf: pf,
pt: pt
},
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: "/results",
success: function(data){
let source = $('#result-template').html();
let template = Handlebars.compile(source);
let html = template(data);
$(".searchResults").innerHTML = html;
}
});
}
The issue that I am facing:
When I send back the results from my DB, I am unable to display it using Handlebars.
How do I handle the data after success function is called. I want the results to be appended in the 'searchResults' div. I went through this link, however I am yet not able to see the data of my results.
Update
I took dummy data from the Handlebars Docs and used it for my template and yet there is no data being appended in the html
The console.log(html) returns the template without the new data items
index.hbs
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
</div>
</script>
<script>
....
$.ajax({
....
success: function(data){
var source = document.getElementById("entry-template").innerHTML;
var template = Handlebars.compile(source);
var context = {title: "My New Post", body: "This is my first post!"};
var html = template(context);
console.log(html);
....
</script>
Output for console.log(html)
<div class="entry">
<h1></h1>
<div class="body">
</div>
</div>
For the first issue. Your method is GET so I think you can get it by req.query.productId.
For the second issue. You should append your data in ajax success callback function, because ajax is asynchronous. For example:
$.ajax({
type: 'GET',
data: encodeURIComponent(searchObject),
url: "/results",
success: function(data){
console.log(data);
let sr = document.getElementById('sr');
$(sr).append();
}
});
How to get "data" from JQuery Ajax requests
I'm learning laravel 5 about one month, and now i having a problem with javascript.
I add an form click on blade file to delete the post.
But now i don't want to use form, i replace that by javascript.
How can i detect when use touch the delete'button.
#extends ('layouts.master')
#section ('head.title')
Blog
#stop
#section ('body.content')
<div class="container">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
</div>
</div>
<form class="form-show">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<h2> {{ $article->title}} </h2>
<p> {{ $article->content}} </p>
</div>
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
Cập nhật
<button type="submit" class="btn btn-primary">Xoa</button>
</div>
</div>
</div>
</form>
</div>
<script src="jshow.js"></script>
#stop
Add id to button so it is easily locatable in Javascript
<button id="delete-button" type="submit" class="btn btn-primary">Xoa</button>
Next add this javascript
var deleteButton = document.getElementById("delete-button");
deleteButton.onclick = function() { delete(); return false; }
Process the deleting in delete() method
Try this :
<!DOCTYPE html>
<html>
<body>
<button onclick="myFun()">Click me</button>
<p id="demo"></p>
<script>
function myFun() {
console.log('Clicked');
}
</script>
Since HTML forms can't make PUT, PATCH, or DELETE requests, you will need to add a hidden _method field to spoof these HTTP verbs
Also, don't forget to add csrf-token as well which is needed in order to validate a POST request.
First add this meta tag inside your <head>.
<meta name="csrf-token" content="{{ csrf_token() }}">
Then place this code at the top of your JS file.
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Now, you can make use of data-* attribute to hold the route of that article for AJAX.
<button data-route="{{ route('article.destroy', $article->id) }}" class="btn btn-danger deleteArticle">Delete</button>
In your JS
document.querySelector('.deleteArticle').addEventListener('click', function() {
var route = this.dataset.route;
if (confirm("Are you sure you wish to delete this article?")) {
$.ajax({
method: 'POST',
url: route,
data: {
"_method": 'DELETE',
},
success: function() {
// handle success here
},
error: function() {
// handle error here
},
});
}
});