My JSON looks like this:
users: [
{ 'name': 'User 1'},
{ 'name': 'User 2'},
{ 'name': 'User 3'},
{ 'name': 'User 4'},
{ 'name': 'User 5'},
{ 'name': 'User 6'},
]
Now i am looping this and i am displaying in the same div, But i need to diaplay the data by these conditions:
Up to length 3 should display in one div and rest things should display in another div(Ex another div right to that div). Here the JSON will be dynamic, The length may be <3 or >3. My requirement looks like this:
JSFiddle link
Vue is convenient to accomplish such tasks.
You need another two computed properties based on users.
template:
<div>{{left}}</div>
<div>{{right}}</div>
computed: {
left: function(){
return this.users.slice(0, 3);
},
reight: function() {
return this.users.slice(3);
}
}
Another option: https://jsfiddle.net/kth61cLu/1/
<div id="app">
<h3>Users</h3>
<div class="users1">
<div v-for="(user, index) in users" v-if="index < 3">
<p>{{user.name}}</p>
</div>
</div>
<div v-if="users.length > 3" class="users2">
<div v-for="(user, index) in users" v-if="index > 3">
<p>{{user.name}}</p>
</div>
</div>
</div>
Create a computed property that splits your array into two with the first having three elements and the second having the rest.
Then loop over the splitUsers array to display.
new Vue({
el: '#app',
data: {
users: [{"name":"User 1"},{"name":"User 2"},{"name":"User 3"},{"name":"User 4"},{"name":"User 5"},{"name":"User 6"}]
},
computed: {
splitUsers () {
const split = [ this.users.slice(0, 3) ]
if (this.users.length > 3) {
split.push(this.users.slice(3))
}
return split
}
}
})
#app { display: flex; }
#app div { padding: 1rem; border: 1px solid black; }
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<div v-for="chunk in splitUsers">
<p v-for="user in chunk">{{ user.name }}</p>
</div>
</div>
OK, here is what you can try.
<h3>Users</h3>
<div v-for="(user) in users.slice(0,3)">
<p>{{user.name}}</p>
</div>
<div v-for="(user) in users.slice(3)">
<p>{{user.name}}</p>
</div>
Hope it helps!
Related
I've an array of objects (items) that can be incremented with other items, I show them by looping into the array.
see:
and I would like to high-light an item when I click on it (e.g. : another border-top-color), but I'm failing at targeting one specific element and applying a style without applying the style to the whole array. Any idea?
Template, I'm using vuedraggable, don't mind it:
<template #item="{ element }">
<div
class="item"
:key="element"
#click="
messageItemTitle(element.title);
messageItemID(element.id);
"
>
<div class="item-title">
{{ element.title }}
</div>
<div class="item-subType">
{{ element.type }}
</div>
</div>
</template>
The script : Well, none of what I've coded previously worked, so here's the data only :
data() {
return {
dragItems: dragItemsList, //15 items that I can clone into dropItems
dropItems: [], //Array where I can add the items from dragItems
};
},
You can conditionally apply class:
const app = Vue.createApp({
data() {
return {
items: [{id:1, title: 'aaa', type: 'type1'}, {id:2, title: 'bbb', type: 'type2'}, {id:3, title: 'ccc', type: 'type3'}],
selected: null
};
},
methods: {
message(el) {
this.selected = el.id
}
}
})
app.mount('#demo')
.item {
border: 3px solid transparent;
border-top-color: black;
}
.selected {
border-top-color: green;
}
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div v-for="(element, i) in items" :key="i">
<!--<template #item="{ element }">-->
<div
class="item"
:class="selected === element.id && 'selected'"
:key="element"
#click="message(element)"
>
<div class="item-title">
{{ element.title }}
</div>
<div class="item-subType">
{{ element.type }}
</div>
</div>
<!--</template>-->
</div>
</div>
I'm trying to teach myself javascript and Vue.js. I was following the documentation on Vue's site and modifying their demonstrations as an exercise. I wanted to change their looping directive example to dynamically add images to the list from a specified url. I can't seem to get the image to show despite setting the image properties src field. I have verified that everything runs and the field is in fact getting set. I assume I must be misunderstanding something related to the DOM or ordering of events.
Thanks in advance.
HTML
<script src="https://unpkg.com/vue#next"></script>
<div id="list-rendering" class="demo">
<input v-model="imgsrc"></input>
<button v-on:click="setImage"> Set</button>
<ol>
<li v-for="todo in todos">
{{ todo.text }} {{todo.image}}
</li>
</ol>
</div>
CSS
.demo {
font-family: sans-serif;
border: 1px solid #eee;
border-radius: 2px;
padding: 20px 30px;
margin-top: 1em;
margin-bottom: 40px;
user-select: none;
overflow-x: auto;
}
Javascript
const ListRenderingApp = {
data() {
return {
todos: [
{ text: 'Learn JavaScript',
image: new Image(16,16)},
{ text: 'Learn Vue',
image: new Image(16, 16)},
{ text: 'Build something awesome',
image: new Image(16, 16)}
],
imgsrc: ""
}
},
methods:{
setImage(){
this.todos.map(todo => todo.image.src = this.imgsrc)
}
}
}
Vue.createApp(ListRenderingApp).mount('#list-rendering')
When using v-for make sure to add :key. Docs
Also pay attention to your html elements. <img> & <input>.
Also return the result of .map() to this.todos. Docs
new Vue({
el: "#app",
data: {
imgSrc: 'https://via.placeholder.com/150/FF0000',
todos: [
{ text: "Learn JavaScript", image: 'https://via.placeholder.com/150/0000FF' },
{ text: "Learn Vue", image: 'https://via.placeholder.com/150/0000FF' },
{ text: "Play around in JSFiddle", image: 'https://via.placeholder.com/150/0000FF' },
{ text: "Build something awesome", image: 'https://via.placeholder.com/150/0000FF' }
]
},
methods: {
setImage: function(todo){
this.todos = this.todos.map(todo => ({ ...todo, image: this.imgSrc }))
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<small>image url</small>
<input v-model="imgSrc" />
</div>
<br />
<div>
<button #click="setImage()">
Click to set Image
</button>
</div>
<ol>
<li v-for="(todo, i) in todos" :key="i">
<label>
{{ todo.text }}
</label>
<img :src="todo.image" alt="img" width="100" height="100">
</li>
</ol>
</div>
try running this snippet & click the "Click to set Image"
try this.
<div id="list-rendering" class="demo">
<input v-model="imgsrc"></input>
<button v-on:click="setImage"> Set</button>
<ol>
<li v-for="(todo, index) in todos :key="index"">
{{ todo.text }}
<img :src="todo.image"/>
</li>
</ol>
</div>
I want to show the even indexes of my array myArray:
myArray: [{'label': 'hasan', 'value': 'hosein'},
{'label': '1', 'value': '2'},
{'label': 'gholi', 'value': 'gholam'},
{'label': '3', 'value': '4'},
{'label': 'an', 'value': 'goh'},
{'label': '5', 'value': '6'},
{'label': 'pashm', 'value': 'khar'},
{'label': '7', 'value': '8'}]
in the right and odd indexes on the left column using v-for.
This is my HTML code:
<div class="row" v-for="objData in myArray" :key="objData.label">
// right column
<div class="line col-2"></div>
<div class="line col-3 "></div>
// left column
<div class="line col-2"></div>
<div class="line col-3 "></div>
</div>
I tried to separate the odd and even indexes into two new arrays and added a <div> above <div class="row> with a new v-for to loop through both arrays in my separate <div>s but it scrambled my array elements. So how can I show even indexes of myArray on the right and odd indexes on the left column?
Spoiler: I like the last one better
You could either use this trick with i and v-if:
new Vue({
el: '#app',
data() {
return {
myArray: [{label:"hasan",value:"hosein"},{label:"1",value:"2"},{label:"gholi",value:"gholam"},{label:"3",value:"4"},{label:"an",value:"goh"},{label:"5",value:"6"},{label:"pashm",value:"khar"},{label:"7",value:"8"}]
};
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap-grid.min.css" />
<div id="app" class="container">
<div class="row">
<div class="col-sm">
<template v-for="(objData, i) in myArray" :key="objData.label">
<div v-if="i%2">{{objData.label}}</div>
</template>
</div>
<div class="col-sm">
<template v-for="(objData, i) in myArray" :key="objData.label">
<div v-if="!(i%2)">{{objData.label}}</div>
</template>
</div>
</div>
</div>
Or separate your array in two in a computed prop, using reduce:
new Vue({
el: '#app',
data() {
return {
myArray: [{label:"hasan",value:"hosein"},{label:"1",value:"2"},{label:"gholi",value:"gholam"},{label:"3",value:"4"},{label:"an",value:"goh"},{label:"5",value:"6"},{label:"pashm",value:"khar"},{label:"7",value:"8"}]
};
},
computed: {
splitArray() {
const [ evens, odds ] = this.myArray.reduce((res, item, i) => {
res[i % 2].push(item);
return res;
}, [[], []]);
return { evens, odds };
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap-grid.min.css" />
<div id="app" class="container">
<div class="row">
<div class="col-sm">
<div v-for="objData in splitArray.odds" :key="objData.label">
{{objData.label}}
</div>
</div>
<div class="col-sm">
<div v-for="objData in splitArray.evens" :key="objData.label">
{{objData.label}}
</div>
</div>
</div>
</div>
... And if you want to go further, and not have to repeat yourself in the template:
new Vue({
el: '#app',
data() {
return {
myArray: [{label:"hasan",value:"hosein"},{label:"1",value:"2"},{label:"gholi",value:"gholam"},{label:"3",value:"4"},{label:"an",value:"goh"},{label:"5",value:"6"},{label:"pashm",value:"khar"},{label:"7",value:"8"}]
};
},
computed: {
splitArray() {
return this.myArray.reduce((res, item, i) => {
res[1 - i % 2].push(item);
return res;
}, [[], []]);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap-grid.min.css" />
<div id="app" class="container">
<div class="row">
<div v-for="(col, i) in splitArray" :key="`col-${i}`" class="col-sm">
<div v-for="objData in splitArray[i]" :key="objData.label">
{{objData.label}}
</div>
</div>
</div>
</div>
You should make your data like this
var myArray = [
{
left: {'label': 'hasan', 'value': 'hosein'},
right: {'label': '1', 'value': '2'}
}...
];
OR
use display:grid without div.row
<div v-for="(objData, index) in myArray" :key="objData.label" :class="['line', {col-2: index%2 === 1, col-3: index%2 === 0}"></div>
What about this? Managed to separate the array in two columns but resorted to two v-for, maybe you won't like that. It uses less javascript than the other solutions proposed but with the trade-off of a more complicated HTML.
HTML:
<div class="row">
<!-- Odd column -->
<div style="border: 1px solid red;">
<div v-for="(value, index) in myArray">
<div v-if="index % 2 != 0">{{ value }}</div>
</div>
</div>
<!-- Even column -->
<div style="border: 1px solid blue;">
<div v-for="(value, index) in myArray">
<div v-if="index % 2 == 0">{{ value }}</div>
</div>
</div>
</div>
This question already has answers here:
A way to render multiple root elements on VueJS with v-for directive
(6 answers)
Closed 2 years ago.
hopefully someone here will be able to help me with this problem.
I have the following data:
[
{
title: 'Header',
children: [
{
title: 'Paragraph',
children: [],
},
],
},
{
title: 'Container',
children: [
{
title: 'Paragraph',
children: [],
},
],
},
]
I want to render this in a list of <div> like this:
<div class="sortable-item" data-depth="1" data-index="0">Header</div> <!-- Parent -->
<div class="sortable-item" data-depth="2" data-index="0">Paragraph</div> <!-- Child-->
<div class="sortable-item" data-depth="1" data-index="1">Container</div> <!-- Parent -->
<div class="sortable-item" data-depth="2" data-index="0">Paragraph</div> <!-- Child-->
I have built a component that would be recursive, this is what I have so far:
<template>
<template v-for="(item, index) in tree">
<div
class="sortable-item"
:data-depth="getDepth()"
:data-index="index"
:key="getKey(index)"
>
{{ item.title }}
</div>
<Multi-Level-Sortable
:tree="item.children"
:parent-depth="getDepth()"
:parent-index="index"
:key="getKey(index + 0.5)"
></Multi-Level-Sortable>
</template>
</template>
<script>
export default {
name: 'MultiLevelSortable',
props: {
tree: {
type: Array,
default() {
return [];
},
},
parentDepth: {
type: Number,
},
parentIndex: {
type: Number,
},
},
methods: {
getDepth() {
return typeof this.parentDepth !== 'undefined' ? this.parentDepth + 1 : 1;
},
getKey(index) {
return typeof this.parentIndex !== 'undefined' ? `${this.parentIndex}.${index}` : `${index}`;
},
},
};
</script>
As you can see not only I have a <template> as the root element I also have a v-for, two "no no" for Vue.js. How can I solve this to render the list of elements like I pointed out above?
Note: I have tried vue-fragment and I was able to achieve the structure I wanted, but then when I tried using Sortable.js it didn't work, as if it wouldn't recognise any of the .sortable-item elements.
Any help will be greatly appreciated! Thank you!
Thanks to #AlexMA I was able to solve my problem by using a functional component. Here is what it looks like:
import SortableItemContent from './SortableItemContent.vue';
export default {
functional: true,
props: {
tree: {
type: Array,
default() {
return [];
},
},
},
render(createElement, { props }) {
const flat = [];
function flatten(data, depth) {
const depthRef = typeof depth !== 'undefined' ? depth + 1 : 0;
data.forEach((item, index) => {
const itemCopy = item;
itemCopy.index = index;
itemCopy.depth = depthRef;
itemCopy.indentation = new Array(depthRef);
flat.push(itemCopy);
if (item.children.length) {
flatten(item.children, depthRef);
}
});
}
flatten(props.tree);
return flat.map((element) => createElement('div', {
attrs: {
'data-index': element.index,
'data-depth': element.depth,
class: 'sortable-item',
},
},
[
createElement(SortableItemContent, {
props: {
title: element.title,
indentation: element.indentation,
},
}),
]));
},
};
The SortableItemContent component looks like this:
<template>
<div class="item-content">
<div
v-for="(item, index) in indentation"
:key="index"
class="item-indentation"
></div>
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">{{ title }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'SortableItemContent',
props: {
title: String,
indentation: Array,
},
};
</script>
Given the data I have posted on my question, it now renders the HTML elements like I wanted:
<div data-index="0" data-depth="0" class="sortable-item">
<div class="item-content">
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Header</div>
</div>
</div>
</div>
<div data-index="0" data-depth="1" class="sortable-item">
<div class="item-content">
<div class="item-indentation"></div>
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Paragraph</div>
</div>
</div>
</div>
<div data-index="1" data-depth="0" class="sortable-item">
<div class="item-content">
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Container</div>
</div>
</div>
</div>
<div data-index="0" data-depth="1" class="sortable-item">
<div class="item-content">
<div class="item-indentation"></div>
<div class="item-wrapper">
<div class="item-icon"></div>
<div class="item-title">Paragraph</div>
</div>
</div>
</div>
Thank you again #AlexMA for the tip on Functional Components.
I am a bit stuck at the moment trying to figure out how to active the current clicked element inside a loop. Basically I just want to change some CSS to that item, for example opacity, and know where I actually clicking inside the loop (this one I can handle with onclick I think).
So basically I tried this:
<div class="panel panel-default">
<ul class="list-group">
<li :style="panel.color" v-for="(panel,key,index)in getPanels"
:class="{active: panel === activeItem}" class="list-group-item"
>
A section {{panel.section}} was {{panel.action}}
</li>
</ul>
</div>
data() {
return {
activeItem: null
}
},
.active {
opacity: 0.7;
}
The active class is not getting applied to the specific clicked item. What is wrong – can anyone help?
Your code has a couple problems:
instead of :style="panel.color", you should put in an object, so it should be :style="{ color: panel.color }"
you forgot to add a click handler, i.e., you should add v-on:click="..."
Note: To simplify things, I didn't use your getPanels but use an array instead, it shouldn't affect how you understand how everything works.
const app = new Vue({
el: '#app',
data: {
panels: [
{section: 'One', action: 'Action 1', color: 'red' },
{section: 'Two', action: 'Action 2', color: 'blue' },
{section: 'Three', action: 'Action 3', color: 'green' },
{section: 'Four', action: 'Action 4', color: 'orange' },
{section: 'Five', action: 'Action 5', color: 'purple' }
],
activeItem: -1
},
methods: {
clickHandler(idx) {
this.activeItem = idx
}
}
});
.active {
opacity: 0.7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="panel panel-default">
<ul class="list-group">
<li
class="list-group-item"
v-for="(panel, index) in panels"
:class="{active: index === activeItem}"
:style="{ color: panel.color }"
v-on:click="clickHandler(index)"
>
A section {{panel.section}} was {{panel.action}}
</li>
</ul>
</div>
</div>