Using AJAX in Node js and displaying data using Handlebars - javascript

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

Related

Load list objects in ajax response and create dynmic list divs with this data

I have this part of HTML:
<div class="container">
<div class="row">
<div class="col-sm-4">
<div class="card">
<div class="card-body">
<h4 class="card-title">{title}</h4>
<p class="card-text">{content}</p>
Read...
</div>
</div>
</div>
</div>
</div>
and I have ajax request which calls after page loading:
<script>
$(window).on('load', function () {
loadArticles();
});
function loadArticles() {
$.ajax({
dataType: "json",
url: "/articles", success: function (result) {
}
});
}
</script>
I need to create list of cards(<div class="card">) with data from response. For example, I get 5 articles in response. I need to create 5 card divs and fill its data from the response. How can I do it?
Loop over the objects you get back from the ajax call and use the jQuery .append() function to add them to the dom.
First, you need to add an identifying class (or id) to the parent div in your HTML and remove the card HTML:
<div class="container">
<div class="row">
<div class="col-sm-4 cards-wrapper"></div>
</div>
</div>
Then in your loadArticles function loop over your ajax response and append to that jQuery selected we just defined - '.cards-wrapper':
function loadArticles() {
$.ajax({
dataType: "json",
url: "/articles",
}).done(function(data) {
const cards = data.body.cards; // Or however you need to traverse the response object
cards.forEach(function(card) {
$('.cards-wrapper').append('<div class="card"><div class="card-body"><h4 class="card-title">' + card.title + '</h4><p class="card-text">' + card.content + '</p>Read...</div></div>');
})
});
}
Ideally you should extract out that append code into its own function for readability, etc.
You can do it by simply using html template
HTML
First you need to add card-container id to the HTMl tag in which we will inject HTMl using ajax
<div class="container">
<div class="row">
<div class="col-sm-4" id="card-container">
</div>
</div>
</div>
Javascript
<script>
$(window).on('load', function () {
loadArticles();
});
function loadArticles() {
$.ajax({
dataType: "json",
url: "/articles", success: function (result) {
//Get template html using ajax and append it with **card-container**
var cardTemplate = $("#cardTemplate").html();
result.forEach(function (card) {
$('#card-container').append(cardTemplate.replace("{title}",
card.title).replace("{content}", card.content));
})
}
});
}
</script>
HTML Template
Assign id cardTemplate to html template
<template id="cardTemplate">
<div class="card">
<div class="card-body">
<h4 class="card-title">{title}</h4>
<p class="card-text">{content}</p>
Read...
</div>
</div>
</template>
I have also implemented on my end so it will surely gonna work !!!

Use JsonResponse variable from Django in Html via ajax

I am returning JsonResponse with the required hash from Django view on a ajax call.
How to use the Json object inside html via {{}} (jinja templating). Below is my ajax call:
$(function () {
$('#getData').submit(function (e) {
e.preventDefault();
$.ajax({
url: "/Report",
type: 'get',
data: {
'date1': $('#d1').val(),
'date2': $('#d2').val(),
},
success: function (data) {
alert("Success");
// How to pass the data here to use it in html
}
});
});
});
My sample html :
<div id="maindiv" class="col col-5 col-sm-10" style="display: none;">
<div>
<h3> Showing Results for {{info.fromDate}} to {{info.toDate}}</h3>
</div>
<br><br>
<div id="summary">
<div class="card-deck">
<div class="card mx-auto">
<div class="card-body text-center">
<p style="text-align: center;vertical-align: middle;padding: 20px;" class="card-text">
<h1><b><span style="font-size:80px;">{{info.total}}</span></b></h1>
<h6> Total </h6>
</p>
</div>
</div>
<div class="card" id='chart1' style="width: 100%; height: 500px;">
<div class="card-body text-center">
<script>
Info = {{ info.marks| safe }}
createpiechart("marks", 'chart1', Info); <!-- This creates an amchart -->
</script>
</div>
</div>
</div>
</div>
This is just a sample. I have many more charts and processing for which I used the jsonresponse variable via {{}} inside html. What is the correct way to get the data from ajax to html?
Initially I used render to return response to html. But I see that the data I capture in ajax has the entire html replaced with the {{}} variable's value
return render(request=request, template_name='home/home.html', context={"info": InfoArray})
How to use the hash/context I pass from Django view inside html via a ajax ?
App\home.urls file:
urlpatterns = [
#path('', views.index, name = "index"),
path('', views.homepage, name="homepage"),
]
App\urls file:
urlpatterns = [
path('Report/', include('home.urls')),
path('admin/', admin.site.urls),
]

res.render() function rendering an ejs page doesn't refresh the UI, but the ejs page get called

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
}
})
}

Node.js error: [Error: Query is required]

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.

Handlebars.js returns empty markup

I have a problem with rendering handlebars.js template. I'm doing AJAX request to server, and servers returns me data which contains 'dishes' array with objects.Dish object contains id,price,weight,description,and array of photos. Then i render it with handlebars,and its works properly, 'html' variable containts rendered markup.
var data ;
var modalDishInfo;
var modalDishComments;
var vitrina;
function ajax(params){
$.ajax({
url: '/admin/getDishByCategory',
type: 'POST',
dataType: "json",
data: params,
success: function (result){
data = JSON.parse(result);
vitrina = Handlebars.compile( $('#vitrina_template').html() );
modalDishInfo = Handlebars.compile( $('#modalDishInfo').html() );
var html = vitrina(data.dishes);
console.log(html);
$('.foodmenucontent').empty();
$('.foodmenucontent').append(vitrina(data.dishes));
}
<script id="vitrina_template" type="text/x-handlebars-template">
{{#each this}}
<div class="col-lg-3 mealcard" >
<p class="text-center mealname">{{ dish_name }}</p>
<div class="weightandprice">
<div class="weightcontainer"><span class="mealweight pull-right">{{ dish_weight }} грамм</span></div>
<div class="pricecontainer"><span class="mealprice pull-left">{{ dish_price }} руб.</span></div>
</div>
<button class="orderbutton center-block">ЗАКАЗАТЬ</button>
</div>
{{/each}}
</script>
As you can see this code renders elements, which contains links with openModal() function.I have empty bootstrap modal window and want to render its content, according to clicked link.
function openModal(id){
var foo = id.slice(-1);
var modaldata = data.dishes;
var modaldish = $.grep(modaldata, function (element) {
return element.id == foo;
});
modaldish = modaldish[0];
console.log(modaldish);
var markup = modalDishInfo(modaldish);
$('#modalDishInfo').empty();
$('#modalDishInfo').append(markup);
$('#modalDish').modal('show');
$('.fotorama').fotorama();
};
and template
<script id="modalDishInfo" type="text/x-handlebars-template">
<div class="modalcontainer">
<div class="row">
<div class="col-lg-6">
<div class="fotorama" data-nav="thumbs" data-width="100%" data- thumbwidth="80" data-thumbheight="45"
data-transition="crossfade" data-loop="true" data-keyboard="true" data-navposition="bottom"
data-fit="cover" data-ratio="1600/900">
{{#each dish_photos}}
<img src="/uploads/gallery/{{ this.path }}" class="img-responsive" alt="">
{{/each}}
</div>
</div>
<div class="col-lg-6">
<p class="mealname">{{ dish_name}}</p>
<pre>{{ dish_description }}</pre>
<p>{{ dish_weight }}гр.</p>
<p class="mealprice">{{ dish_price }}руб.</p><br>
<button class="orderbutton ">ЗАКАЗАТЬ</button>
</div>
</div>
The problem is second template(modalDishInfo) dont want to render, console.log returns 'markup' variable completely empty. I tried different combinations of block helpers, and expressions,but none of them working. Maybe im missing something important? Or need to use specific expressions, when passing single object to template?
I found mistake that i did.My template id and container id(which is empty) are the same. So jquery selector returned me empty markup. I didn't notice that, because im using TWIG and my scripts was in {% verbatim %} tag to avoid TWIG errors, so i dont receive any errors or warning using duplicate ID's in code. Hope my answer is informative and can be helpful to someone.

Categories

Resources