I've been trying to implement some searching features. I need the program to read search area and redirect the user to the page with results. Here's my search.ejs
<script>
function executeSearch($this) {
console.log('button clicked');
let request_data = document.getElementById('search-area').value;
console.log("Request data : " + request_data);
$.post("search", {subtitle: request_data}, function(json) {
console.log('requested access completed');
})
}
</script>
<input id="search-area" type="search" value="" placeholder="Enter movie title" />
<button onclick="executeSearch(this)" class="details-button">Search</button>
<ul class="search-list">
<% for(let i=0; i< movieList.length; i++) { %>
<li class="single-search-result">
<h3 class="text-light">
<%=movieList[i].title%>
</h3>
<br>
</li>
<% } %>
</ul>
Here's the code handling the request :
app.post('/search', (req, res) => {
movie_controller.getBySubTitle(req.body.subtitle)
.then(result => {
res.render('search', {
movieList: result
});
})
.catch(err => {
console.log(err);
res.render(500);
})});
By using console.log, I've determined that there are no issues in transferring required data, however I can't render 'search' page after getting data from getBySubtitle(). Any ideas what can be causing this? Thank you in advance.
Related
Any help appreciated. I've got an app that pulls data from google books api. From each book page, the user is able to leave a review. The path to the review is /review/${isbn Number}. Each page has a path based on the isbn. The review routes work and I'm able to make the post request through insomnia/postman with no issues, I'm just having trouble with the front-end js in pulling the data from the input boxes to make the post request. I'm not sure if the issue is because the isbn being in the path. Below is my front-end javascript that I am unable to fix.
const newFormHandler = async (event) => {
event.preventDefault();
console.log("testing")
const description = document.querySelector('#description').value;
const reviewTitle = document.querySelector('#reviewTitle').value;
const isbn = window.location.search
if (description) {
const response = await fetch(`api/review/${isbn}`, {
method: 'POST',
body: JSON.stringify({ description, reviewTitle }),
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
document.location.reload();
} else {
alert('Failed to create review');
}
}
};
document
.querySelector('.form-group')
.addEventListener('submit', newFormHandler);
My form is below:
<div class="col form-group">
<div class ="card reviewCard" style = "background-color:#fcf8f3; color: #65625e;">
<form id="blog-form">
<div>
<label for="reviewTitle">Review Title</label>
<input
value="{{title}}"
id="reviewTitle"
name="reviewtitle"
placeholder="Enter Review Title"
type="text"
required="required"
class="form-control"
data-bv-notempty="true"
data-bv-notempty-message="The title cannot be empty"
/>
</div>
<div>
<label for="review">Review</label>
<textarea
id="description"
name="review"
cols="40"
rows="10"
required="required"
class="form-control"
>{{description}}</textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
And here is my route that works fine with insomnia, no issues.
router.get('/review/:id', async (req, res) => {
try {
const isbn13 = req.params['id'];
const reviewData = await Review.findAll({ where: {
isbn:isbn13
},
include: [
{
model: User,
attributes: ['name'],
}
]
})
const reviews = reviewData.map((review) => review.get({ plain:true}));
// console.log(isbn13);
res.render('review', {
isbn: isbn13, reviews:reviews
});
} catch (err) {
console.log(err)
}
});
Any help appreciated. I tried to pull in the isbn number from the path, but with no success. I think I have it formatted wrong somehow.
First console log your req
You should see the body containing some data.
In a get request the they are arguments in the URL.
In a Psot request they are in the body of the request.
Sorry if this is super obvious, but I'm new to coding. I'm using node.js, express, mongoose and mongodb to try and add an update function to my app to make it CRUD by adding an edit button. Whenever I click the edit button though it still just deletes the item as if I were clicking the checkbox. I'm thinking it's because I'm calling the item from the same form of "pending items" but it seems like my update code isn't even registering as my console.logs for //Edit items aren't logging.
I want it to identify the item by its id when its edit button is submitted (then put the item in the newTask input to be edited and resubmitted as an update but I haven't figured out how to link those 2). I know the code is wonky, I'm just trying to figure out how to put this together, so thanks for any help!
<div class="box">
<!-- Item add function -->
<% newListItems.forEach(function(item){ %>
<!-- Form for pending items -->
<form action="/update" method="post">
<!-- Items -->
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onChange="this.form.submit()">
<button type="submit" class="editItmbtn" name="editItembtn">E</button>
<p><%=item.name%></p>
</div>
<input type="hidden" name="listName" value="<%= listTitle %>"></input>
</form>
<% }) %>
<!-- End pending items -->
<!-- Form to add items -->
<form class="item" action="/" method="post">
<input type="text" name="newTask" id="id" placeholder="Add new task..." autocomplete="off">
<button type="submit" name="list" value="<%= listTitle %>">+</button>
</form>
</div>
''//Requirements
const express = require("express");
const session = require("express-session")//for sessions
const favicon = require("serve-favicon"); //for favicon
const path = require("path");// for favicon
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser"); //for sessions
const mongoose = require("mongoose"); const _ = require("lodash");
const MongoStore = require("connect-mongo")(session);
const app = express();
// Edit items
app.put("/update", function(req, res) {
const itemName = req.body.newTask;
const taskID = req.body.editItmbtn;
const userInput = req.body.id;
Item.useFindAndModify(taskID), {
$set: {"/update": userInput}}, {new: true},
(err, result) => {
if (err) {
console.log("ERROR");
} else {
res.redirect("/");
res.render("list", {
listTitle: "Tasks",
newListItems: foundItems
});
}
}
});
// Delete checked items
app.post("/update", function(req, res) {
const checkedItemId = req.body.checkbox;
const listName = req.body.listName;
if (listName === "Tasks") {
Item.findByIdAndRemove(checkedItemId, function(err) {
if (!err) {
console.log("Successfully deleted checked item.");
res.redirect("/");
}
});
} else {
List.findOneAndUpdate({
name: listName
}, {
$pull: {
items: {
_id: checkedItemId
}
}
}, function(err, foundList) {
if (!err) {
res.redirect("/" + listName);
}
});
}
});
In your form you are using the method="post" which means that the request will be submitted to your app.post("/update" ..... ) , that is why you always land there, where you delete your item.
You have to do two things:
Change the app.put('/update' ...) to app.post('/update' ...)
Change the app.post('/update' ... which is meant to delete the item to something semantically more relevant like maybe app.post('/delete' ... and change the frontend with which you delete method respectively.
You have app.put("/update", function(req, res) in Express but <form action="/update" method="post"> in your form, which matches your delete route.
For your second question, there are many ways to populate your item form in Javascript, but there might be an even more efficient way if you tell us if you're using a framework.
Your form always submit the http request with the "POST" method, but in your code the app.post("update") deletes the item
i got an issue regarding checkboxes with nedb. I want to send true or false if the checkbox is checked or not to the database i cannot solve this issue. i am working with node.js and nedb. please help!
client js eventlistener:
var taskDone = document.querySelectorAll('.taskDone');
taskDone.forEach(btn => {
btn.addEventListener('click', (e) => {
var done = e.target.attributes[1].value;
let id = e.target.getAttribute('data-id');
let isDone = document.querySelector(`input[data-id=${id}]`).value;
console.log(isDone + "isdone")
if ($(taskDone).is(':checked')) {
$('.text').addClass('line-through')
console.log("trues")
$.ajax({
url: 'http://localhost:3000/done/' + id,
type: 'PUT',
data: { isDone }
}).done(function (data) {
//location.reload()
console.log(data)
})
} else {
console.log('falses')
$('.text').removeClass('line-through')
}
})
})
update function to nedb:
function taskIsDone (id, done) {
return new Promise((resolve, reject) => {
db.update({ _id: id }, { $set: done }, { returnUpdatedDocs: true }, (err, num, updateDocs) => {
if (err) {
reject(err)
} else {
resolve(updateDocs)
}
})
})
}
server:
app.put('/done/:_id', async(req, res) => {
try {
var id = req.params._id;
let done = {
title: req.body.isDone,
}
const updateToDo = await taskIsDone(id, done)
console.log(updateToDo + " Todo done");
res.json(updateToDo);
} catch (error) {
res.json({error: error.message});
}
})
html/ejs:
<% for ( var i = 0; i < row.length; i++) { %>
<div class="edit-container" >
<input type="text" name="editTask" value="<%=row[i].title %>" data-id="<%=row[i]._id %>">
<button name="<%= row[i]._id %>" class="edit" data-id="<%=row[i]._id %>">save edit</button>
</div>
<div>
<input type="checkbox" name="isDone" class="taskDone" data-id="<%=row[i]._id %>">
<span class="text"><%= row[i].title %></span>
<button class="delete" name="<%= row[i]._id %>">delete</button>
</div>
<br>
<% } %>
i could really need some help with this! thanks
I have recreated a minimal example of what you are trying to do with checkbox checked state. I have added three checkboxes with same class name .taskDone
And i have using a change function not a click function. Every-time you clicked on the checkbox and check it will show the console log with checked and the data-id of that checkbox as well.
To get the data-id you can simply use .data function of jQuery and just specify what you want after the data-** to get it stored value.
In addition, do not use fat arrow - => function with jQuery. Use normal function statements so you can access you things by using $(this) instead of specifying each class or id
Live Working Demo:
let taskDone = document.querySelectorAll('.taskDone'); //get all the chechbox with same class .taskDone
taskDone.forEach(function(btn) { //use normal function
btn.addEventListener('change', function() {
let id = $(this).data('id') //get the data id of checkbox
if ($(this).is(':checked')) { //check if the clicked checkbox is checked or not
console.log(id + ' is Checked - Updating neDB') //console.log
$.ajax({
url: 'http://localhost:3000/done/' + id,
type: 'PUT',
data: 'isDone'
}).done(function(data) {
console.log(data)
})
} else {
console.log("Not Checked")
}
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox" name="isDone" class="taskDone" data-id="1">
<input type="checkbox" name="isDone" class="taskDone" data-id="2">
<input type="checkbox" name="isDone" class="taskDone" data-id="3">
I've looked at all the threads that already exist on this topic and have not been able to come up with a solution for my case.
I have multiple forms rendering with the help of Handlebars like this:
<ul>
{{#each listRecords}}
<form id="form{{id}}" action="/expand-list-records-save/{{../listId}}/{{id}}" method="POST">
<div class="record-box">
<li>{{recordTitle}} by {{artistName}} ({{releaseYear}})
<br>
<div>
<label>Paste media embed code here:</label>
<textarea class="form-control form-control-lg" name="embeddedmedia" cols="30" rows="10">{{embeddedMedia}}</textarea>
</div>
<br>
<br>
</li>
</div>
</div>
</form>
{{/each}}
</ul>
<input id="submit" class="btn btn-secondary btn-block" type="submit" value="Submit embed code" >
<script>
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
for (var i=0; i < document.forms.length; i++) {
console.log(`submitting ${document.forms[i].id}`)
document.forms[i].submit();
}
})
})
</script>
my Node.js + Express.js route looks like this
router.route('/expand-list-records-save/:listId/:recordId')
.post((req, res) => {
// console.log(req)
Record.findOne({
where: {
id: req.params.recordId
}
}).then(result => {
// console.log(req.body)
result.update({
embeddedMedia: req.body.embeddedmedia
})
}).then(() => {
console.log("sending list to view")
sendListDataToView({ params: {id: req.params.listId} }, res, 'view-list')
})
})
I'm having a few problems. First of all, this logic only executes a POST request for the item that the very last form on the page is for. Why is it that the console.log works for every single instance in my loop when iterating through all the document forms? From what I know, I think I need to use AJAX here somehow to execute all the POST requests. And the second main thing that I don't think is giving me problems at this point, but will once I get the first issue solved, is that my route is not written to handle a batch of requests like I need it to.
UPDATE
Upon a recommendation in comments, I decided try and write an Ajax request to post all of the forms to a separate route which will handle it from there. How do I pass an array of forms to the data parameter? I get the Uncaught RangeError: Maximum call stack size exceeded error this way:
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
$.ajax({
type: 'POST',
url: window.location.origin + $('h3')[0].innerText,
data: document.forms,
success: (data) => {
console.log(data)
}
})
})
})
After going through some other examples, I tried rewriting original submit script like this. And, in this case, it does not pick up the action attribute.
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
$('form').each(() => {
var that = $(this);
$.post(that.attr('action'), that.serialize())
})
})
})
So, I have finally come up with a solution, if anyone is interested. Perhaps not prettiest, but it works.
<script>
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
var counter = 0;
var totalForms = $('form').length;
$('form').each((i, form) => {
const redirectIfDone = () => {
if (counter === totalForms) {
alert("all forms updated successfully")
window.location.replace(window.location.origin + '/list/' + $('h3')[0].innerText)
}
}
if (!($(form).find('textarea').val())) {
counter++
redirectIfDone();
return true;
} else {
$.ajax({
type: 'POST',
url: $(form).attr('action'),
data: $(form).serialize(),
success: (data) => {
counter++;
redirectIfDone();
}
})
}
})
})
})
</script>
Virtually no changes to the route. Overall, I'm still interested in seeing other possible solutions.
router.route('/expand-list-records-save/:listId/:recordId')
.post((req, res) => {
Record.findOne({
where: {
id: req.params.recordId
}
}).then(result => {
result.update({
embeddedMedia: req.body.embeddedmedia
})
res.end()
})
})
Ive built a rest-API to add todos in a mongodb. I can successfully save instances by using the following setup in postman:
http://localhost:3000/api/addtodo x-www-form-urlencoded with values text="Test", completed: "false".
Now when I try to replicate this with Angular, it doesnt work, the todo is saved but without the text and completed attributes, I cant seem to access the text or completed values from body. What am I doing wrong? Code below:
Angular-HTML:
<div id="todo-form" class="row">
<div class="col-sm-8 col-sm-offset-2 text-center">
<form>
<div class="form-group">
<!-- BIND THIS VALUE TO formData.text IN ANGULAR -->
<input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">
</div>
<!-- createToDo() WILL CREATE NEW TODOS -->
<button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>
</form>
</div>
</div>
Angular-js:
$scope.createTodo = function() {
$http.post('/api//addtodo', $scope.formData)
.success(function(data) {
$scope.formData = {}; // clear the form so our user is ready to enter another
$scope.todos = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
REST-API:
router.post('/addtodo', function(req,res) {
var Todo = require('../models/Todo.js');
var todo = new Todo();
todo.text = req.body.text;
todo.completed = req.body.completed;
todo.save(function (err) {
if(!err) {
return console.log("created");
} else {
return console.log(err);
}
});
return res.send(todo);
});
$http.post sends it's data using application/json and not application/x-www-form-urlencoded. Source.
If you're using body-parser, make sure you've included the JSON middleware.
app.use(bodyParser.json());
Either that or change your default headers for angular.
module.run(function($http) {
$http.defaults.headers.post = 'application/x-www-form-urlencoded';
});