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.
Related
I want to get values of a BeginCollectionItem using javascript but it seems like i am not winning
my html
#using HtmlHelpers.BeginCollectionItemCore
#using E_Commerce.Application.Models
#model ProductImageModel
<li class="mb-2">
#using (Html.BeginCollectionItem("ProductImages"))
{
Html.RenderPartial("_imagesPartial", Model);
}
</li>
the _imagesPartial html
#model E_Commerce.Application.Models.ProductImageModel
<div class="col-md-12 row mb-2">
<div class="col-md-4 imageDiv">
<img class="border rounded" />
</div>
<div class="col-md-4">
<input type="file" asp-for="File" onchange="uploadImageTest(this)" />
</div>
<div class="col-md-4">
Remove
</div>
</div>
and my javascript
let addNewImagesFormSubmit = (event) => {
var Id = $("#Id").val();
var productImages = $("#ProductImages").val();
$.ajax({
url: "#Url.Action("AddNewImages","ProductImage")",
data: {
Id: Id,
productImages:productImages
},
success: function (data) {
$("#productImagesDiv").html(data);
}
})
}
the server side does not seem to get any images when using javascript to get the BeginCollectionItem.
how do i get it to work
I have problems accessing this "name" property on the component. I can only access it statically.
<template>
<div class="col-md-12">
<p
v-for="channel in channels"
:key="channel.id"
class="channel"
:class="{ 'active': channel.id == activeChannel }"
#click="setChannel(channel.id)">
{{ channel.users[0].name }}
</p>
</div>
</template>
Here is an Image of my Vue Devtools
So I have an v-for loop over channels, and I want to: Access the Usernames for each channel (if it is not my own preferably as "username" is set on my own i think its easy to exclude it right?) So that in the end In Channel 1 when there are 2 Users , I want to show the corresponding username, so the "other username", the one i am chatting with, and he should see my name that is the initial goal.
I thought of doing something like this:
<template>
<div class="col-md-12">
<p
v-for="channel in channels"
:key="channel.id"
class="channel"
:class="{ 'active': channel.id == activeChannel }"
#click="setChannel(channel.id)">
<!-- {{ channel.users[0].name }} -->
<span v-for="user,key in channel">{{key}}</span>
</p>
</div>
it at least displays the content of the channels object for each channel, but something like this isnt gonna work: key.user.name , unfortunately im stuck here. please help :)
edit: here is a dd() of the view
click
EDIT 2: Parent Data Provided:
//chat-app.blade.php
<div id="app">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Chats</div>
<vue-chat :channels="{{ $channels }}" ></vue-chat>
</div>
</div>
</div>
</div>
</div>
</div>
//<vue-chat> component
<template>
<div class="chat">
<div class="container">
<div class="row">
<div class="col-md-3">
<vue-chat-channels
:channels="channels"
:active-channel="activeChannel"
#channelChanged="onChannelChanged"
:username="sername"
></vue-chat-channels>
</div>
<div class="col-md-3">
<vue-chat-messages :messages="messages"></vue-chat-messages>
</div>
<div class="col-md-3">participants</div>
</div>
<div class="message-input-wrapper col-md-12"><vue-chat-new-message :active-channel="activeChannel"
:username="username"></vue-chat-new-message></div>
</div>
</div>
</template>
<script>
export default {
props: ["channels"],
data() {
return {
activeChannel: this.channels[0].id,
messages: [],
username: ''
};
},
methods: {
fetchMessages() {
let endpoint = `/channels/${this.activeChannel}/messages`;
axios.get(endpoint).then(({ data }) => {
this.messages = data;
});
},
onChannelChanged(id) {
this.activeChannel = id;
this.fetchMessages();
}
},
created() {
this.fetchMessages();
axios.get('/userfetch').then( ({data}) => {
console.log("Current User: "+data.name);
this.username = data.name;
});
console.log(this.channels[0].name);
// for (let channel of this.channels) {
this.channels.forEach(channel => {
// Channelname
window.Echo.channel('presence-'+channel.name)
.listen('MessageSent', (channel) => {
console.log(channel.data.message);
this.messages.push({ message: channel.data.message, author_username: channel.data.author_username});
if (this.activeChannel == channel.id) {
console.log("received message");
}
});
});
}
};
</script>
<style>
</style>
//ChatController.php
public function index()
{
$channels = Channel::with('users')->whereHas('users', function($q) {
$q->where('user_id',Auth::id());
})->get();
$user = Auth::user()->name;
return view('chat-app' , compact('channels','user'));
}
Short Explanation: ChatController returns the blade view, which has the data channels and user (my username) , and then vue comes into play which should pass down the prop of my username but i couldnt get it to work just yet
So you need to access users in every channel.
You can try like this:
<div class="col-md-12">
<p
v-for="channel in channels"
:key="channel.id"
class="channel"
:class="{ 'active': channel.id == activeChannel }"
#click="setChannel(channel.id)">
<span v-for="user in channel.users">
{{ user.name }}
</span>
</p>
</div>
This should work. If you have errors provide it here.
If you need to compare every user you can do it simply with v-if:
<span v-for="user in channel.users">
<span v-if="user.name === parentdata">
{{ user.name }}
</span>
</span>
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'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.
I am trying to grab dynamically created information with ng-model (info created with ng-repeat). I am doing so in order to send the information in the form of an object to my flat database, Firebase. Unfortunately, the ng-model is only grabbing "" (empty strings) as my values. Any suggestions?
/////////// Controller below
// Bookmark object
$scope.bookmark = {
url: "",
illnessName: "",
symptom: ""
};
// Save illness card to bookmarks
$scope.saveToBookmarks = ($bookmark) => {
console.log($bookmark);
if ($scope.bookmark.illnessName === ""){
alert("Hey, quit that NAME!");
return;
}
if ($scope.bookmark.symptom === ""){
alert("Hey, quit that SYMPTOM!");
return;
}
if ($scope.bookmark.url === ""){
alert("Hey, quit that URL!");
return;
}
BookmarksFactory.addUserBookmark({
url: $scope.bookmark.url,
illnessName: $scope.bookmark.illnessName,
symptom: $scope.bookmark.symptom
});
console.log("$scope.bookmark.url :", $scope.bookmark.url);
};
///////// Factory below
"use strict";
app.factory("BookmarksFactory", function(FBCreds, $q, $http) {
var addUserBookmark = (newBookmark) =>{
console.log("Factory is working!");
return $q( (resolve, reject) => {
$http.post(`${FBCreds.databaseURL}/bookmarks.json`,
JSON.stringify(newBookmark))
.then( (FBObj) => {
resolve(FBObj);
})
.catch( (error) => {
reject(error);
});
});
};
return {
addUserBookmark
};
});
<!-- This will be search results populate -->
<div class="search-results" >
<div class="row">
<div class="col-lg-4 text-center search-card box" ng-repeat="illness in illnesses | filter: searchText.search">
<!-- Insert search results here -->
<div class="inner ">
<div class="card">
<button type="button" class="close close-card" aria-label="Close" ng-click="removeCard($event)">
<span class="close-card-x" aria-hidden="true">×</span>
</button>
<img class="card-img-top" src="{{illness.url}}" alt="Card image cap" ng-model="bookmark.url">
<div class="card-block">
<h4 class="card-title" ng-model="bookmark.illnessName">{{illness.illnessName}}</h4>
<p class="card-text" ng-model="bookmark.symptom">{{illness.symptom}}</p>
<button class="btn btn-primary" ng-click="saveToBookmarks(bookmark)">Save</button>
<button class="btn btn-success" ng-click="goToEditPage()">Edit</button>
</div>
</div>
</div>
</div>
</div>
</div>
change to $scope.bookmark ={};
and use http to get data file you code , example of me is test.php
$http(){
method:'POST',
url:'test.php',
data : $.param($scope.bookmark),
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(success)
function success(){
////
}