Append inner objects in multi-dimensional array with a loop - javascript

I have a multi-dimensional array of comment objects and I figured out how I can loop through the first-level of objects and then append them, but I am having a really hard time figuring out how to bring the replies along with the comments that have replies. My strategy was to use an "if" statement that checks to see if there's a reply, and then loops through the replies like I do the comments, but I think that the closest() function is the issue, I'm not quite sure how to access the DOM element that corresponds to that comment to use it as a selector. I think maybe another strategy would be to process the replies at the same time as the comments with one .each loop. Any help would be greatly appreciated!
var myCommentArray = [
{
_id: "888888888888888888",
index: 1,
name: "Perez",
message: "First Comment .......",
subject: "enim officias",
replies: [ // notice this comment has replies (just 1 but it is still an array)
{
_id: "77777777777777777777",
index: 0,
name: "Reply to First Comment Ines for Perez",
message: "...",
subject: "reply subject consequat"
}
]
},
{
_id: "999999999999",
index: 0,
name: "Shelton",
message: "2nd Comment....a",
subject: "enim irure",
replies: null // notice this comment has no replies and as such is null. this is better than an empty array
},
{
_id: "666666666666666666",
index: 2,
name: "Perez",
message: "3rd Comment.......",
subject: "enim officias",
replies: [
{
_id: "55555555555555555555",
index: 0,
name: "1st Reply to 3rd Comment",
message: "...",
subject: "reply subject consequat"
},
{
_id: "44444444444444444444",
index: 1,
name: "2nd Reply to 3rd Comment",
message: "...",
subject: "reply subject consequat"
}
]
}
];
sabio.page.processComments = function (i, currentComment) {
var commentsFormat = '<br> <div class="comment-avatar media-left"> <img src="http://placehold.it/50x50" alt="avatar">' +
'</div><div class="comment-content media-body clearfix"> <div class="comment-avatar media-left"></div><h3 class="media-heading">' +
currentComment.subject + '</h3> <div class="comment-meta">By ' + currentComment.name + '</div> <div class="comment-body"> <p>'
+ currentComment.message + '</p><a href="#" class="replyButton">' +
'<i class="fa fa-reply"> </i> Reply </a> </div> </div>';
$('.comments').append(commentsFormat);
};
$.each(myCommentArray, sabio.page.processComments);
sabio.page.processReplies = function (j, currentComment) {
var repliesFormat = '<br> <div class="comment-avatar media-left"> <img src="http://placehold.it/50x50" alt="avatar">' +
'</div><div class="comment-content media-body clearfix"> <div class="comment-avatar media-left"></div><h3 class="media-heading">' + currentComment.subject + '</h3> <div class="comment-meta">By ' + currentComment.name + '</div> <div class="comment-body"> <p>' + currentComment.message + '</p><a href="#" class="btn btn-gray more reply">' +
'<i class="fa fa-reply"> </i> Reply </a> </div> </div>';
var closestComment= $(currentComment).closest();
$(closestComment).append(repliesFormat);
});
if (myCommentArray.replies) {
$.each(myCommentArray.replies, sabio.page.processReplies);
};

There is definitely some misunderstanding here of what jQuery's .closest takes as a parameter and there are some curious scoping errors with currentComment. However, the primary thing to fix is that if replies is a property of each comment object, then you will need to process the replies for each comment item - that is, the code to process replies should be handled somehow by the processComments function.
With that said, I will propose to you what I think is a better solution to your problem. Because you are trying to render HTML from your comments array, I think a much cleaner and elegant solution would be to use a JavaScript template. I will use the .template method from the Underscore.js library:
<script id="CommentsTemplate" type="text/template">
<div>
<% _.each(comments, function (comment) { %>
<div class="comment-avatar media-left">
<img src="http://placehold.it/50x50" alt="avatar" />
</div>
<div class="comment-content media-body clearfix">
<div class="comment-avatar media-left"></div>
<h3 class="media-heading"><%- comment.subject %></h3>
<div class="comment-meta">By <%- comment.name %></div>
<div class="comment-body">
<p><%- comment.message %></p>
<i class="fa fa-reply"> </i> Reply
</div>
</div>
<div>
<% _.each(comment.replies, function (reply) { %>
<div class="comment-avatar media-left">
<img src="http://placehold.it/50x50" alt="avatar" />
</div>
<div class="comment-content media-body clearfix">
<div class="comment-avatar media-left"></div>
<h3 class="media-heading"><%- reply.subject %></h3>
<div class="comment-meta">By <%- reply.name %></div>
<div class="comment-body">
<p><%- reply.message %></p>
<i class="fa fa-reply"> </i> Reply
</div>
</div>
<% }); %>
</div>
<% }); %>
</div>
</script>
With our template in place, our rendering can be reduced to some very simple code:
var template = _.template($('#CommentsTemplate').html());
$('.comments').html(template({ comments: comments }));

Related

how to add <a> HTML tag or URL in object array in .ejs

I am new to creating array object , and I dont know how to add link in <a> tag in an array
object code:
powerUpList: [
{
id:1,
name:'Stack Export',
description:'Export your complete stack back in an excel sheet. ',
url: "https://help.stackby.com/article/145-export-stack-in-excel",
category:'Data Recovery',
status:0,
},
{
id:2,
name:'Microsoft Excel Importer',
description:'Import an excel file to create a new stack or import it to a table. ',
url: "https://help.stackby.com/article/146-excel-importer",
category:'Import',
status:0,
},
{
id:3,
name:'Append CSV file in an existing table',
description:'Import a CSV file into an existing table and map the columns to take the form of your table. ',
url: "https://help.stackby.com/article/144-import-data-in-an-existing-table",
category:'Data Transformation',
status:0,
},
],
powerUpCategoryList: [
'Data Recovery',
'Import',
'Data Transformation',
'Sharing',
'Automation',
'Apps',
],
this is my JS object code
and this is actual code in .ejs where all instance perform , and the accordian does not close ,
powerup.ejs
<div class="col-md-9" id="root">
<div role="tablist" id="accordion-1">
<%for (var i = 0; i < powerUpList.length; i++) { %>
<div class="card">
<div class="card-header" role="tab">
<a data-toggle="collapse" aria-expanded="true" aria-controls="accordion-1 .item-<%= i+1 %>" href="div#accordion-1 .item-<%= i+1 %>"><%= powerUpList[i].name%></a>
<%if (powerUpList[i].status == 0) { %>
<a class="live-lable status-live" >
Live
</a>
<%}else if(powerUpList[i].status == 1){-%>
<a class="live-lable status-comingsoon" >
Coming soon
</a>
<%}else if(powerUpList[i].status == 2){-%>
<a class="live-lable status-beta" >
In-Beta
</a>
<%}-%>
</div>
<div class="collapse show item-<%= i+1 %>" role="tabpanel" data-parent="#accordion-1">
<div class="card-body">
<p class="card-text" ><%= powerUpList[i].description%></p>
--------- HERE I WAnt to show **URL** as learn more
Learn more..
</div>
</div>
</div>
<%}-%>
</div>
</div>
here , I want to show in url: "https://help.stackby.com/article/145-export-stack-in-excel", in as download link , and open new window , but i don't know how to add in <a> tag in file
From what I understand, you can do
learn more

How to identify multiple forms in one template?

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.

Type error when sending 'data' in post request

So I am trying to send a POST request with an object that has a property for "areas". This will return me some bit of data that is located only around some area. The API I am using is in php but I'm using javascript. I have already made POST requests and have been getting information back, but I can't get it to work when I try to tell it what "area" I want to use. The API says that it takes an array for the area, but when I do it in javascript, I'm getting a TypeError. Here is the API:
search_area_name: Search within specific areas. Recommend to build a pick list to choose from if this search critieria will be used.
values: (array)
An example of setting up the data in php is as so:
$data = array (
'partner_key' => '7e52cad4e91ee36e308d35f93a9db02b',
'action' => 'propertySearch',
'return' => 'xml',
'search_offset' => '0',
'search_limit' => '100',
'search_mls_id' => array('1'),
'search_area_name'=>array("Kimball","Silver Creek Commercial"),
'debug' => '0'
);
My POST request in javascript with my data set up:
app.get("/listing", function (req, res) {
var listingID = req.query.id, //passing these in later..
listingAddress = req.query.address,
listingArea = [req.query.area];
var data = {
partner_key : '7e52cad4e91ee36e308d35f93a9db02b',
action : 'propertySearch',
return : 'json',
search_offset : '0',
search_limit : '15',
search_mls_id : ['1'],
search_area_name : listingArea,
debug : '0'
};
request.post({url: url, formData: data}, function(err, httpResponse, body) {
if (!err && httpResponse.statusCode == 200) {
var mlsData = JSON.parse(body);
res.render('listing', {mlsData: mlsData['data']});
}
else {
return console.error('upload failed:', err);
}
});
});
So I realize that it is a lot to read, but if I take out the search_area_name property, all is well and I receive the exact response I expect. When I run the script with the search_area_name property, I receive TypeError. Any advice or input would be greatly appreciated!
Edit, Additional Info:
This is what my browser will display (put it all in there just in case):
TypeError: /Users/blazekotsenburg/WebstormProjects/ResidePC/assets/views/listing.ejs:129
127| <%include partials/filter-suggest.ejs%>
128|
>> 129| <%include partials/listingGridTemplate.ejs%>
130|
131| <%include partials/footer.ejs%>
esc is not a function
at rethrow (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/ejs/lib/ejs.js:285:18)
at eval (eval at compile (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/ejs/lib/ejs.js:524:12), <anonymous>:268:9)
at eval (eval at compile (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/ejs/lib/ejs.js:524:12), <anonymous>:270:10)
at returnedFn (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/ejs/lib/ejs.js:555:17)
at tryHandleCache (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/ejs/lib/ejs.js:203:34)
at View.exports.renderFile [as engine] (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/ejs/lib/ejs.js:412:10)
at View.render (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/express/lib/view.js:128:8)
at tryRender (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/express/lib/application.js:640:10)
at Function.render (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/express/lib/application.js:592:3)
at ServerResponse.render (/Users/blazekotsenburg/WebstormProjects/ResidePC/node_modules/express/lib/response.js:971:7)
and the listingGridTemplate.ejs, which is where I guess it is failing even though it works when that property is not passed, is as so:
<div class="row grid-list"><!-- Begin First Row-->
<div class="container">
<h1 id="featured-listings">Featured Listings</h1>
<% mlsData.forEach(function(listing) { %>
<div class="col-lg-4 col-md-4 col-sm-6">
<div id= "<%= listing['list_id']%>">
<% var listingImage = listing['photos'].split(",");%>
<% if (!listingImage[0]) { %>
<div class="thumbnail photo-card">
<%include listingGridStatus.ejs%>
<a href="/listing?id=<%=listing['list_id']%>?area=<%=listing['area_name']%>?address=<%=listing['address']%>">
<img class="img-responsive" src="https://source.unsplash.com/wtrAchtpc-w">
<p class="no-image-header">No Images <i class="fa fa-frown-o" aria-hidden="true"></i></p>
<section class="photo-card-info-spec-row1">
<span class="photo-card-price">
$<%= listing['price'].replace(/\B(?=(\d{3})+(?!\d))/g, ",") %>
</span>
<span class="photo-card-info">
<%include listingGridPropCheck.ejs%>
</span>
</section>
<section class="photo-card-info-spec-row2">
<span class="photo-card-dom">
<span class="dom">dom:</span>
<%= listing['days_on_market']%>
</span>
<span class="photo-card-punct">|</span>
<span class="photo-card-address">
<%= listing['address']%>,
<%= listing['city']%>,
<%= listing['state']%>
</span>
</section>
</a>
</div>
<% } else { %>
<div class="thumbnail photo-card">
<%include listingGridStatus.ejs%>
<a href="/listing?id=<%=listing['list_id']%>&address=<%=listing['address']%>&area=<%=listing['area_name']%>">
<img class="img-responsive img-drop-shadow" src="http://www.realcove.net/<%= listingImage[0]%>">
<section class="photo-card-info-spec-row1">
<span class="photo-card-price">
$<%= listing['price'].replace(/\B(?=(\d{3})+(?!\d))/g, ",") %>
</span>
<span class="photo-card-info">
<%include listingGridPropCheck.ejs%>
</span>
</section>
<section class="photo-card-info-spec-row2">
<span class="photo-card-dom">
<span class="dom">dom:</span>
<%= listing['days_on_market']%>
</span>
<span class="photo-card-punct">|</span>
<span class="photo-card-address">
<%= listing['address']%>,
<%= listing['city']%>,
<%= listing['state']%>
</span>
</section>
</a>
</div>
<% } %>
</div>
</div>
<% }); %>
</div>
</div>
I believe you simply need to remove the square brackets surrounding req.query.area
listingArea = req.query.area;
Sorry for not posting this as a comment (do not have 50 rep yet). A suggestion I could give you is for you to put some debugging messages to see what exactly gets passed to your variables.
put a console.log([req.query.area]); just above your var definitions, and put one more console.log(listingArea) just before your post request. Check your console if you are getting some gibberish values and that should be your starting point. Hope this helps you out!
EDIT:
I believe I found a relevant answer that might help you out.
Try https://stackoverflow.com/a/15851761/7654934

jQuery on click function or random quote machine and JSON API

For the first time using JSON API. Please tell me where and what I'm doing wrong. This is a challenge from freeCodeCamp. We need to do build a random quote machine.
Once I click on New Quote button is should give us a random quote. In jQuery I'm looping through json and on click function has to change me current h2 class = text with the new random quote from JSON.
Project is here http://codepen.io/ekilja01/full/Lbdbpd/.
Please help.
Here is my HTML:
<div class="container-fluid">
<div class = "well">
<div class="row">
<h2 class="text text-center"><i class="fa fa-quote-left"> </i> Hey, what when and why there is no and yes?</h2>
<p class="author">-Alina Khachatrian</p>
<div class="buttons">
<div class="row">
<div class="col-xs-6">
<a id="tweet-quote" title="Tweet current quote" target="_blank" href="#">
<i class="fa fa-twitter fa-2x"></i>
</a>
</div>
<div class="col-xs-6">
<button type="button" class="btn btn-default btn-transparent" id ="getNewQuote" title="Get a new quote">New Quote</button>
</div>
</div>
<footer class="text-center">
<hr>
<p>Written and coded by Edgar Kiljak.</p>
</footer>
</div>
</div>
and JS:
$(document).ready(function(){
$(".btn").on("click", function(){
$.getJSON("http://quotes.rest/qod.json", function (json) {
var html = "";
json.forEach(function(val){
var keys = Object.keys(val);
html += "<div class = 'newQuote>'";
"<h2 = '" + val.quote + "'>";
html += "</h2>";
html += "</div>";
})
$(".text").html(html);
});
});
});
and JSON:
{
"success": {
"total": 1
},
"contents": {
"quotes": [
{
"quote": "Great things are done by a series of small things brought together.",
"length": "67",
"author": "Vincent Van Gogh",
"tags": [
"inspire",
"small-things",
"tso-art"
],
"category": "inspire",
"date": "2016-12-10",
"title": "Inspiring Quote of the day",
"background": "https://theysaidso.com/img/bgs/man_on_the_mountain.jpg",
"id": "DLThmumKP4CCe1833rRvNQeF"
}
]
}
}
your problem is here: json.forEach(function(val)...
since the JSON is not an array, it should be: json.contents.quotes.forEach(function(val)
json.contents.quotes is an array ([brackets] instead of {}) and forEach is only for arrays
Please post the error you are getting.
Also in your .js file, you are doing :
"<h2 = '" + val.quote + "'>";
which should be
"<h2>'" + val.quote + "'</h2>";
Another advice would be to put the code where you are handling the response in .done(). This method as far as I know, is available with the $.get() method.
From the jQuery Docs,
$.get( "test.cgi", { name: "John", time: "2pm" } )
.done(function( data ) {
alert( "Data Loaded: " + data );
});

Clean way to create dom elements based on data from array?

I have an array full of objects that have the same keys, but different values. These objects need to be turned into divs with various sub elements. The following code works, but seems messy and hard to maintain.
pim.buildProducts = function(array) {
// for each item in the array
productHTML = '';
compareBoxHTML = '<div class="checkbox compare">' +
'<label data-placement="right" data-toggle="tooltip" title="Add to Compare"><span class="compare-text">Compare</span><input type="checkbox" value="compare"></label>' +
'</div>';
for(i = 0; i < array.length; i++) {
// build image
image = '<div class="image"><img src="' + array[i].image + '" /></div>';
// build description
desc = '<div class="desc"><h4>' + array[i].id + '</h4>' +
'<h5>' + array[i].description + '</h5>';
// build icons
icons = '<div class="pim-icons"><a class="cart" href="' + array[i].url + '"><span class="glyphicon glyphicon-shopping-cart"></span></a>';
if(array[i].catalog) {
icons += '<a class="file" href="' + array[i].catalog + '"><span class="glyphicon glyphicon-file"></span></a>';
}
icons += '</div>';
productHTML += '<div class="product">' + image + desc + icons + '</div>';
}
return productHTML;
};
Are there better ways of doing this, or other options that I could pursue?
Thanks!
Walter
As far as I know this is the cleanest way to do it.
First of all you should create all html like this;
<div hidden="hidden" id="mainDiv">
<div class="checkbox compare">
<label data-placement="right" data-toggle="tooltip" title="Add to Compare">
<span class="compare-text">Compare</span>
<input type="checkbox" value="compare">
</label>
</div>
<div class="image">
<a href="">
<img src="">
</a>
</div>
</div>
After that you should make the parent div hidden, and clone it to an object with .clone(). After that you just simply change values of the element like;
<script>
function fillData(array)
{
for(var i ; i<array.length;i++)
{
var element = $("#mainDiv").clone();
element.attr("hidden","");
element.find("img").attr("id",array[i].url);
$(document).append(element);
}
}
</script>
and append to where ever you want.
You could use document.createElement and the other DOM methods instead of writing HTML, but that may be even more verbose than using an HTML string.
Another option would be to use a template function like what lodash provides, which would make the HTML a lot cleaner.
The JS becomes:
pim.buildProducts = function(array) {
var tplHtml = document.getElementById('productsTpl').innerHTML;
var tpl = _.template(tplHtml);
return tpl({ products: array });
}
And you could put the template in your HTML file:
<script type="text/template" id="productsTpl">
<% products.forEach(function(item) { %>
<div class="product">
<div class="image">
<a href="<%= item.url %>">
<img src="<%= item.image %>" />
</a>
</div>
<div class="desc">
<h4><%= item.id %></h4>
<h5><%= item.description %></h5>
<div class="pim-icons">
<a class="cart" href="<%= item.url %>">
<span class="glyphicon glyphicon-shopping-cart"></span>
</a>
<% if (item.catalog) { %>
<a class="file" href="<%= item.catalog %>">
<span class="glyphicon glyphicon-file"></span>
</a>
<% } %>
</div>
</div>
</div>
<% }) %>
</script>
Take a look to document.createElement function and all others for DOM handling

Categories

Resources