MeteorJS get MongoDB ID in click event - javascript

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.

Related

How to trigger click event of anchor tag via jQuery for dynamic data?

Trying to trigger click event of a tag with jQuery but the event is linked with data set by $.getJSON in a section.
Following is the code of HTML:
...
const page=1, dataList=[];
$.getJSON('https://api.punkapi.com/v2/beers?page=' + page + '&per_page=25', function (beerList) {
console.log(beerList)
dataList.push(beerList);
currentElements = renderFuntion(beerList);
$('#dynamicData').html(currentElements);
});
function renderFuntion(data) {
var temp = '';
data.forEach(element => {
var item = `
<div class="card col-xs-12 col-lg-4 my-2">
<div class="card-body">
<figure> <img src=${element.image_url} class="img-fluid" alt=${element.name}></figure>
<article class="ms-3">
<h5 class="card-title">${element.name}</h5>
<p class="card-text">${element.description.length > 220 ? element.description.substring(0, 220) + '...' : element.description}</p>
</article>
<!--- This is what's suppose to give the event on click. --->
</i>
</div>
</div>`
temp += item;
});
return temp;
}
//this is what's not working
$('.card a').click(function () {
var usersid = $(this).attr("id");
favouritesList.push(usersid);
console.log(favouritesList);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<section class="my-3 g-0" id="dynamicData">
</section>
Please do guide if possible. Thanks.
Your code isn't working because your element isn't loaded with the page, so when jQuery runs $('.card a').click(function..., $('.card a') returns null, so the listener is not attached.
Add your listener to the document like so:
$(document).on("click", ".card a", function() { ... })
When the user clicks on the document, jQuery will check if $(".card a") is the target, even if added after the page was initially loaded, and will execute your function if it is.
ajaxComplete also solve your problem.
$(document).ajaxComplete(function () {
$('.card a').click(function () {
const favouritesList = [];
let usersId = $(this).attr("id");
favouritesList.push(usersId);
console.log(favouritesList);
})
})
As #will already pointed out: it is a timing problem. In your code the click event was attached before the contents form your $.getJSON() arrives. By using a delegated event attachment you can solve the problem:
const page=1, dataList=[], favouritesList=[];
$.getJSON('https://api.punkapi.com/v2/beers?page=' + page + '&per_page=25', function (beerList) {
// console.log(beerList)
dataList.push(beerList);
currentElements = renderFuntion(beerList);
$('#dynamicData').html(currentElements);
});
function renderFuntion(data) {
var temp = '';
data.forEach(element => {
var item = `
<div class="card col-xs-12 col-lg-4 my-2">
<div class="card-body">
<figure> <img src=${element.image_url} class="img-fluid" alt=${element.name}></figure>
<article class="ms-3">
<h5 class="card-title">${element.name}</h5>
<p class="card-text">${element.description.length > 220 ? element.description.substring(0, 220) + '...' : element.description}</p>
</article>
<!--- This is what's suppose to give the event on click. --->
click me</i>
</div>
</div>`
temp += item;
});
return temp;
}
//this is now working:
$('section').on("click",".card a",function () {
favouritesList.push($(this).attr("id"));
console.log(favouritesList);
});
.img-fluid {height:80px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<section class="my-3 g-0" id="dynamicData">
</section>
To access a dynamic element via click event you have to use something like this $(document).on('click','elementName',function(){})
I have made some minor changes and hope that they are what you are looking for. Happy coding!
var favouritesList = [];
const page=1, dataList=[];
$.getJSON('https://api.punkapi.com/v2/beers?page=' + page + '&per_page=25', function (beerList) {
dataList.push(beerList);
currentElements = renderFuntion(beerList);
$('#dynamicData').html(currentElements);
});
function renderFuntion(data) {
var temp = '';
data.forEach(element => {
var item = `
<div class="card col-xs-12 col-lg-4 my-2">
<div class="card-body">
<figure> <img src=${element.image_url} class="img-fluid" alt=${element.name}></figure>
<article class="ms-3">
<h5 class="card-title">${element.name}</h5>
<p class="card-text">${element.description.length > 220 ? element.description.substring(0, 220) + '...' : element.description}</p>
</article>
<!--- This is what's suppose to give the event on click. --->
</i> User Icon
</div>
</div>`
temp += item;
});
return temp;
}
//I've changed the way you are trying to get that ID from the anchor tag
$(document).on('click','a',function () {
var usersid = $(this).attr("id");
favouritesList.push(usersid);
console.log(favouritesList);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="my-3 g-0" id="dynamicData">
</section>

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!!

How to loop through JavaScript object in HTML?

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

How can I get specific information from an external api to my ng-repeat

main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
// I WANT THIS PART !!
<div class="well sideBars customShadow">
<img class="img-responsive" ng-src="{{film.Poster}}" title="{{film.Title}}">
<h4 class="text-center">{{film.Title}}</h4>
<p class="text-center" style="margin-bottom: 2px;"><b>Year:</b> {{film.Year}}</p>
<p class="text-center"><span class="customMargin">Runtime: {{film.Runtime}}</span></p>
<p class="text-center"><span class="customMargin">Director: {{film.Director}}</span></p>
<p class="text-center"><span class="customMargin">Writer: {{film.Writer}}</span></p>
<p class="text-center"><span class="customMargin">Actors: {{film.Actors}}</span></p>
</div>
</div>
</div>
This is part of my main.html . In h3 and p tags, I get imdbId and blogContent from my database and put it in ng-repeat in order to traverse blog posts in list. I want to be able get other information(under // I WANT THIS PART) for every post in myBlogPost.
MainController.js
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
$scope.myBlogPosts = response;
});
};
refresh();
This part work as expected when page loaded.
I need also these parts in Main Controller ;
var onGetFilmData = function (data) {
$scope.film = data;
};
var onError = function (reason) {
$scope.error = reason;
};
imdb.getImdbInfo(-- need Id --).then(onGetFilmData, onError);
But I need to put each post id somehow in order to get specific data from Imdb api.
Imdb.js
(function(){
var imdb = function($http){
var getImdbInfo = function (id) {
return $http.get('http://www.omdbapi.com/?i=' + id + '&plot=short&r=json')
.then(function(response){
return response.data;
});
};
return{
getImdbInfo: getImdbInfo
};
};
var module = angular.module('myApp');
module.factory('imdb', imdb);
})();
If I delete id part and put a specific id string in getImdbInfo function, all post in main.html fill with just one film information. I want to fetch those data for each film in my database(I am holding imdb id of each film in my database).
MainController
var jsonObj = {};
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
jsonObj = response;
for(var i = 0; i < jsonObj.length ; i++){
jsonObj[i].title = '';
}
for(var i = 0; i < jsonObj.length ; i++){
(function(i) {
imdb.getImdbInfo(jsonObj[i].imdbId).then(function (data) {
jsonObj[i].title = data.Title;
});
})(i);
}
$scope.myBlogPosts = jsonObj;
});
};
refresh();
main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
<!-- Side Widget Well -->
<div class="well sideBars customShadow">
<h4 class="text-center">{{post.title}}</h4>
</div>
</div>
</div>
I solve my problem with adding response from Imdb to my json object which is coming from database. So I can easily use them in ng-repeat.

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.

Categories