I currently have some code which takes an array of javascript objects and generates an html list based on it, I want to display the corresponding image with the text in the list. But currently I can only get it to display the address if the image of text. Any help would be appreciated thanks!
HTML
<form>
<p>Please insert the items</p>
<input type="text" id="box" />
</form>
<div id="root"></div>
CSS
ul {
border: 2px solid grey;
border-radius: 5px;
padding: 1em;
}
li {
display: inline-block;
padding: 0.5em;
}
JS
const catalog = {
GalaxyTablet: {
name: "GalaxyTablet",
key: "galaxytablet",
keywords: ["galaxy", "tablet", "samsung"],
price: 800,
image: "https://www.jbhifi.co.nz/FileLibrary/ProductResources/Images/150044-M-HI.jpg"
},
GalaxyPhone: {
name: "GalaxyPhone",
key: "galaxyphone",
keywords: ["galaxy", "phone", "samsung"],
price: 1000,
image: "https://assets.kogan.com/files/product/etail/Samsung-/S10WHT_03.jpg?auto=webp&canvas=753%2C502&fit=bounds&height=502&quality=75&width=753"
},
HTCPhone: {
name: "HTCPhone",
key: "htcphone",
keywords: ["htc", "phone"],
price: 650,
image: "https://cdn.mos.cms.futurecdn.net/ca063713e185be46e62ec2eb3762a540.jpg"
},
};
const form = document.querySelector("form");
form.addEventListener("submit", submitHandler);
function submitHandler(event) {
event.preventDefault();
const searchTerm = form.box.value;
const results = search(searchTerm);
render(results);
}
function search(searchTerm) {
return Object.keys(catalog)
.filter((key) => catalog[key].keywords.includes(searchTerm.toLowerCase()))
.map((key) => catalog[key]);
}
function render(results) {
const root = document.querySelector("#root");
const list = results.map(itemToLi).join("");
root.innerHTML = `<ul>
${list}
</ul>`;
}
function itemToLi(item) {
return `<li>${item.name}</li>$ ${item.price}<li> <li>${item.image}<li>`;
}
You're only getting the URI text because that is what you are outputting:
<li>${item.image}<li>
You need to create an img tag:
<li><img src="${item.image}"></li>
Here's a small demo with the contents of your catalog:
const catalog = {
GalaxyTablet: {
name: "GalaxyTablet",
key: "galaxytablet",
keywords: ["galaxy", "tablet", "samsung"],
price: 800,
image: "https://www.jbhifi.co.nz/FileLibrary/ProductResources/Images/150044-M-HI.jpg"
},
GalaxyPhone: {
name: "GalaxyPhone",
key: "galaxyphone",
keywords: ["galaxy", "phone", "samsung"],
price: 1000,
image: "https://assets.kogan.com/files/product/etail/Samsung-/S10WHT_03.jpg?auto=webp&canvas=753%2C502&fit=bounds&height=502&quality=75&width=753"
},
HTCPhone: {
name: "HTCPhone",
key: "htcphone",
keywords: ["htc", "phone"],
price: 650,
image: "https://cdn.mos.cms.futurecdn.net/ca063713e185be46e62ec2eb3762a540.jpg"
}
};
const root = document.querySelector("#root");
const list = Object.values(catalog).map(itemToLi).join("");
root.innerHTML = `<ul>
${list}
</ul>`;
function itemToLi(item) {
return `<li>${item.name}</li>$ <li>${item.price}</li> <li><img src="${item.image}"></li>`;
}
<div id="root"></div>
You need to use img tag to render image. Also do not forget to use alt attribute inside that.
You can update your existing JavaScript method itemToLi as :
function itemToLi(item) {
return `<li>${item.name}</li>$ <li>${item.price}</li> <li><img src="${item.image}" alt=">${item.name}"></li>`;
}
Related
Given the sample code using ag-grid with Vue 3
<script setup lang="ts">
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import { AgGridVue } from "ag-grid-vue3";
import { ref } from "vue";
const columnDefs = ref([
{
field: "person",
},
{
field: "car",
},
]);
const rowData = ref([
{
person: "person-1",
car: "car-1",
},
{
person: "person-1",
car: "car-2",
},
{
person: "person-1",
car: "car-3",
},
{
person: "person-2",
car: "car-4",
},
{
person: "person-3",
car: "car-5",
},
{
person: "person-3",
car: "car-6",
},
]);
</script>
<template>
<ag-grid-vue
style="width: 500px; height: 500px"
class="ag-theme-alpine"
:columnDefs="columnDefs"
:rowData="rowData"
>
</ag-grid-vue>
</template>
You will get the following output
I could group the table based on person but is there a way to merge cells in a single column?
I think I can't use row-spanning for a single column definition because inside the rowSpan function it is not possible to tell ag-grid to combine multiple cells of a single column.
Any ideas?
After spending a few hours on this requirement, I came up with the solution.
Here are the steps which I performed :
To implement rowSpan, You have to manipulate the rowData so that it will contain the person property only in the first occurance object. As a result, When we will apply the rowSpan based on the length it will not take that length for each same person name. This is how I manipulated the rowData :
const uniq = {};
const rowData = [{
person: "person-1",
car: "car-1",
}, {
person: "person-1",
car: "car-2",
}, {
person: "person-1",
car: "car-3",
}, {
person: "person-2",
car: "car-4",
}, {
person: "person-3",
car: "car-5",
}, {
person: "person-3",
car: "car-6",
}];
rowData.forEach(obj => {
!uniq[obj.person] ? uniq[obj.person] = true : delete obj.person;
});
console.log(rowData);
Now, Created a rowSpan function which is basically used to return the length of the objects contains occurrence of same person names.
function rowSpan(params) {
const person = params.data.person;
const gridData = getData(); // getData() with return the original rowData array.
return gridData.filter((e) => e.person === person).length;
}
At the end, for styling the rowSpan columns. I used these styles (refer from here)
.show-cell {
background: white;
border-left: 1px solid lightgrey !important;
border-right: 1px solid lightgrey !important;
border-bottom: 1px solid lightgrey !important;
}
This is how the columnDefs for person object looks like :
{
field: "person",
rowSpan: rowSpan,
cellClassRules: { 'show-cell': 'value !== undefined' }
}
Live Demo : Row Spanning Demo
Are you sure row-spanning will not work? Can you try to implement this?
function rowSpan(params) {
const person = params.data.person;
return rowData.value.filter((e) => e.person === person).length;
}
const columnDefs = ref([
{
field: "person",
rowSpan: rowSpan,
},
{
field: "car",
},
]);
<script>
let cats = [
{ id: 'OUtn3pvWmp1', name: 'Cat1' },
{ id: 'OUtn3pvWmp2', name: 'Cat2' },
{ id: 'OUtn3pvWmp3', name: 'Cat3' },
{ id: 'OUtn3pvWmp4', name: 'Cat4' },
{ id: 'OUtn3pvWmp5', name: 'Cat4' },
{ id: 'OUtn3pvWmp6', name: 'Cat6' },
{ id: 'OUtn3pvWmp7', name: 'Cat7' },
{ id: 'OUtn3pvWmp8', name: 'Cat8' },
{ id: 'OUtn3pvWmp9', name: 'Cat9' },
{ id: 'OUtn3pvWmp10', name: 'Cat10' },
{ id: 'OUtn3pvWmp11', name: 'Cat11' },
{ id: 'OUtn3pvWmp12', name: 'Cat12' },
//{ id: 'OUtn3pvWmp13', name: 'Cat13' },
];
</script>
<style>
.row {
display:grid;
grid-columns: 4;
}
.card{
background-color: gray !important;
color: white;
border-radius:10px;
border-color: #404040;
padding-left: 15px;
padding-right: 15px;
}
.card-group {
display: flex;
}
</style>
<h1>EXPECTED OUTPUT, BY LOOP OVER cats </h1>
{#each cats as cat, i}
{#if i % 4 === 0}
<div class="row">
<div class="card-group">
<div class="card">{cats[i].name}</div>
<div class="card">{cats[i + 1].name}</div>
<div class="card">{cats[i + 2].name}</div>
<div class="card">{cats[i + 3].name}</div>
</div>
</div>
{/if}
{/each}
I would like cat13 item display in the another row ,anyone please give some trick.
I am using sveltejs 3
My runnable script here
I would create another array out of cats array. the new array will include sub-arrays each array will include at most 4 elements.
then in the HTML, we iterate over the new array then iterate over the sub arrays
/* divide your elements into another array where each index is a sub array array of 4 elements */
let chunkit = (maxItems = 4) => {
/* array to store the sub arrays of 4 elements */
let chunks = [[]]
/* iterate over the cats */
for (let cat of cats) {
/* if the last array of 4 elements has the length of 4, we create a new sub-array */
if (chunks[chunks.length - 1].length == maxItems) {
chunks.push([])
}
/* add the current element to the last sub array */
chunks[chunks.length - 1].push(cat)
}
return chunks
}
then we iterate over the return value of the function chunkit
<div class="row">
{#each chunkit() as fourCats}
<div class="card-group">
{#each fourCats as cat}
<div class="card">{cat.name}</div>
{/each}
</div>
{/each}
</div>
you can pass a number as parameter to the function maxItems to set the number of elements in each sub category
here is an example repl
I have a question about jQuery: I now render content from an array to the dom with:
errors.forEach(error => {
let message = error.message;
let fileName = error.fileName;
$('.errors').append("<li class=\"error-item\">".concat(fileName, " : ").concat(message, "</li><br>"));
});
but I got some actions that update the array where I render the content from, but when I run this function after the update of the array, it appends. I use the .empty() before now but that feels like a hack around but I want it to update
I hope this make sense
What can I do that it updates the dom instead of appending?
I hope someone can help me with this
The jQuery method .html() overwrites the content of the targeted element(s) with a given htmlString. The following demo function logger() will accept an array of objects and render the data in a <ul>.
let failData = [{
DOM: '.fail',
title: 'Errors: ',
header: ['File', 'Message']
},
{
item: '12ffda8a99.log',
message: 'Connection failed'
},
{
item: 'bb6200c400.log',
message: 'Corrupted download'
},
{
item: 'd93ba66731.log',
message: 'Encryption key needed'
},
{
item: '08caa5240f.log',
message: 'Mismatched certification'
},
{
item: 'dead0b8a99.log',
message: 'Insecure protocol'
}
];
let warnData = [{
DOM: '.warn',
title: 'Alerts: ',
header: ['System', 'Message']
},
{
item: 'network',
message: '1GB of data left before limit is exceeded'
},
{
item: 'file',
message: '1GB of storage left before partition is full'
},
{
item: 'power',
message: '5% of battery remains'
}
];
let infoData = [{
DOM: '.info',
title: 'Updates: ',
header: ['Priority', 'Message']
},
{
item: 'critical',
message: 'Anti-virus update required'
},
{
item: 'optional',
message: 'Media encoding update available'
}
];
const logger = array => {
let html = '';
let list;
for (let [index, object] of array.entries()) {
list = $(array[0].DOM);
if (index === 0) {
list.prev('header').html(`<u><b>${object.title}</b><output>${array.length-1}</output></u><u><b>${object.header[0]}</b><b>${object.header[1]}</b></u>`);
} else {
html += `<li><b>${object.item}</b><b>${object.message}</b></li>`;
}
}
list.html(html);
return false;
}
logger(failData);
logger(warnData);
logger(infoData);
header {
margin: 10px 0 -15px;
}
ul {
padding: 5px 0 10px;
border: 3px ridge #777;
overflow-y: hidden;
}
ul,
header {
display: table;
table-layout: fixed;
width: 90%;
}
li,
u {
display: table-row;
}
b {
display: table-cell;
width: 50%;
border-bottom: 1px solid #000;
padding-left:5px
}
output {
display: inline-block;
width: 10ch;
}
<main>
<section class='logs'>
<header></header>
<ul class='fail'></ul>
<header></header>
<ul class='warn'></ul>
<header></header>
<ul class='info'></ul>
</section>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
How do I create a dynamical filter using a computed property from vue when the dataset is from a graphql-query?
I've looked at several articles that all use the array.filter()-method, but I can't get it to work on my dataset (dummy data below):
books: [{
node: {
title: 'Elon Musk',
by:'Ashlee Vance',
},
node: {
title: 'Steve Jobs',
by:'George Llian',
},
node: {
title: 'Face of Facebook',
by: 'Sandip Paul',
},
node: {
title: 'Tim Cook',
by:'Andy Atkins',
url:'http://www.voidcanvas.com/'
},
node: {
title: 'Abdul Kalam',
by:'Arun Tiwari',
},
node: {
title: 'Story of Elon Musk',
by:'BP John',
},
node: {
title: 'Story of Bill Gates',
by:'Russel Crook',
},
node: {
title: 'Becoming Steve Jobs',
by:'Andrew Russel',
}
}]
Method:
computed: {
filteredBooks: function () {
var books_array = this.books,
searchString = this.searchString;
if(!searchString) {
return books_array;
}
searchString = searchString.trim().toLowerCase();
books_array = books_array.filter(function(item) {
if(item.node.title.toLowerCase().indexOf(searchString) !== -1) {
return item;
}
});
return books_array;
}
HTML:
<div id="app">
<input type="text" v-model="searchString" placeholder="search" />
<ul style="list-style: none;">
<li v-for="book in filteredBooks">
<p>{{book.title}} -by- {{book.by}}</p>
</li>
</ul>
</div>
This is my first coding project since early 2000, so please feel free to point me in the right direction if this is the wrong forum for this question.
I set up a jsfiddle to play with the case.
Here is the code with some modifications:
var app = new Vue({
el: '#app',
data: {
searchString: '',
books: [{
title: 'Elon Musk',
by: 'Ashlee Vance'
},
{
title: 'Steve Jobs',
by: 'George Llian'
},
{
title: 'Face of Facebook',
by: 'Sandip Paul'
},
{
title: 'Tim Cook',
by: 'Andy Atkins',
url: 'http://www.voidcanvas.com/'
},
{
title: 'Abdul Kalam',
by: 'Arun Tiwari'
},
{
title: 'Story of Elon Musk',
by: 'BP John'
},
{
title: 'Story of Bill Gates',
by: 'Russel Crook'
},
{
title: 'Becoming Steve Jobs',
by: 'Andrew Russel'
}
]
},
computed: {
filteredBooks: function() {
return this.books.filter(e => this.searchString === '' ? false : e.title.toLowerCase().indexOf(this.searchString.toLowerCase()) !== -1 ? true : false);
}
}
});
body {
background-color: #dbd8d8;
padding: 20px;
}
input {
width: 300px;
height: 30px;
padding: 0.2rem;
}
.design {}
p {
position: relative;
display: block;
padding: .4em .4em .4em 2em;
margin: .5em 0;
border: 3px solid white;
background: #FC756F;
color: #444;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="searchString" placeholder="search" />
<ul style="list-style: none;">
<li v-for="book in filteredBooks">
<p>{{book.title}} -by- {{book.by}}</p>
</li>
</ul>
</div>
Remove the node: from before the objects in the books data array - books array should contain a bunch of plain objects. If you put node: before each object, then you "say" that the every node is the key of key-value pair of an object (so the keynames will be identical - node!!!)
Simplify filteredBooks computed - no need to store all the variables. This function (filteredBooks) doesn't change the inputs, so you can use this here. The filter() functions doesn't change the array it filters - rather it returns a new array, containing only values that the iteratee function "saw" as true
You check for !searchString and that's never the case - searchString is always going to be true as you initialize it with searchString: '' (an empty value - but a value), so I changed it checking for the empty value in the filteredBooks computed.
I modified the code so that it compares lowercase to lowercase. With your code if someone typed a search string in uppercase, then there'd have been no match.
I have already created a diagram and a custom node following this example.
The problem is, when I try to get the JSON from the diagram, the attributes I've added to the custom node are not shown, although they appear on the lateral panel.
Here's an example:
<html>
<head>
<script src="http://cdn.alloyui.com/2.5.0/aui/aui-min.js"></script>
<link href="http://cdn.alloyui.com/2.5.0/aui-css/css/bootstrap.min.css" rel="stylesheet"></link>
<style>
.diagram-node-custom .diagram-node-content {
background: url(http://www.saltlakemailing.com/wp-content/uploads/2012/03/process_icon.png) no-repeat scroll center transparent;
}
</style>
<script>
var Y = YUI().use('aui-diagram-builder',
function(Y) {
Y.DiagramNodeCustom = Y.Component.create({
NAME: 'diagram-node',
ATTRS: {
type: {
value: 'custom'
},
customAttr: {
validator: Y.Lang.isString,
value: 'A Custom default'
}
},
EXTENDS: Y.DiagramNodeTask,
prototype: {
getPropertyModel: function () {
var instance = this;
var model = Y.DiagramNodeTask.superclass.getPropertyModel.apply(instance, arguments);
model.push({
attributeName: 'customAttr',
name: 'Custom Attribute'
});
return model;
}
}
});
Y.DiagramBuilder.types['custom'] = Y.DiagramNodeCustom;
Y.diagramBuilder = new Y.DiagramBuilder(
{
boundingBox: '#myDiagramContainer',
fields: [
{
name: 'name1',
type: 'custom',
customAttr: 'VALUECUSTOM',
xy: [100, 100]
}
],
srcNode: '#myDiagramBuilder'
}
).render();
}
);
</script>
</head>
<body>
<div id="myDiagramContainer">
<div id="myDiagramBuilder"></div>
</div>
<button onClick="console.log('JSON: '+JSON.stringify(Y.diagramBuilder.toJSON()));">GET JSON</button>
</body>
</html>
And this is the JSON I get when I do Y.diagramBuilder.toJSON():
{"nodes":[{
"transitions":[],
"description":"",
"name":"name1",
"required":false,
"type":"custom",
"width":70,
"height":70,
"zIndex":100,
"xy":[100,100]
}]}
The new attribute needs to be added to the SERIALIZABLE_ATTRS array.
Something like that:
this.SERIALIZABLE_ATTRS.push('customAttr');
I created a JSFiddle example: http://jsfiddle.net/aetevpfn/