meteor: product grid with pictures in FS collection - javascript

I want to make a grid of a 'partner' collection
Each partner consist of a partnerGridForm template
<template name="partnerGridForm">
<div class="col-sm-4">
<div class="panel panel-default">
{{_id}}
{{#each profileImages}}
{{>partnerGridImage}}
{{/each}}
<h3 class="panel-title">{{profile.partner_company}}</h3>
<hr>
{{profile.partner_address_line_1}}<br>
{{#if profile.partner_address_line2}}
{{profile.partner_address_line2}}<br>
{{/if}}
{{profile.partner_zipcode}} {{profile.partner_city}}<br>
{{profile.partner_state}} - {{profile.partner_country}}<br>
{{profile.partner_phone_nr}}<br>
{{profile.partner_mobile_nr}}<br>
<hr>
<b>
{{profile.partner_category}}<br>
{{profile.partner_sub_category}}</b>
<hr>
{{{profile.partner_promo_text}}}
</div><!-- end panel -->
</div><!-- end feature -->
</template>
All these partnerGridForm templates are displayed on a page trough a loop trough the partner collection (which is in fact the user collection)
<template name="partnerGrid">
<div class="container container-top">
{{#each partners}}
{{>partnerGridForm}}
{{/each}}
</div> <!-- end container -->
</template>
The pictures for each partner are stored separately in a ProductImages.fs file
I want each partnerGridForm to display the images in the partnerGridForm but I can't make it work.
Trough the Reactive character of meteor all my gridforms are constantly updated with the last picture I get.
Currently i have the following helpers on the partnerGrid template
// Template Helpers
Template.partnerGrid.helpers({
partners: function () {
return Meteor.users.find();
},
});
// On rendering get lists
Template.partnerGrid.onRendered(function (event,template) {
if (typeof partnerSub !== 'undefined') {
if ( partnerSub != null ) {
partnerSub.stop();
};
};
partnerSub = Meteor.subscribe('partnerList');
});
and the following helpers on the partnerGridForm template
Template.partnerGridForm.helpers({
profileImages: function() {
return PartnerImages.find();
},
});
Template.partnerGridForm.onRendered(function(event,template){
currentPartner = Template.currentData() // Get current data in template
currentPartnerId = currentPartner._id;
if (typeof ImageSub !== 'undefined') {
if ( ImageSub != null ) {
ImageSub.stop();
};
};
ImageSub = Meteor.subscribe('partnerImages', currentPartnerId);
});
The problem is that the helpers on the partnerGridForm update each form that is rendered so each form wil always get the last image?
Can anyone help?

Related

JavaScript filter loop control. How can I handle every single element?

The problem is I can't control the filter remember I have 20 products I need to establish if my category is not matched else product not found.
But my code product not found returned 20 times 🤨
see my output Screenshot
html = `<p>Product not found</p>` // just + sign remove
I am trying another way it returns a single time but, if my condition is true the product does not show like
`if (post.category === "men's clothing")`
How can I solve it?
// Men catagory
men.addEventListener('click', loadDataMen);
function loadDataMen() {
fetch('./src/db/db.json')
.then(function (response) {
return response.json();
})
.then(function (info) {
let html = '';
info.filter((post) => {
try {
if (post.category === "no match") {
html += `
<div class="single_product product-box bg-darkopacity rounded-20 position-relative mb-5 grid_view p-3">
<!--
Hover Zoom you can cancel if you don't like it just remove "hover-zoom" class
-->
<div class="hover-zoom drop-shadow-product position-relative">
<!-- PLaceholder Image -->
<img src="${post.image}" alt="Image" />
<!-- User Rating -->
<div class="show-rating">
<div class="rate">
${post.rating.rate} ⭐ | ${post.rating.count}
</div>
</div>
</div>
<!-- Product Wishlist -->
<div class="love bubbly-button">
<i class="fa-regular fa-heart"></i>
</div>
<!-- Product Offer -->
<div class="product-tag-warning badge bg-warning">${post.tag}</div>
<div class="product-functionality text-center mt-3">
<!-- Product title -->
<div class="product-title fw-bold text-break">
${post.title.substring(0, 18)}...
</div>
<!-- Product price -->
<div class="product-price mb-2"><strong>${post.price} only</strong></div>
<!-- Router navigation -->
<div class="two-btn-sm">
View
Buy
</div>
</div>
<!-- Product Description -->
<div class="discription">
<small class="text-decoration-underline">
<strong>Discription</strong>
</small>
<p class="p-0">
${post.description} SeeMore
</p>
</div>
</div>
`;
}
else{
html += `<p>Product not found</p>`
}
} catch (error) {
html = `<p>Somthing went wrong ${error}</p>`
}
})
output.innerHTML = html
})
.catch(function (error) {
console.log(error)
})
}
The problem is that you are using the filter method as a loop.
For more info about filter method check this out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
You must filter your posts result using a condition like so:
let filteredPosts=info.filter((post)=>post.category === "no match");
then doing something like that:
if(filteredPosts.length<=0){
html='Product not found'
}else{
filteredPosts.splice(4); //only 4 posts
for(let post of filteredPosts){
html+=....
}
}
Improved response:
What you have to do to filter by category or color or both, you must save which category and color the user has selected:
let selectedCategory=null;
let selectedColor=null;
let categoryItems=document.getElementByClassName('category'); //assuming categories items has a class category
for(let categoryItem of categoryItems){
//foreach category found
categoryItem.addEventListener('click', function(event){
selectedCatory=this.innerHTML; //get the clicked category
showFilteredItems();
});
}
let colorsItems=document.getElementByClassName('color');
//assuming colors items has a class color
for(let colorItem of colorsItems){
//foreach color found
categoryItem.addEventListener('click', function(event){
selectedColor=this.innerHTML; //get the clicked color
showFilteredItems();
});
}
//this function is your old method
function showFilteredItems(){
....
fetch(...)
....
//by default all results
let filteredPosts=info;
//category filter
if(selectedCategory){
filteredPosts=filteredPosts.filter((post)=>post.category === selectedCategory);
}
//color filter
if(selectedColor){
filteredPosts=filteredPosts.filter((post)=>post.color=== selectedColor);
}
//show the items
if(filteredPosts.length<=0){
html='Product not found'
}else{
filteredPosts.splice(4); //only 4 posts
for(let post of filteredPosts){
html+=....
}
}
}
Keep in mind that in production application, we prefer to send filters(category and color) to the backend of the application that way you will not receive all the items (this can be really slow if you have thousands of items) but only the filtered.

How do I pass a value from an HTML tag to Vue's data object on each iteration of a for loop

I have a list of users and I have ordered them by title. When the list if created I want to check if the previous user has a different title than the current user and if so do some logic.
<div>
<div v-for="user in the_category`.`users" :key="user.id">
<h1 v-bind:title="user.title" >{{ user.title }}</h1>
// If the title is not the same as the previous user.title I would like the following <h3> below to show
// However if the title is the same as the previous user.title I do not want it to show.
// How do I pass the value/text of the <h1> to my title data variable
// and compare the value with the last value on each loop?
<h3 v-show="user.title != title">This is a new title</h3>
</div>
</div>
</template>
<script>
export default {
props: [
"the_category",
"title"
],
data: function() {
return {
category: this.the_category,
title: "",
};
},
}
</script>
```
You can get the index of your loop in your v-for and then use it to check the full array.
First, replace this:
v-for="user in the_category`.`users"
With this:
v-for="(user, index) in the_category`.`users"
That will get both the current element (user) and its index in the array (index).
Then, replace this:
<h3 v-show="user.title != title">This is a new title</h3>
With this:
<h3 v-show="index === 0 || user.title != the_category`.`users[index - 1].title">This is a new title</h3>
Your <h3> will then be visible if your element is the first (index === 0) or if the current title differs from the previous.

How do I implement Dragula.js for multiple containers for my Laravel app?

My question has to do with implementing the dragula library into my laravel app.
I have got dragula to work, but it only works on one 'team' object. I'm trying to create a loop where each team object will accept the nametag div. I've put the {{ $teamvalue->id }} in the id in order to create a unique id for each object, but this only lets the last team object that was generated be interacted with.
<div class="nameroll" id="nameroll">
#foreach($names as $key => $value)
<div class="nametag" draggable="true">
<p>{{ $value->name }}</p>
</div>
#endforeach
</div>
<div class="modroll">
#foreach($mods as $key => $value)
#foreach($modteams as $key => $teamvalue)
#if($teamvalue->mod_id === $value->id)
<div class="col-md-4">
<div class="title">
<h1>{{ $value->mod_intent }}</h1>
<h1>{{ $teamvalue->modteam_description }}</h1>
</div>
<div class="team" id="team{{ $teamvalue->id }}">
</div>
</div>
#endif
#endforeach
#endforeach
</div>
I then tried this in combination with putting a {{ $teamvalue->id }} in the js code.
<script type="text/javascript">
#foreach($modteams as $key => $teamvalue)
dragula([document.getElementById('nameroll'), document.getElementById('team{{ $teamvalue->id }}')], {
copy: function (el, source) {
return source === document.getElementById('nameroll')
},
accepts: function (el, target) {
return target !== document.getElementById('nameroll')
}
});
#endforeach
</script>
But because this creates multiple versions of the same code, they conflict and none of the team objects can be interacted with.
I just want to know how to create a loop so that the js code will allow dragula to work properly with each team object, so I can drag a nametag to each one and it'll stay there.
Thanks!
Edit: Clarification
The div Nameroll holds a collection of Users (Nametag).
The div Modroll holds a collection of Teams (Team).
I want to be able to drag a 'Nametag' object to any of the 'Team' Objects and have it be stored there. Currently, this works, but with only the last 'team' object in the collection. I've deduced that the js function for Dragula only fires off for the last collection as I've passed a value ( $teamvalue->id) and only the last id in the group exists in the function.
I think you can get rid of the foreach in the javascript. Try using the draggable="true" ondragstart="myFunction(event)" for the div class='team' and wrap the drag logic in a function.
<div class="team" id="team{{ $teamvalue->id }}" draggable="true" ondragstart="dragMe('{{$teamValue}}')">
</div>
When the div is dragged, The dragMe function would be called where laravel would convert the $teamValue to json so you can use directly in the javascript.
<script type="text/javascript">
function dragMe(teamValue){
dragula([document.getElementById('nameroll'), document.getElementById('team'+teamValue.id)], {
copy: function (el, source) {
return source === document.getElementById('nameroll')
},
accepts: function (el, target) {
return target !== document.getElementById('nameroll')
}
});
}
</script>

Why Props data passing between Parent to Child not working in VueJS?

Here, i use some list of data in Javascript variable to pass to VueJs Template Loop.
My Javascript Variable,
var templates = {
0:{
'nb':1,
'column':2
},
1:{
'nb':1,
'column':2
}
}
My Parent Template is,
<div v-for="(te,index) in templates" class="row">
<campaign_segment :t_nb="te.nb" :t_column=te.column> </campaign_segment>
</div>
Parent Template's Source,
<template v-if="showTemplate" id="segment_body">
<b>#{{ t_nb }}</b>
<div v-for="a in t_nb">
<block v-if="t_column == 4 || t_column > 4"></block> // <== Child Template,
</div>
</template>
Child Template's Source,
<template id="campaignBlock">
<b>HIT</b>
</template>
My VueJS,
// For Parent Template
Vue.component(
'campaign_segment', {
template: '#segment_body',
props: ['t_nb', 't_column']
});
// For Child Template
Vue.component('block', {
template: '#campaignBlock'
});
In general,
<div v-for="a in t_nb">
<block v-if="t_column == 4 || t_column > 4"></block> // <== Child Template,
</div>
No loop were generated.
here, if i use,
<div v-for="a in 2">
<block v-if="t_column == 4 || t_column > 4"></block> // <== Child Template,
</div>
Loop is working fine.
Whats wrongs with this, why vuejs never uses the same instance to process the loop ?
What I see is, there can be two issees:
Having multiple components under template, so you can put all of them in a single div, as is solved here.
Using parseInt in v-for, as it can be passed as a string in props instead of an integer, you can also put data type of props as explained here.
HTML
<template>
<div v-if="showTemplate" id="segment_body">
<b>#{{ t_nb }}</b>
<div v-for="a in parseInt(t_nb)">
<block v-if="t_column == 4 || t_column > 4"></block> // <== Child Template,
</div>
</div>
</template>

knockoutjs binding to a ko.observableArray of the selected child view model in a main view model

in knockoutjs i have a main view model which has a collection of child view models as in the following
//apply binding on document ready
$( document ).ready(function() {
ko.applyBindings(new MainviewModel);
});
//main view model, contains a child view models ko.observableArray
var MainviewModel = function()
{
// ko.observableArray of subviewmodels
this.subViewModels= ko.observableArray
([
new SubViewModel('A'),
new SubViewModel('B'),
new SubViewModel('C')
]);
//selected subviewmodel
this.selectedSubViewModel = ko.observable();
//set slected subviewmodel
this.selectSubViewModel = function(subviewmodel)
{
this.selectedSubViewModel = selectedSubViewModel(subviewmodel);
}
};
//child view model, contains ko.observableArray of models
var SubViewModel = function(key)
{
this.key = key;
//load items from json based on key value...
//ko.observableArray of subviewmodels
this.items=ko.observableArray([new Model("car","4 wheels"), new Model("plane","can fly")]);
//selected model
this.selectedModel = ko.observable();
//set selected model
this.selectModel = function(item)
{
this.selectedModel = selectedModel(item);
}
};
//model
var Model = function(word,defention)
{
//properties
this.word = word;
this.defention = defention;
};
and in my html i have 2 lists changing selection in the first list should change the source of the second list, however when i change selection in list one nothing happens and when i debug in chrome i get Uncaught ReferenceError: selectedSubViewModel is not defined
<!--main view model list view-->
<div class="list-group" data-bind="foreach: subViewModels">
<a href="#" class="list-group-item" data-bind="css: {active: $parent.selectedSubViewModel() == $data}, click: $parent.selectSubViewModel.bind($parent)">
<h4 class="list-group-item-heading" data-bind="text: key"></h4>
</a>
</div>
<!--selected sub view model list view-->
<div class="list-group" data-bind="foreach: selectedSubViewModel.items">
<a href="#" class="list-group-item" data-bind="css: {active: $parent.selectedModel() == $data}, click: $parent.selectModel.bind($parent)">
<h4 class="list-group-item-heading" data-bind="text: word"></h4>
<p class="list-group-item-text" data-bind="text: defention"></p>
</a>
</div>
Your main problem is that your selectSubViewModel and selectModel are incorectly trying to the set your selectedSubViewModel and selectedModel observables.
The correct syntax is:
this.selectSubViewModel = function(subviewmodel)
{
this.selectedSubViewModel(subviewmodel);
}
this.selectModel = function(item)
{
this.selectedModel(item);
}
See also in the documentation: Reading and writing observables
However it won't make your code to work because when the foreach: selectedSubViewModel.items binding is executed for the first time selectedSubViewModel will be null and KO won't find the selectedSubViewModel.items.
To avoid this you can use the with binding to wrap your foreach. In this case your second list is only rendered if there is something in the selectedSubViewModel and you don't have to write selectedSubViewModel.someproperty it is enough to write foreach: items etc:
<!--selected sub view model list view-->
<!-- ko with: selectedSubViewModel -->
<div class="list-group" data-bind="foreach: items">
<a href="#" class="list-group-item"
data-bind="css: { active: $parent.selectedModel() == $data},
click: $parent.selectModel.bind($parent)">
<h4 class="list-group-item-heading" data-bind="text: word"></h4>
<p class="list-group-item-text" data-bind="text: defention"></p>
</a>
</div>
<!-- /ko -->
Demo JSFiddle.

Categories

Resources