HTML and simple JS (no react / vue).
I need to do a list of cards and would like to do a loop to avoid repeating the html.
My current code :
<div id="products-cards-container">
<div class="products-cards">
<div class="product-header">
<img src="../assets/img/image1.png"/>
</div>
<div class="product-content">
<h4>title 1</h4>
<p>super content 1</p>
</div>
<button class="info-button">+ info</button>
</div>
<div>
<div class="product-header">
<img src="../assets/img/cards/products/image2.png"/>
</div>
<div class="product-content">
<h4>title 2</h4>
<p>super content 2</p>
</div>
<button class="info-button">+ info</button>
</div>
<div>
<div class="product-header">
<img src="../assets/img/cards/products/image-3.png"/>
</div>
<div class="product-content">
<h4>title 3</h4>
<p>blablablablbalbalbabla blablaba</p>
</div>
<button class="info-button">+ info</button>
</div>\
</div>
I am trying to return html
script.js
const valuesCards = [
{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
]
creating a function that inserts the list of cards in the .products-cards div :
function returnCards() => {
----- AND HERE I AM STUCK
(as well, all tries I did with a simple array / object returned only the last info) ----
}
Use Array.prototype.map function and a template literal.
const container = document.getElementById('products-cards-container');
const valuesCards = [{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
]
function returnCards(valuesCards) {
return "<div class=\"products-cards\">" + valuesCards.map(valuesCard => `
<div>
<div class="product-header">
<img src="${valuesCard.image}"/>
</div>
<div class="product-content">
<h4>${valuesCard.title}</h4>
<p>${valuesCard.content}</p>
</div>
<button class="info-button">+ info</button>
</div>`).join('') + "</div>";
}
container.innerHTML = returnCards(valuesCards);
<div id="products-cards-container"></div>
You can do it this way
const valuesCards = [
{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
];
let cardHTML = '';
valuesCards.map(element => {
cardHTML += '<div> \
<div class="product-header"> \
<img src="'+element.image+'"/> \
</div> \
<div class="product-content"> \
<h4>'+element.title+'</h4> \
<p>'+element.content+'</p> \
</div> \
<button class="info-button">+ info</button> \
</div> \
';
});
document.getElementsByClassName('products-cards')[0].innerHTML = cardHTML;
<div id="products-cards-container">
<div class="products-cards">
</div>
</div>
const valuesCards = [
{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
]
valuesCards.map(card=> {
var cardDiv = document.createElement('div');
cardDiv.innerHTML = `
<div class="product-header">
<img src="${card.image}"/>
</div>
<div class="product-content">
<h4>${card.title}</h4>
<p>${card.content}</p>
</div>
<button class="info-button">+ info</button>`
document.getElementsByClassName('products-cards')[0].appendChild(cardDiv);
})
<div id="products-cards-container">
<div class="products-cards">
</div>
</div>
You need to do a "for loop" that iterates over your valuesCards object and for each value return an html template an inject into the desired element in DOM.
For example:
// Define the object
const valuesCards = [
{
'image': '../img/image1.png',
'title': 'title 1',
'content': 'super content 1',
},
{
'image': '../img/image2.png',
'title': 'title 2',
'content': 'super content 2'
},
{
'image': '../img/image-3.png',
'title': 'title3',
'content': 'blablablablbalbalbabla blablaba'
}
]
// Identify the DOM element to store cards
cardsContainer = document.querySelector('#products-cards-container');
// Create a loop that iterates over valuesCards object
for (let value of Object.values(valuesCards)) {
console.log(value.title);
// Create an element to store new card content
let newCard = document.createElement('DIV');
// Add products-cards class to new element
newCard.classList.add('products-cards');
// Create a multiline string template with current values each time
cardHtml = `
<div class="">
<div class="product-header">
<img src="${value.image}"/>
</div>
<div class="product-content">
<h4>${value.title}</h4>
<p>${value.content}</p>
</div>
<button class="info-button">+ info</button>
</div>
`;
// Append the new element to the cardsContainer element in DOM
cardsContainer.appendChild(newCard);
// Inject the template html on DOM's new append item
newCard.innerHTML = cardHtml;
}
Important!! create a div element with the id="products-cards-container" in the html where you want to print the cards.
Related
I have 2 objects with data and I need to restruct the rendering in html.
This is what I get:
but this is what I need to obtain:
Black box is the *ngFor of articles object, respectively red one is *ngFor of adsContainer object
<div *ngFor="let article of articles; let i = index;">
<mat-card class="card">
<div>
<a class="title">
{{article.title}}
</a>
<a>
<p>{{article.content}}</p>
</a>
</div>
</mat-card>
<div class="container" *ngIf="(i % 7) === 6">
<div *ngFor="let data of adsContainer">
<div>
<h1>{{data.value}}</h1>
</div>
</div>
</div>
</div>
public adsContainer: any = [
{ id: '1', value: 'text value here' },
{ id: '2', value: 'text value here' },
{ id: '3', value: 'text value here' }
]
public articles: any = [
{ id: '1', title: 'title value here', content: 'content here' },
{ id: '2', title: 'title value here', content: 'content here' },
{ id: '3', title: 'title value here', content: 'content here' },
........
{ id: '20', title: 'title value here', content: 'content here' },
]
Your issue is that you add your ads containers in a loop
<div class="container" *ngIf="(i % 7) === 6">
<!-- this loop here -->
<div *ngFor="let data of adsContainer">
<div>
<h1>{{data.value}}</h1>
</div>
</div>
</div>
what you want is to add only one ad container at a time, in order to do that, you have to rmove the loop
In order to have the three ads show, you'll have to do some math to get the ad index from the article index
something like this :
<div class="container" *ngIf="(i % 7) === 6">
<div>
<h1>{{adsContainer[mathFloor(i / 7)].value}}</h1>
</div>
</div>
note that Math is not available in angular templates, you'll have to redefine floor as a component method
function mathFloor(val) {return Math.floor(val)}
Try to use directly the array , also when you call
adsContainer[(parseInt(i / 6)-1)% adsContainer.length, it will always restart the index:
<div *ngFor="let article of articles; let i = index;">
<mat-card class="card">
<div>
<a class="title">
{{article.title}}
</a>
<a>
<p>{{article.content}}</p>
</a>
</div>
</mat-card>
<div class="container" *ngIf="(i % 7) === 6">
<div>
<div>
<h1>{{adsContainer[(parseInt(i / 6)-1)%adsContainer.length].value}}</h1>
</div>
</div>
</div>
</div>
I encounter a problem close but still different that my previous question.
Here is my Data object:
componants: {
element1: {
base: {
title: `Example`,
description: `Color Modifier`,
modifierClass: `Color ModifierClass`,
},
modifiers: {
block1: {
/* Modifier Class */
class: 'doc_button--green',
/* Description of the usage of the class */
description: 'Primary Button'
},
block2: {
class: 'doc_button--orange',
description: 'Secondary Button'
},
block3: {
class: 'doc_button--red',
description: 'Tertiary Button'
}
}
},
element2: {
base: {
title: `Example`,
description: `Size Modifier`,
modifierClass: `Size ModifierClass`,
},
modifiers: {
block1: {
class: 'doc_button--small',
description: 'Small Button'
},
block2: {
class: 'doc_button--big',
description: 'Big Button'
}
}
}
},
And how I use it for the nested loops:
<div>
<div v-for="(componant) in modifier" :key="componant">
<div v-for="(element, l) in componant" :key="l">
<h2 class="doc_title">
{{element.title}}
</h2>
<p class="doc_description">
{{element.description}}
</p>
<h3 class="doc_subtitle">
{{element.modifierClass}}
</h3>
</div>
<div v-for="(modifier) in componant" :key="modifier">
<ul class="doc_list doc_list--parameters" v-for="(block,k) in modifier" :key="k">
<li class="doc_list-text">
<p>{{block.class}}</p> : <p>{{block.description}}</p>
</li>
</ul>
<div class="doc_row">
<div class="doc_list-container">
<ul class="doc_list" v-for="(block,k) in modifier" :key="k">
<div class="doc_list-element" v-html="parentData.core.html"
:class="[parentData.core.class, `${block.class}`]">
</div>
<p class="doc_element-text"> {{block.class}} </p>
</ul>
</div>
</div>
<pre class="doc_pre">
<code class="language-html doc_code">
<div v-text="parentData.core.html">
</div>
</code>
</pre>
</div>
</div>
</div>
I should get only the second row of circles, with the colors, and don't understand why I get a first row of undefined ones.
There is one more layer of data inside modifiers:, comparent to base:.
But I added one more v-for loop, so doesn't it should work?
Thanks.
I eventually found the solution, so I put it here to solve my problem.
There was a mistake in the data organization, and once that was corrected, a little modification in the loops' structure.
No more block objects, but rather a modifiers array. It reduces the loop numbers and everything works.
componants: {
element1: {
base: {
title: `Example`,
description: `Color Modifier`,
modifierClass: `Color ModifierClass`,
},
modifiers: [{
/* Modifier Class */
class: 'doc_button--green',
/* Description of the usage of the class */
description: 'Primary Button'
},
{
class: 'doc_button--orange',
description: 'Secondary Button'
},
{
class: 'doc_button--red',
description: 'Tertiary Button'
}
],
},
element2: {
base: {
title: `Example`,
description: `Size Modifier`,
modifierClass: `Size ModifierClass`,
},
modifiers: [{
class: 'doc_button--small',
description: 'Small Button'
},
{
class: 'doc_button--big',
description: 'Big Button'
},
]
}
},
<template>
<div>
<div v-for="(componant) in modifier" :key="componant">
<!-- <div v-for="(element, l) in componant[0]" :key="l"> -->
<h2 class="doc_title">
{{componant.base.title}}
</h2>
<p class="doc_description">
{{componant.base.description}}
</p>
<h3 class="doc_subtitle">
{{componant.base.modifierClass}}
</h3>
<!-- </div> -->
<ul class="doc_list doc_list--parameters" v-for="(modifier) in componant.modifiers" :key="modifier">
<li class="doc_list-text">
<p>{{modifier.class}}</p> : <p>{{modifier.description}}</p>
</li>
</ul>
<div class="doc_row">
<div class="doc_list-container">
<ul class="doc_list" v-for="(modifier) in componant.modifiers" :key="modifier">
<div class="doc_list-element" v-html="parentData.core.html"
:class="[parentData.core.class, `${modifier.class}`]">
</div>
<p class="doc_element-text"> {{modifier.class}} </p>
</ul>
</div>
</div>
<pre class="doc_pre">
<code class="language-html doc_code">
<div v-text="parentData.core.html">
</div>
</code>
</pre>
</div>
</div>
</template>
here is the plunkr which will best illustrate my problem.
https://plnkr.co/edit/VrT7TmK6tY2u4k2NYJ2U?p=preview
there are two panels the panel on the left has a list of items that you can drag and drop onto wells on the right. the wells are created with a ng repeat. unfortunately I can not drag from one well to the another well on the right side of the screen. not sure why.
<div ng-controller="oneCtrl"><!--one-->
<div class="span3" style='margin-left:10px;'>
<div class="thumbnail"
data-drop="true"
ng-model='list1'
data-jqyoui-options="optionsList1"
jqyoui-droppable="{multiple:true}">
<div class="alert alert-info btn-draggable"
ng-repeat="item in list1"
ng-show="item.title" data-drag="{{item.drag}}"
data-jqyoui-options="{revert: 'invalid'}"
ng-model="list1" jqyoui-draggable="{index: {{$index}},animate:true}"
><!--alert-->
{{item.title}}
</div><!--alert-->
</div>
</div>
<div class="span3" style='margin-left:150px;'>
<div class=thumbnail>
<div class="well"
ng-repeat = "org in orgs"
data-drop="true"
ng-model="org.list"
data-jqyoui-options="{accept:'.btn-draggable:not([ng-model=org.list])'}"
jqyoui-droppable="{multiple:true}">
<div class=" alert alert-success btn-draggable" ng-repeat="item in org.list"
ng-show="item.title"
data-drag="{{item.drag}}"
data-jqyoui-options="{revert: 'invalid'}"
ng-model="org.list"
jqyoui-draggable="{index: {{$index}},animate:true}">
{{item.title}}
</div>
</div>
</div>
</div>
</div><!--one-->
and the javascript
var App = angular.module('drag-and-drop', ['ngDragDrop']);
App.controller('oneCtrl', function($scope, $timeout) {
$scope.orgs = [
{name: 'org1', list: []},
{name: 'org2', list: []},
{name: 'org3', list: []},
]
$scope.list1 = [
{ 'title': 'Item 1', 'drag': true },
{ 'title': 'Item 2', 'drag': true },
{ 'title': 'Item 3', 'drag': true },
{ 'title': 'Item 4', 'drag': true },
{ 'title': 'Item 5', 'drag': true },
{ 'title': 'Item 6', 'drag': true },
{ 'title': 'Item 7', 'drag': true },
{ 'title': 'Item 8', 'drag': true }
];
});
That's because you have data-jqyoui-options="{accept:'.btn-draggable:not([ng-model=org.list])'}" for the repeater in the right panel. This selector is the same for all the generated droppable-zones and it controls which draggable elements are accepted by the droppable. You will be able to drag and drop the items between the generated zones, if you will remove this option (or modify it according to your requirements).
Working plnkr.
I have a list of object element like:
$scope.products = [
{
name: 'First News Heading',
content: 'Lorem ipsum',
cover: 'img/img1.jpg'
},
{
name: 'Second News Heading',
content: 'The veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.',
cover: 'img/img2.jpg'
},
];
And I want to display then like:
<div class="thumbnail">
<img ng-src="{{ product.cover }}">
<p class="title">{{ product.name }}</p>
<p class="content">{{ product.content }}</p>
</div>
using ng-repeat displays all of them together. but I want to use a click or swipe up to display next-next element.
Try the following :
<div class="thumbnail">
<img ng-src="{{product.cover}}">
<p class="title">{{product.name}}</p>
<p class="content">{{product.content}}</p>
</div>
<input type="button" ng-click="previous()" value="Previous" />
<input type="button" ng-click="next()" value="Next" />
JS:
$scope.currentPosition=0;
$scope.product=$scope.products[0]; //initialize the product var with first element
$scope.next=function(){
$scope.currentPosition++;
if($scope.currentPosition <$scope.products.length)
$scope.product=$scope.products[$scope.currentPosition];
else
$scope.curentPosition=$scope.products.length-1;
}
$scope.previous=function(){
$scope.currentPosition--;
if($scope.currentPosition >=0)
$scope.product=$scope.products[$scope.currentPosition];
else
$scope.curentPosition=0;
}
I am using MeteorJS (So great :) ), to develop simple app. I want to use masonry, so I am using sjors:meteor-masonry package.
When I use this code everything works fine:
var itemsData = [
{
title: 'First item',
description: 'Lorem 1',
price: 20
},
{
title: 'Secounde item',
description: 'Lorem 2',
price: 40
},
{
title: 'Third item',
description: 'Lorem 3',
price: 10
},
{
title: 'Fourth item',
description: 'Lorem 4',
price: 10
},
{
title: 'Five item',
description: 'sit 4',
price: 10
}
];
Template.itemsList.helpers({
items: itemsData
});
Template.itemsList.rendered = function() {
var container = document.querySelector('#main');
var msnry = new Masonry( container, {
// options
columnWidth: 200,
itemSelector: '.item'
});
};
But when I change part (code) for Template.itemsList.rendered to masonry don't work:
Template.itemsList.helpers({
items: function() {
return Items.find();
}
});
Any ideas ?
EDIT
myapp/lib/collections/items.js
Items = new Mongo.Collection('items');
And it is populated whit data from mongoshell. Data is ok, but masonry dont work.
EDIT 2
Masonry stops animating on screen resize and grid is not working as it should. No errors.
myapp/client/templates
<template name="itemSingle">
<div id="profile-widget" class="panel item">
<div class="panel-heading">
</div>
<div class="panel-body">
<div class="media">
<div class="media-body">
<h2 class="media-heading">{{title}}</h2>
{{description}}
</div>
</div>
</div>
<div class="panel-footer">
<div class="btn-group btn-group-justified">
<a class="btn btn-default" role="button"><i class="fa fa-eye"></i> 172</a>
<a class="btn btn-default" role="button"><i class="fa fa-comment"></i> 34</a>
<a class="btn btn-default highlight" role="button"><i class="fa fa-heart"></i> 210</a>
</div>
</div>
</div>
</template>
<template name="itemsList">
<div id="main">
{{#each items}}
{{> itemSingle}}
{{/each}}
</div>
</template>
I encountered the same problem, without never finding the perfect solution.
I tried to resolve with the setTimout() workaround :
Template.main.rendered = function() {
setTimeout(function() {
$('#container').masonry({
columnWidth: 35,
itemSelector: '.thumb',
gutter: 10,
isFitWidth: false
});
}, 1);
}