Display an object in Vue template - javascript

I have an object like:
Obj = {
'min_mix': 1,
'max_mix': 2,
'climbing': {
'easy':[
{
'hour': 1.0,
'id':0,
}
],
'height': [
{
'hour': 1.0,
'price': 100
}
]
}
}
I have to display this in my HTML:
min_mix : 1
max_mix : 2
climbing:
easy:
hour : 1.0
id : 0
height:
hour: 1.0
price: 100
For now I use some v-for, but I don't have a good result,
I don't understand how display correctly the array in easy and height?
https://jsfiddle.net/CanadianDevGuy/fjaoztgn/15/

Please check this https://jsfiddle.net/Lewvsp0k/
<v-card-text v-for='(value,name) in obj' :key='name' class="pt-0 pb-0">
<template v-if="typeof value !== 'object'">
<b>{{name}} : {{ value }} </b>
</template>
<template v-else>
<div v-for="(rowValue, rowIndex) in value" :key='rowIndex' class="mb-2 pa-0">
<b>{{name}} : </b>
<div v-for="(rowValue1, rowIndex1) in rowValue" :key='rowIndex1' class="mb-2 pa-0">
<b>{{rowIndex1}} : </b>
<div>
<div v-for="rowValue2 in rowValue1" :key='rowValue2' class="mb-2 pa-0">
<div v-for="(rowValue3, rowIndex3) in rowValue2" :key='rowValue3' class="mb-2 pa-0">
<b> {{rowIndex3}} : {{rowValue3}} </b>
</div>
</div>
</div>
</div>
</div>
</template>
</v-card-text>

Should work
<template v-for="(item, index) in Obj ">
<template v-if="typeof item !== Object">
<div :key="index">
{{ index }} : {{ item }}
</div>
</template>
<template v-else>
<div :key="'subitem' + indexSubItem" v-for="(subItem, indexSubItem) in item">
{{ indexSubItem }} : {{ subItem }}
</div>
</template>
</template>

The easiest way is to actually create a new component, say <object-entry>, that can perform iterative nesting logic: if the value it encounters is a string or number, we print it out directly; otherwise, we nest another level of <object-entry> in it.
The <object-entry> component (you can also rename it to anything you like, that makes more sense) will do the following:
always print the key from the key-value pair.
decide if it should print the value from the key-value pair:
if value is a typeof string/number, we print it
if value is not (then it is an array or object), we then call the component itself again (effectively nesting itself recursively)
In pseudo-code it looks like this:
<div>
<strong>{{ key }}</strong>:
<template v-if="isStringOrNumber(value)">
{{ value }}
</template>
<object-entry
v-else />
</div>
This ensures that we will keep indenting until we encounter the "bottommost" value that is either a number or string.
Note: You cannot truly get rid of the object index in the markup, due to the way your data is structured: if climbing is supposed to only contain 'easy' and 'height', they should not be double nested inside a meaningless key of 0. Likewise, for easy and height, it makes little sense they are stored as array of objects when it should be just an object itself.
See proof-of-concept here, with suggested changes to your data (you can also use your original data structure, but it will come with an extraneous level of nesting):
Vue.use(Vuetify);
Vue.component('object-entry', {
template: '#object-entry-template',
props: ['entryKey', 'entryValue'],
methods: {
isStringOrNumber: function(v) {
return typeof v === 'string' || typeof v === 'number';
}
}
})
var vm = new Vue({
el: "#app",
data: () => ({
obj: {
'min_mix': 1,
'max_mix': 1,
'climbing': {
'easy': {
'hour': 1.0,
'id': 0
},
'height': {
'hour': 1.0,
'price': 100.0
}
}
},
})
});
<link href="https://unpkg.com/vuetify#0.14.8/dist/vuetify.min.css" rel="stylesheet"/>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.14.8/dist/vuetify.min.js"></script>
<div id="app">
<v-app>
<object-entry
v-for="(value, key) in obj"
v-bind:key="key"
v-bind:entry-key="key"
v-bind:entry-value="value" />
</v-app>
</div>
<script type="text/x-template" id="object-entry-template">
<v-card-text class="pt-0 pb-0">
<strong>{{ entryKey }}</strong> :
<template v-if="isStringOrNumber(entryValue)">
{{ entryValue }}
</template>
<!-- Here, the component nests itself if value is not a string/number -->
<object-entry
v-else
v-for="(value, key) in entryValue"
v-bind:key="key"
v-bind:entry-key="key"
v-bind:entry-value="value" />
</v-card-text>
</script>

Related

How to loop thought map and create specific object ? Using Javascript

All code you can find here: https://stackblitz.com/edit/angular-display-show-json-in-propertlu-format?file=src%2Fapp%2Fapp.component.ts
I have a problem formatting the array I'm loop through.
Main logic function is this:
this.allFilters.push(
Array.isArray(val.value)
? {
name: val.name,
value: val.value.map((obj: any) => obj.value).join(" - "),
displayName: val.displayName,
displayValue: val.value.map((obj: any) => obj.value).join(" - ")
}
: {
name: val.name,
value: val.value,
displayName: val.displayName,
displayValue: val.displayValue
}
);
Let me explain what I did here. Loop the thought array and push values. If it is array, the first condition goes, if it is not through, then the second.
Example of allFilters and val you can see on stackblitz and code bellow.
I loop thought array very simple:
<span
*ngFor="let filter of allFilters">
{{ filter.displayName }}: {{ filter.displayValue }}
</span>
If are you using VUE.js this is
<span
v-for="filter in allFilters" :key="filter.name>
{{ filter.displayName }}: {{ filter.displayValue }}
</span>
I need create object like a:
First: 2
Second: dateFrom 2021-04-08
Second: dateTo: 2021-04-20
Third: 15
The issue is not connected out for vue or angular or even reactions. This is js problem.
No need for such remappings, displaying logic should be usually in template, something like:
<span
*ngFor="let filter of allFilters"
class="badge badge-light my-3 mx-2 px-3 py-1">
<ng-container *ngIf="isArray(filter.value); else noArray">
<div *ngFor="let obj of filter.displayValue">
{{filter.displayName}}: {{obj.name}} {{obj.value}}
</div>
</ng-container>
<ng-template #noArray>
{{filter.displayName}}: {{filter.displayValue}}
</ng-template>
</span>
js:
isArray(value) {
return Array.isArray(value);
}
recieveMessage(val) {
this.allFilters.push(val);
}
It is possible to do it in template, like this:
<hello name="{{ name }}"></hello>
<div class="span">
<span
*ngFor="let filter of allFilters"
class="badge badge-light my-3 mx-2 px-3 py-1"
>
<ng-container *ngIf="filter.name != 'Second';else withDate">
{{ filter.displayName }}: {{ filter.displayValue }}
</ng-container>
<ng-template #withDate>
Second: dateFrom {{ filter.displayValue|slice:0:10 }}<br>
Second: dateTo: {{ filter.displayValue|slice:13:24 }}
</ng-template>
</span>
</div>

v-for and state management issue

I have a list of items rendered with v-for. I want each item to have a "?" that is clickable to show a modal containing a description for that specific item. My issue right now is that when the "?" is clicked, it shows the modal for every item in the v-for. How do i solve this?
<div
v-for="(item, index) in items"
:key="index"
>
<div>
{{ item.name }}
<div>
<span #click="itemModal = true">
?
</span>
<div v-show="itemModal">
{{ item.description }}
<button #click="itemModal = false">
Close modal
</button>
</div>
</div>
</div>
</div>
export default {
data() {
return {
itemModal: false
}
}
}
Your itemModal property is share with all items currently, so you need one modal status for each item.
eg. you can create a toggle method to update an array of modal status:
<div
v-for="(item, index) in items"
:key="index"
>
<div>
{{ item.name }}
<div>
<span #click="toggle(index)">
?
</span>
<div v-show="itemModal[index]">
{{ item.description }}
<button #click="toggle(index)">
Close modal
</button>
</div>
</div>
</div>
</div>
export default {
data() {
return {
itemModal: []
}
},
methods: {
toggle(index) {
this.$set(this.itemModal, index, !this.itemModal[index])
}
}
}
nb: an array (or an object) is not reactive in depth, so we have to use Vue.$set (cf. docs)

using 'v-for' to output object to list

I have an object that get dynamically updated with various key/value, for the most part it will look like below:
object: {
parentKey1:{
childKey1:'value',
childKey2:'value'
},
parentKey2:{
childKey3:'value',
childKey4:'value'
}
}
I am then using 'v-for' in a list to export the object, like so:
<ul>
<li v-for="(value, key) in object">{{ key }} - {{ value }}</li>
</ul>
Now, it's displaying on the DOM generally how I want it, except that its printing out {} curly brackets around the values.
PARENT - { "CHILD": "VALUE" }
I'd like it to be:
PARENT - CHILD VALUE
Try this
new Vue({
el: '#app',
data: {
object: {
parentKey1: {
childKey1: 'value',
childKey2: 'value'
},
parentKey2: {
childKey3: 'value',
childKey4: 'value'
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<div v-for="(value, key) in object">
{{key}} -
<span v-for="(cvalue, ckey) in value">
{{ ckey }} {{ cvalue }} {{' '}}
<span>
</div>
</div>
You need to provide a new nested iteration to do that.
<ul>
<li v-for="(parentValue, parentKey) in object">
<template v-for((childValue, childKey) in parentValue)>
{{ parentKey }} - {{ childKey }} {{ childValue }}
</template>
</li>
</ul>
Well the value is still an Array thats why it is displayed like that.
Maybe try
<ul>
<li v-for="(value, key) in object">{{ key }} - <span v-for="val in value">{{ val }}</span></li>
</ul>

Vuejs v-for on inline elements trims whitespace

When looping over an array (or object) with v-for on an inline element, vuejs does not render whitespace around said element.
For example, I have this html:
<div id="app">
Vue Rendering<br>
<a v-for="fruit in fruits" v-bind:href="fruit.url" v-html="fruit.label"></a>
</div>
<div>
Navite rendering<br>
Apple
Banana
Peach
</div>
and this javascript:
var fruits = [
{
label: 'Apple',
url: 'apple.html'
},
{
label: 'Banana',
url: 'banana.html'
},
{
label: 'Peach',
url: 'peach.html'
}
];
var app = new Vue({
el: '#app',
data: {
fruits: fruits
}
});
When Vue renders this, it deletes the spaces between the links. See this jsfiddle.
How can I counter this behaviour ?
From the Docs on List Rendering > v-for on a <template>
Similar to template v-if, you can also use a <template> tag with v-for to render a block of multiple elements. For example:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
So in order to get sibling elements (and yeah, a breaking space character would count as one), you'll have to add the loop to a parent <template> container and then include any elements / spacing you want in the looped contents like this:
<template v-for="fruit in fruits" >
<span>{{fruit}}</span>
</template>
As of Vue 2.x+, templates trim any breaking space characters, even if they are escaped.
Instead, you can add a slot or text interpolation like this:
<template v-for="fruit in fruits" >
<span>{{fruit}}</span><slot> </slot>
</template>
<template v-for="fruit in fruits" >
<span>{{fruit}}</span>{{ ' ' }}
</template>
If you only want spaces in-between elements, you can output the space conditionally:
<template v-for="(fruit, i) in fruits" >
<span>{{fruit}}</span>{{ i < fruits.length -1 ? ', ': '' }}
</template>
Demo in Stack Snippets
var app = new Vue({
el: '#app',
data: {
fruits: ["apple", "banana", "carrot"]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.0/vue.js"></script>
<div id="app">
<template v-for="(fruit, i) in fruits" >
<span>{{fruit}}</span>{{ i < fruits.length -1 ? ', ': '' }}
</template>
</div>
Further Reading:
Issue #1841 - Suggestion: v-glue / v-for element joins
I had a case where I needed to wrap each character (including spaces) of a string in a <span> and solved it this way.
<template v-for="(letter, i) in 'My text with spaces'">
<span v-if="letter !== ' '">{{ letter }}</span>
<span v-else> </span>
</template>
You can use CSS:
<a v-for="fruit in fruits" v-bind:href="fruit.url" v-html="fruit.label"
style="margin-right: 5px;"></a>

Issue on Passing param to Template as Variable in Html Using Vue JS

I use Vue js to implement template system, its completely dynamic.
So i need to pass attributes and variables to template and each template has different values.
I done on passing attributes, but issue on passing value of a variable.
Here,
My HTML :
<div v-for="product in productsList">
<block v-if="column == 4 || column > 4"
:listAlign="showList ? 'left' : 'center'"
:product_name = product.name
:showAction="showAction"
:block_class="showList ? 'col-md-12 col-sm-12 col-xs-12' : 'col-md-3 col-sm-6 col-xs-6'">
</block>
</div>
Here, i have to pass the value of 1. product_name, 2. product_price, 3. showAction
Also the Class and Align attributes are passed successfully.
My Template :
<template v-if="showTemplate" id="campaignBlock">
<div :class="block_class" :align="listAlign">
<p>#{{ product_name }}</p>
<p>Price : #{{ product_price }}</p>
<input v-show="showAction" type="button" #click="alt()" class="btn btn-default
" value="Action">
</div>
</template>
My VueJS :
Vue.component('block', {
template: '#campaignBlock',
props: ['block_class', 'align', 'listAlign','showAction', 'product_name','product_name'],
data: function () {
return {
n: 0,
nb: 1,
column: 2,
showPrice: false,
showAction: true,
showList: false,
listAlign: 'left'
}
}
});
I only having trouble with passing variables to template.
Is this concept is possible ?
Or any other solution for this issue ?
Thanks in Advance.
What exactly error you are getting. I have corrected few mistakes, check below:
<template>
<div v-if="showTemplate" id="campaignBlock">
<div :class="{'col-md-12' : block_class_var}" :align="listAlign">
<p>{{ product_name }}</p>
<p>Price : {{ product_price }}</p>
<input v-show="showAction" type="button" #click="alt()" class="btn btn-default
" value="Action">
</div>
</div>
</template>
Here is the documentation of using dynamic class in vueJs.

Categories