How to loop through JavaScript object in HTML? - javascript

I want to loop through a JavaScript object and repeat an html script as many times as the object length.
Here, I have the following in a script tag
<script>
var obj;
ipcRenderer.on('requests-results', (event, hosSchema) => {
obj = hosSchema
})
</script>
obj is an array retrieved from Mongo database as the picture below shows:
and I have the following inside <body> tag:
<div class="row">
<div class="col-md-4 col-sm-4">
<div class="card">
<div class="card-content">
<span class="card-title">.1.</span>
<p>.2.</p>
</div>
<div class="card-action">
.3.
.4.
</div>
</div>
</div>
</div>
How can I loop through obj to repeat the code between <div> tag as many times as obj.length?

I would suggest you to use Handlebars as #Amit mentioned.
first move out the code inside <div id="page-inner"> as below:
<div id="page-inner">
</div>
<script id= "requests-template" type="text/x-handlebars-template">
<div class="row">
{{#each requests}}
<div class="col-md-4 col-sm-4">
<div class="card">
<div class="card-content">
<span class="card-title">{{this.fieldName}}</span>
<p>{{this.fieldName}}</p>
</div>
<div class="card-action">
{{this.fieldName}}
{{this.fieldName}}
</div>
</div>
</div>
{{/each}}
</div>
</script>
Then inside another script page of type text/javascript you create the requests and assigned obj/hosSchema to it as below:
<script type="text/javascript">
var requestInfo = document.getElementById('requests-template').innerHTML;
var template = Handlebars.compile(requestInfo);
var requestData = template({
requests: obj
})
$('#page-inner').html(requestData);
</script>
NOTE: you need handlebars package installed (npm install handlebars --save)

Use templating script like Handlebars.js, Mustache.js or underscore.js.
Check below link for more description.
http://www.creativebloq.com/web-design/templating-engines-9134396

Try this:
var divlist = document.getElementsByTagName['div'];
var duplicate = null;
var rowIndex = -1;
var found = false;
for(var i = 0;i<obj.length;i++)
{
if(!found)
for(var p = 0;p<divlist.length;p++)
{
if(rowIndex != -1 && duplicate != null)
{
//set a Boolean to true and break
found = true;
break;
}
if(divlist[p].className == "col-md-4 col-sm-4")
{
//copy the element
duplicate = divlist[p];
}
else if(divlist[p].className == "row")
{
//identify the row's index
rowIndex = p;
}
}
//append the duplicate
divlist[rowIndex].appendChild(duplicate);
}

Related

Shopify metafields AJAX call to get object data as a variable

I have been trying for hours to figure out how to get Shopify product metafields as variables when I am constructing a compare products page.
I can get the product, and I can get the metafields now. The issue is making the metafields available outside the AJAX call.
I can push the metafields object to an array, but I cannot seem to access any of the keys within the array for some reason.
Code below. The product in the first function is actually the product ID.
function singleProductCall(product) {
//get all products in store (via AJAX) and filter
jQuery.getJSON(`/products.json`, function(response) {
let all_products = response.products;
const keep = parseInt(product, 10);
single_product = all_products.filter(obj => obj.id == keep);
single_product.forEach(product => {
let iter =
document.querySelectorAll('#compareContainer .compare-box').length + 1;
let fields = getMetafields(product);
console.log('as variable', fields);
createCompareItem(product, iter, fields);
});
initAccordion()
});
}
function getMetafields(product) {
let fields = [];
$.ajax({
type: 'GET',
url: `/products/${product.handle}?view=metafields`,
success: function(response) {
metafields = JSON.parse(response);
fields.push(metafields);
console.log('within ajax', metafields);
},
error: function(status) {
console.warn('ERROR', status);
}
});
return fields;
}
And here is the createCompareItem function, where I want to use the metafield values.
function createCompareItem(product, iter, fields) {
$('#compareContainer').append(`
<div id="compare-${
product.id
}" class="pure-u-1 pure-u-sm-1-3 compare-box pure-box">
<div class="box-${iter}">
<div class="compare-box-header">
<img src="https://via.placeholder.com/160x60.png" alt="${
product.title
}" class="product-name" />
</div>
<div class="green-box-container">
<div class="accordion accordion-hide-first product-features-accordion">
<h2>The Rundown</h2>
<div class="content">
<p>${product.title}</p>
</div>
<h2>Key Features</h2>
<div class="content">
<p>${product.id}</p>
</div>
<h2>Benefits</h2>
<div class="content">
<p>${product.handle}</p>
</div>
<h2>Ingredients</h2>
<div class="content">
<p>${product.url}</p>
</div>
<h2>Amino Acids</h2>
<div class="content">
<div class="chart">
<div class="chart-left">Alanine</div>
<div class="chart-right">0.900g</div>
</div>
<div class="chart">
<div class="chart-left">Alanine</div>
<div class="chart-right">0.900g</div>
</div>
<div class="chart">
<div class="chart-left">Alanine</div>
<div class="chart-right">0.900g</div>
</div>
</div>
</div>
</div>
</div>
<button onClick="removeCompare(${
product.id
})" class="button"><i class="button-left"></i><span>Remove</span><i class="button-right"></i></button>
</div>
`);
}
Currently the fields variable is returning an array with a single object in it, which contains the metafield keys and values. However I am having trouble accessing the keys and values.
Any help is appreciated as I have been at this for a while and can't find a solution!!

Find first instance of specific value and add ID, repeated

Given a series of div's with known values is it possible to create a filter or array that finds the first instance of the values and adds an ID over multiple items? Here's the basic structure:
<div class="boxes">
<div class="box"><time>2017</time></div>
<div class="box"><time>2016</time></div>
<div class="box"><time>2015</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2013</time></div>
</div>
And here's the snippet I'm using to find one of the values:
var elems = $('.box').filter(function(){
return this.textContent.trim() === "2014"
}).first().attr('id', 'one');
I'm not sure the best way to go about looking for additional instances?
For example, it feels like there's a better way than simply repeating the argument. The novice in me admittedly does not know what this type of function would be called.
var elems = $('.box').filter(function(){
return this.textContent.trim() === "2014"
}).first().attr('id', 'one');
var elems = $('.box').filter(function(){
return this.textContent.trim() === "2017"
}).first().attr('id', 'one');
It would be a bonus to not have to add the specific value, i.e. 2017 (<time id="one">2017</time>), 2016 (<time id="two">2016</time>) but I'm not even sure if that's realistic.
Working Fiddle: https://jsfiddle.net/heykenneth/gn4gmvt0/1/.
You can do this by wrapping up parameters of the code you've already written:
markFirstYear("2014", "one");
markFirstYear("2015", "two");
markFirstYear("2016", "three");
markFirstYear("2017", "four");
// ... etc
function markFirstYear(year, id) {
var elems = $('.box').filter(function(){
return this.textContent.trim() === year
}).first().attr('id', id);
}
#one {color:red;}
#two {color:blue;}
#three {color:green;}
#four {color:purple;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="boxes">
<div class="box"><time>2017</time></div>
<div class="box"><time>2016</time></div>
<div class="box"><time>2015</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2013</time></div>
</div>
First create array of all values you have, then just get unique values from that array and finally iterate through unique array.
var myArr = new Array();
$('.box time').each(function(){
myArr.push($(this).text());
});
var unique = myArr.filter(function(item, index, array) {
return index == myArr.indexOf(item);
});
for (var i = 0; i <= unique.length; i++) {
var elems = $('.box').filter(function() {
return this.textContent.trim() === unique[i];
}).first().attr('class', 'one');
}
.one {color:red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="boxes">
<div class="box"><time>2017</time></div>
<div class="box"><time>2016</time></div>
<div class="box"><time>2015</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2014</time></div>
<div class="box"><time>2013</time></div>
</div>

MeteorJS get MongoDB ID in click event

I have the following mongo collection in my MeteorJS app:
//Server side, load all the images
Images = new Mongo.Collection('images');
Meteor.startup(() => {
if(Images.find().count() == 0){
for(var i = 1; i < 23; i++){
Images.insert({
src:"img_"+i+".jpg",
alt:"image "+i
});
}
}
});
I pass that to a template and that works. However, I then want to retrieve the MongoDB id of an image (id that is the primary key/unique ID in MongoDB). I do it with the following code:
//Client side, get the collection and load it to the template
Images = new Mongo.Collection('images');
Template.images.helpers({images:Images.find()});
Template.images.events({
'click .js-del-image': (event) => {
var imageId = event._id;
console.log(imageId);
}
});
This gives me undefined. What am I doing wrong? I thought this._id should give me the MongoDB ID.
For the record, This is my template, the _id attribute gets filled out:
<template name="images">
<div class="row">
{{#each images}}
<div class="col-xs-12 col-md-3" id="{{_id}}">
<div class="thumbnail">
<img class="js-image img-responsive thumbnail-img" src="{{src}}"
alt="{{alt}}" />
<div class="caption">
<p>{{alt}}</p>
<button class="js-del-image btn btn-warning">delete</button>
</div>
</div>
</div> <!-- / col -->
{{/each}}
</div> <!-- / row -->
</template>
The problem was in the declaration of a function:
(event) => { ... } seems to have _id of undefined.
function(event) {...} seems to have the correct context.
See this answer for further information about (event) => {...} vs function(){...} declarations.

Binding links within an ember helper

I'm new to ember.js and ember-cli, all has been going well until I just tried to create my first custom helper.
I'm trying to loop through a model, displaying some image thumbnails on multiple rows within the page.
Everything seems to be working fine but I would like to try and bind the links.
Here's my custom helper:
import Ember from "ember";
export default Ember.Handlebars.makeBoundHelper(function(value, options) {
var out = '';
var b = 0;
for (var i=0; i<value.length; i++) {
b++;
if(b === 1){
out += '<div class="row">';
}
out += '<div class="col-md-4 col-sm-6 col-xs-12 center" style="text-align:center;">\
<div class="row center">\
<div class="col-md-12 center">\
<a href="photo/'+value[i]._data.id+'">\
<img class="center" src="'+value[i]._data.thumb_url+'" />\
</a>\
</div>\
</div>\
<div class="row center">\
<div class="col-md-6">'+value[i]._data.status+'</div>\
<div class="col-md-6"></div>\
</div>\
</div>';
if(b === 3){
out += '</div><div class="row"><div class="col-md-12"> </div></div>';
b=0;
}
}
return new Handlebars.SafeString(out);
});
I know that you can't use link-to directly inside a helper so I've been playing around with different options, with no luck.
The most success I had was trying to run link-to manually using something along the lines of:
Ember.Handlebars.helpers.linkTo.call('photo/1', 'photo.index', options);
But this hasn't been working out for me either.
Any tips? I fear I'm probably going about this in completely the wrong way
Edit
An example of the output I'm trying to achieve with a helper
<div class="row">
<div>
<a link><img></a>
</div>
<div>
<a link><img></a>
</div>
<div>
<a link><img></a>
</div>
</div>
<div class="row">
<div>
<a link><img></a>
</div>
<div>
<a link><img></a>
</div>
<div>
<a link><img></a>
</div>
</div>
You should probably create an Ember Component instead of creating a Handlebars helper. With an Ember Component you can use {{#linkTo}} and all the bindings work.
Use the Ember component to create a virtual property rows, where you set the items
of each row; then you can iterate over the rows and items with regular {{#each}} inside of the component template.
The component code would look like this:
App.SampleComponentComponent = Ember.Component.extend({
rows : function() {
var myRows = [];
var elements = this.get('model');
var b = -1;
for(var i = 0; i<elements.length; i++) {
if(i % 2 === 0) {
b++;
myRows[b] = [];
}
myRows[b][i%2] = elements[i];
}
return myRows;
}.property('model'),
});
The component template would look like:
<ul>
{{#each row in rows}}
<li>
<ol>
{{#each item in row}}
<li>{{item}}</li>
{{/each}}
</ol>
</li>
{{/each}}
</ul>
You will have to pass the array of items to the component in the model paramater.
Working example in: http://emberjs.jsbin.com/cowin/3/
The tutorial in http://emberjs.com/guides/components/ should help.

JQuery Sort Divs by child divs

I have the following list of divs and I'd like to be able to sort them using Javascript / JQuery.
<div class="item">
<div class="genre">Classical</div>
<div class="name">Alpha</div>
<div class="location">London</div>
</div>
<div class="item">
<div class="genre">Blues</div>
<div class="name">Bravo</div>
<div class="location">New York</div>
</div>
<div class="item">
<div class="genre">Pop</div>
<div class="name">Charlie</div>
<div class="location">Paris</div>
</div>
<div class="buttons">
Sort by Genre
Sort by Name
Sort by Location
</div>
I'd like to be able to sort the items by their Genre/Name/Location alphabetically.
Example: If Sort by Genre was clicked, it would sort the items in 0-9 A-Z by Genre.
If any of you have any tips it would greatly be appreciated.
Cheers :)
You have to make a little change to html like following:
<div id="container">
<div class="item">
<div class="genre">Classical</div>
<div class="name">Alpha</div>
<div class="location">London</div>
</div>
<div class="item">
<div class="genre">Blues</div>
<div class="name">Bravo</div>
<div class="location">New York</div>
</div>
<div class="item">
<div class="genre">Pop</div>
<div class="name">Charlie</div>
<div class="location">Paris</div>
</div>
</div>
<div class="buttons">
Sort by Genre
Sort by Name
Sort by Location
</div>
jQuery
function sorting(tag) {
var items = $('div.item').sort(function(a, b) {
var txt1 = $.trim($('div.' + tag, a).text()),
txt2 = $.trim($('div.' + tag, b).text());
if (txt1 > txt2) return 1;
else return -1;
});
return items;
}
$('.buttons a').on('click', function(e) {
e.preventDefault();
$('div#container').html(sorting(this.id));
});
Working Sample
Ok, this would be my pure JS solution.
First, we should wrap your <div>s into a larger container.
<div id = "wrapper">
<div id = "item">...</div>
<div id = "item">...</div>
<div id = "item">...</div>
</div>
Now, let's define a constant - which property do you want to sort it by? (this will probably be a function parameter later in your code).
var propName = "genre";
Let's get all the <div>s and put them in an array.
var items = document.getElementsByClassName("item");
var itemsArray = new Array();
Let us sort them lexicographically according to the text of the selected property.
for (var i = 0; i < items.length; i++)
itemsArray.push(items[i]);
itemsArray.sort(function(a, b) {
var aProp = a.getElementsByClassName(propName)[0].firstChild.nodeValue;
var bProp = b.getElementsByClassName(propName)[0] .firstChild.nodeValue;
if (aProp < bProp)
return -1;
else if (aProp > bProp)
return 1;
else
return 0;
});
Let us construct a document fragment consisting of the sorted <div>s.
var fragment = document.createDocumentFragment();
for (var i = 0; i < itemsArray.length; i++)
fragment.appendChild(itemsArray[i].clone());
Finally, let us clear the contents of the <div id = "wrapper"> and replace it with the document fragment.
document.getElementById('wrapper').innerHTML = '';
document.getElementById('wrapper').appendChild(fragment);
Also, note that document.getElementsByClassName does not work in IE<9, but I was now really lazy to cope with that issue.
A fiddle: http://jsfiddle.net/nNXr4/
Check this beast:
function sortByCreatedOnAsc(a,b){
return $(a).find('.created_on').text() > $(b).find('.created_on').text();
}
function sortByCreatedOnDesc(a,b){
return $(a).find('.created_on').text() < $(b).find('.created_on').text();
}
function reorderEl(el){
var container = $('#tasks');
container.html('');
el.each(function(){
$(this).appendTo(container);
});
}
$('#created_on').click(function(){
if($(this).hasClass("asc")){
reorderEl($('.task').sort(sortByCreatedOnDesc));
$(this).removeClass("asc");
$(this).addClass("desc");
} else {
reorderEl($('.task').sort(sortByCreatedOnAsc));
$(this).removeClass("desc");
$(this).addClass("asc");
}
return false;
});
jsfiddle: http://jsfiddle.net/jKJc3/116/

Categories