This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have a really weird problem with my nodejs project. The project is an online shop with express and handlebar and it has a mongo database connected. In the router section I have this code:
router.get('/item/:item', function (req, res, next) {
var actItem = {};
Item.find().findOne({ '_id': req.params.item }, function (err, item) {
actItem.name = item.item_name;
actItem.price = item.price;
actItem.description = item.description;
actItem.imageLink = item.imageLink;
});
res.render('pages/shop/item-view', { title: 'Express', item: actItem });
});
It looks for the item ID in the URL on the database and it returns the view passing the data to be displayed. It works just fine but then in the view i have this code:
<div id="carouselExampleControls" class="carousel slide" data-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img class="d-block w-100" src="{{item.imageLink}}" alt="First slide">
</div>
<div class="carousel-item">
<img class="d-block w-100" src="" alt="Second slide">
</div>
<div class="carousel-item">
<img class="d-block w-100" src="" alt="Third slide">
</div>
</div>
<a class="carousel-control-prev" href="#carouselExampleControls" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselExampleControls" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
And it works just fine too! But here comes the problem. Whenever I add three dots in any src attribute, it crashed. Even weirder is that it crashed even if I edit the html document after render in chrome and add them, like this:
<img class="d-block w-100" src="..." alt="Second slide">
The error of the crash is this:
actItem.name = item.item_name;
^
TypeError: Cannot read property 'item_name' of undefined
Any idea on how to fix this and why it happens?
SOLUTION
I managed to solve this problem by checking the item before doing anything.
if (item) {
actItem.name = item.item_name;
actItem.price = item.price;
actItem.description = item.description;
actItem.imageLink = item.imageLink;
}
It happens because when I use ... the browser makes the request /item/... to get the image, then the value of req.params.item becomes ... and in database there is no entry with _id = ... . So the item value well be undefined
findOne is an async function so call res.render inside it , and check if item is not null :
router.get('/item/:item', function (req, res, next) {
var actItem = {};
Item.find().findOne({ '_id': req.params.item }, function (err, item) {
if(item){
actItem.name = item.item_name;
actItem.price = item.price;
actItem.description = item.description;
actItem.imageLink = item.imageLink;
res.render('pages/shop/item-view', { title: 'Express', item: actItem
}
else{
res.render('pages/shop/item-view', { title: 'Express', item: 'defaultValue'});
}
});
});
Related
I am trying to set up a Shop page for my website. I have a navigation bar with 7 items. Each item works. They all work via a RestFUL API (Node server, routes, and controllers...). So I have it set up exactly the same for all pages. But for the shop page it won't work, not sure why. Thanks for all input.
Route
`
router.post('/addshopitem', encodedParser, function(req, res) {
console.log("okay step 2");
const name = req.body.name;
const desc = req.body.desc;
const price = req.body.price;
const stock = req.body.stock;
const createdby = req.body.createdby;
console.log("okay step 2 almost");
ShopController.add_item(req,res,name,desc,price,stock,createdby);
console.log("okay step 2 done");
});
Controller
exports.add_item = (req, res, name, desc, price, stock, createdby) => {
const Item = require('../models/item');
const item = new Item(1, name, desc, price, stock, createdby);
const response = item.save();
if (response) {
res.render('shopitemadded');
} else {
res.render('error');
}
}
NAV-Item
<div class='dropdown'>
<button class='dropbtn' id='shop' onclick="window.location.href='/shop';">Shop</button>
<div class='dropdown-content'></div>
</div>
`
Shop.ejs (View)
`
<body>
<div class='nav-div'>
<%- include('./partials/nav.ejs') %>
</div>
<div class='content' id='content-one'>
<div class="grid">
<%- include('./partials/item.ejs') %>
</div>
</div>
<div class='content' id='content-two'>
</div>
<div class='content' id='content-three'>
</div>
</body>
`
Item.ejs (Partial View Element)
`
<% items.forEach(function(item) { %>
<div class="card"><img class="card__img" src="" alt="Canyons" />
<div class="card__content">
<h1 class="card__header"><%=item.name%></h1>
<p class="card__text"><%=item.desc%></p>
<form action="/shop/addtobasket" method="post">
<input type="text" name="iditem" value="2" style="display: none;">
<label for="amount">2
<input type="text" name="amount" value="3">
</label>
<p>Anzahl an Lager: <%=item.stock%></p>
<p>Stückpreis: <%=item.price%></p>
<input class="card__btn" type="submit" name="submit" value="Dem Warenkorb hinzufügen">
</form>
</div>
</div>
<% })%>
`
I am trying to set up a shop page. I did it the exact same way as all the other views (with a route and a controller). Only thing different: The ejs View has a partial (item.ejs) with a for each inside. Maybe I messed something up in there. Not sure.
In res.data.tweets i am fetching tweets from server and storing values for every tweet in tweetData obj. In tweet_text, i would like to add a link to the tagged twitter users(clickin on tag will redirect to twitter profile). I figured out how do i find tagged users but i am not able to add a link either with .link or .href attribbute.
axios.post(API_URL, null, { params }).then(res => {
this.currentPage = res.data.page
this.numberOfPages = res.data.numberOfPages
res.data.tweets.forEach(tweet => {
const tweetData = {
id: tweet.id,
tweet_text: tweet.tweet_text,
twitter_name: tweet.twitter_name,
twitter_username: tweet.twitter_username,
added_at: moment(String(tweet.added_at)).format('MM/DD/YYYY hh:mm'),
}
this.tweets.push(tweetData)
// Below this line i am trying to achieve something by adding a link
this.tweets.forEach(el => {
el.tweet_text.split(' ').forEach(word => {
if (word.includes('#')) {
// word = 'this is tag'
word.link('https://twitter.com')
console.log(word.link)
}
})
})
})
})
This is component where data is shown
<div class="tweets-container">
<div
v-for="tweet in tweets"
:key="tweet.id"
>
<div class="tweet-card">
<div class="username-time">
<div class="user-info">
<p class="name">
{{ tweet.twitter_name }}
</p>
<p class="user-info-p">
#
</p>
<p class="username">
<a
:href="'https://twitter.com/' + tweet.twitter_username"
class="twitter_link"
target="_blank"
>
{{ tweet.twitter_username }}
</a>
</p>
</div>
<div class="time">
<p>{{ tweet.added_at }}</p>
</div>
</div>
<div class="text">
<p>{{ tweet.tweet_text }}</p>
</div>
</div>
</div>
</div>
I don't think it's going to work this way.
You should use a computed property which will append the username to the twitter base link. Then, in your template, you can link the href property to the computed property.
computed: {
userLink: function () {
return "http://twitter.com/" + this.tweet.twitter_username
}
}
You can now use this "userLink" directly in your template.
<p class="username">
<a
:href="userLink"
class="twitter_link"
target="_blank"
>
{{ tweet.twitter_username }}
</a>
</p>
I didn't test the code I wrote, feel free to let me know if you need further advice ;)
With Django I made a Show more comments button using ajax. This system works, but the problem is that there are form fields in the comments I brought in this way, and when I click the more button, csrf_token does not appear in this comment field. As such, I get a csrf_token error when I submit the form. I leave my codes below.
To solve this problem, I ran the form with the get method, but as such, the function in the views directs me to the page with JsonResponse. Another solution was to give the header information csrf_token, but that didn't solve the problem either.
blog_detail.html
<script>
$(document).ready(function(){
$('#load_form').submit(function(e){
e.preventDefault();
var limit = $(this).attr('limit')
var page = document.getElementById('pagination')
var blog_comment_list = $('#blog-comment-list')
var serializedData = $(this).serialize();
$.ajax({
type:'GET',
url: "{% url 'load-more-comments' %}",
data : serializedData,
success: function(response){
blog_comment_list.html(response.comment_html)
if (page.value >= limit){
$('#submit_button').hide()
}
page.value = parseInt(page.value)+1
},
})
})
})
</script>
<div id="blog-comment-list">
{% include 'front_end/blog/comment/partial-comment.html' %}
</div>
<form method="GET" id="load_form" limit="{{num_pages}}">
<input type="hidden" name="pk" value="{{details.id}}">
<input type="hidden" name="page" value="2" id="pagination">
<input type="submit" name="load" value="Load More" id="submit_button">
</form>
partial-comment-html
<div class="media-holder mt-5">
<h3 class="title mb-3">All Comments</h3>
{% for item in comments %}
<div class="infinite-container">
<div class="media mb-5">
<img class="img-fluid rounded-circle box-shadow mr-4" alt="image" src="{{item.owner.get_image_or_default}}" style="width: 100px;height: 100px;">
<div class="media-body">
<h6>{{item.owner.name}} {{item.owner.surname}}</h6>
<br>
<small><span style="font-size:14px;" class="stars-container stars-{% widthratio item.rate.rate 1 20 %}" id="stars">★★★★★</span></small>
<div class="comment-date"> <span class="date">{{item.created_date|naturaltime}}</span>
</div>
<p>{{item.content}} </p>
<div align="center">
{% if item.comments.all %}Cevapları Görüntüle ({{item.comments.count}}){% else %}Cevap Ver{% endif %}
</div>
<div class="generic-comment" id="generic-comment-id-{{item.id}}" style="display:none;">
<!-- -->
<div class="infinite-generic-comment">
<form action="{% url 'comment-answer' %}" method="POST" id="answer_form">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{item.id}}">
<div class="row">
<div class="col-9">
<input type="text" placeholder="Cevap Ver" name='generic_comment' class="form-control">
</div>
<div class="col-2">
<input type="submit" value="Cevap Ver" class="btn btn-outline-primary">
</div>
</div>
</form>
{% for comment in item.comments.all %}
<div class="media mb-2">
<img class="img-fluid rounded-circle box-shadow mr-4" alt="image" src="{{item.owner.get_image_or_default}}" style="width: 100px;height: 100px;">
<div class="media-body">
<h6>{{comment.owner.name}} {{comment.owner.surname}}</h6>
<div class="comment-date"> <span class="date">{{comment.created_date|naturaltime}}</span>
</div>
<p>{{comment.content}} </p>
</div>
</div>
{% endfor %}
</div>
<!-- -->
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<script>
var aTag = document.getElementsByClassName('show-answers')
for (let i = 0; i< aTag.length; i++){
aTag[i].addEventListener('click', (event) => {
var generic = document.getElementById(`generic-${event.target.id}`)
if (generic.style.display == 'none'){
$(`#generic-${event.target.id}`).slideDown('slow')
}
else{
$(`#generic-${event.target.id}`).slideUp('slow')
}
})
}
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(document).ready(function(){
$('#answer_form').submit(function(e){
e.preventDefault()
var serializedData = $(this).serialize()
var url = $(this).attr('action')
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
//headers: { "X-CSRFToken": getCookie("csrftoken") }
});
$.ajax({
type:'POST',
url: url,
data:serializedData,
success: function(response){
if (response.success){
$('#answer_form').trigger('reset')
Swal.fire(
'Başarılı!',
'Cevap onaya gönderildi',
'success'
)
}
}
})
})
})
</script>
views.py
def answer_comment(request):
if request.method=='POST':
comment = Comment.objects.get(id=request.POST.get('comment_id'))
comment.comments.create(owner=request.user, content=request.POST.get('generic_comment'))
return JsonResponse({'success':True},status=200)
return JsonResponse({'success':False}, status = 403)
Note: I tried the csrf_exempt decorator function, but still could not solve the problem.
The methods I use are available in the script tag in partial-comment-html. How can I solve this problem. As I said, when I press the load more button, the necessary form fields to respond to a comment are just below these comments and when load more with ajax, the csrf_token information is deleted in these form fields, and nothing happens in new comments.
Note 2: When {{csrf_token}} is entered, I added the hidden input field with javascript myself, but Django realized that I added it and prevented me.
I am working on a shopping cart project in NodeJS & MongoDB, and I'm trying to make a search bar in my shop page.
index.hbs
<form action="/" method="get" id="query-form">
<input class="col-sm-6 col-md-4 col-md-offset-4 col-sm-offset-3"
type="text" id="query" name="query">
<button type="submit" class="btn btn-success">Search</button>
</form>
</div>
{{# each products }}
<div class="row">
{{# each this }}
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<img src="{{ this.imagePath }}" alt="..." class="img-
responsive">
<div class="caption">
<h3>{{ this.title }}</h3>
<p class="description">{{ this.description }}</p>
<div class="clearfix">
<div class="price pull-left">${{ this.price }}
</div>
<a href="/add-to-cart/{{this._id}}" class="btn
btn-success pull-right" role="button">Add to Shopping Cart</a>
</div>
</div>
</div>
</div>
{{/each}}
</div>
{{/each}}
When I send the information back, while trying to catch the result using a "route", I can't seem to be able to pass this "query" to be search in mongoDB:
index.js
var successMsg = req.flash('success')[0];
if(req.query.query) {
let query = "/" + req.query.query + "/";
Product.find({title: {$regex: query,'$options': 'i'}},function (err,
docs) {
let productChunks = [];
let chunkSize = 3;
for (var i = 0; i < docs.length; i += chunkSize) {
productChunks.push(docs.slice(i, i + chunkSize));
}
res.render('shop/index', {title: 'Shopping Cart', products:
productChunks, successMsg: successMsg, noMessages: !successMsg});
})
}
else
{
Product.find(function (err, docs) {
var productChunks = [];
var chunkSize = 3;
for (var i = 0; i < docs.length; i += chunkSize) {
productChunks.push(docs.slice(i, i + chunkSize));
}
res.render('shop/index', {title: 'Shopping Cart', products: productChunks, successMsg: successMsg, noMessages: !successMsg});
});
}});
While debuuging, it seems that the route is being requested twice, and that in the last time it has no "req" object to use, hence there is no search.
Any ideas?
Thanks.
If using $regex query operator explictly there is no need to append/prepend with / like that because then it would be string with / and not regular expression. $regex is going to make it into a regular expression while evaluation.
Interestingly you're using GET with form (that's okay for search), Make sure req.query is coming to the route with query field (a valid string)
Simply pass the string to regex operator:
/*GET params in express go in req.query only*/
const queryToSearch = req.query.query;
Product.find({title: {$regex: queryToSearch,'$options': 'i'}});
On request being called twice:
It should be an OPTIONS request usually known as pre-flight request is an intended behaviour from sources like: browsers. Read More
I'm currently teaching myself the ins and outs of building a MEAN app. I started with the basic todo app and modified it pretty heavily to support multiple key value pairs and have updated the UI using bootstrap.
It's currently hosted here: http://surveymanager-30817.onmodulus.net/
I've implemented ui-sortable, and it works perfectly by itself.
The challenge I'm having, that I cannot seem to find any relevant documentation or tutorials for - is how to communicate the updated sort order to mongo so when I refresh the page, ng-repeat will repeat question in questions with the order that I had previously created.
Here is the HTML
<!-- index.html -->
<!doctype html>
<!-- ASSIGN OUR ANGULAR MODULE -->
<html ng-app="QuestionManager">
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
<title>Question Manager</title>
<!-- SCROLLS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/question-bootstrap.css" rel="stylesheet">
<style>
html { overflow-y:scroll; }
body { padding-top:30px; }
#todo-list { margin-bottom:30px; }
</style>
<!-- SPELLS -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script><!-- load jquery -->
<script src="//code.jquery.com/ui/1.9.1/jquery-ui.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script><!-- load angular -->
<script src="js/sortable.js"></script>
<script src="core.js"></script>
</head>
<!-- SET THE CONTROLLER AND GET ALL TODOS -->
<body ng-controller="mainController">
<div class="container sm_head">
<div class="col-sm-6">
</div>
<div class="col-sm-6">
<h2 class="pull-right">Survey Manager</h2>
</div>
</div>
<div class="container">
<!-- Nav tabs -->
<ul id="navtabs" class="nav nav-tabs" role="tablist">
<li class="active">Manage</li>
<li>Create</li>
<li>Render Code</li>
<li>About</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="manage">
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">Manage Question Order<span class="badge pull-right">{{ questions.length }} Questions</span></div>
<!-- Table -->
<table class="table">
<thead>
<tr>
<th>Order</th>
<th>Question Name</th>
<th>Evergage Field</th>
<th>Username</th>
<th>Options</th>
</tr>
</thead>
<tbody ui-sortable="sortableOptions" ng-model="questions">
<tr ng-repeat="question in questions">
<td>{{ question.order }}</td>
<td>{{ question.meanName }}</td>
<td>{{ question.fieldName }}</td>
<td>#mdo</td>
<td>
<button type="button" class="btn btn-default btn-sm" ng-click="deleteQuestion(question._id)">
<span class="ques-list glyphicon glyphicon-remove"></span> delete </button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-8">
<form class="form-horizontal">
<fieldset>
<!-- Form Name -->
<legend>Question Details</legend>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="Question Name">Question Order</label>
<div class="col-md-8">
<input id="Question Order" name="Question Order" type="text" placeholder="Question Order" class="form-control input-md" ng-model="formData.order">
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="Question Name">Question Name</label>
<div class="col-md-8">
<input id="Question Name" name="Question Name" type="text" placeholder="Write something meaningful" class="form-control input-md" ng-model="formData.meanName">
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="Custom Field Name">Custom Field</label>
<div class="col-md-8">
<input id="Custom Field Name" name="Custom Field Name" type="text" placeholder="Format: User.Profile.xx.xx.xx ( 1 or 3 additional words)" class="form-control input-md" ng-model="formData.fieldName">
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="Create"></label>
<div class="col-md-4">
<button id="Create" name="Create" class="btn btn-primary" ng-click="createQuestion()">Create</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
<div class="tab-pane" id="create">
</div>
<div class="tab-pane" id="render">...</div>
<div class="tab-pane" id="about">...</div>
</div>
</div>
<script src="js/bootstrap.min.js"></script>
</body>
</html>
Here is the client side:
// public/core.js
var QuestionManager = angular.module('QuestionManager', ['ui.sortable']);
function mainController($scope, $http) {
$scope.formData = {};
// when landing on the page, get all questions and show them
$http.get('/api/questions')
.success(function(data) {
$scope.questions = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
// when submitting the add form, send the text to the node API
$scope.createQuestion = function() {
$http.post('/api/questions', $scope.formData)
.success(function(data) {
$scope.formData = {};
console.log('fuck you!');
$scope.questions = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
// delete a question after checking it
$scope.deleteQuestion = function(id) {
$http.delete('/api/questions/' + id)
.success(function(data) {
$scope.questions = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
$scope.$watch("questions", function(newVal, oldVal) {
console.log("oldval", oldVal);
console.log("newVal", newVal);
});
$scope.sortableOptions = {
update: function(e, ui) {
$http.put('/api/questions', $scope.questions)
console.log($scope.questions);
},
axis: 'y'
};
}
// Some Bootstrap Initializers
$('#navtabs>li a').click(function (e) {
e.preventDefault()
$(this).tab('show')
})
And here is the server side with my random test and console logs starting at line 44
// server.js
var express = require('express');
var app = express(); // create our app w/ express
var mongoose = require('mongoose'); // mongoose for mongodb
var morgan = require('morgan'); // log requests to the console (express4)
var bodyParser = require('body-parser'); // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
mongoose.connect('mongodb://ewill3532:12qwaszx#proximus.modulusmongo.net:27017/pUxo2hir'); // connect to mongoDB database locally
app.use(express.static(__dirname + '/public')); // set the static files location /public/img will be /img for users
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
var question = mongoose.model('question', {
order : String,
fieldName : String,
meanName : String
});
app.get('/api/questions', function(req, res) {
// use mongoose to get all questions in the database
question.find(function(err, questions) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
res.json(questions); // return all questions in JSON format
});
});
// create todo and send back all questions after creation
app.post('/api/questions', function(req, res) {
// create a todo, information comes from AJAX request from Angular
question.create({
order : req.body.order,
fieldName : req.body.fieldName,
meanName : req.body.meanName,
done : false
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the questions after you create another
question.find(function(err, questions) {
if (err)
res.send(err)
res.json(questions);
});
});
});
// delete a todo
app.delete('/api/questions/:todo_id', function(req, res) {
question.remove({
_id : req.params.todo_id
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the questions after you create another
question.find(function(err, questions) {
if (err)
res.send(err)
res.json(questions);
});
});
});
// let's just put this here so that it comes back as found
app.put('/api/questions', function(req, res) {
});
app.get('*', function(req, res) {
res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});
// listen (start app with node server.js) ======================================
app.listen(8080);
console.log("App listening on port 8080");
Any help here, or if someone can point me to some good documentation or tutorials - either way would be very helpful!'
Thanks!