Conditionally rendering v-for list based on array attribute? - vuejs2

I am looping through an array and conditionally rendering a list like so:
<tr v-for='item in list' v-if='item.sequencenumber == 0' :key='item.id'>
<th>{{item.p_propertyalphacode}}</th>
<td>
<ul>
<li>
{{item['External Entity Identifier']}}
</li>
</ul>
</td>
</tr>
<tr v-for='item in list' v-if='item.sequencenumber > 0' :key='item.id'>
<th>{{item.p_propertyalphacode}}</th>
<td>
<ol>
<li>
{{item['External Entity Identifier']}}
</li>
</ol>
</td>
</tr>
Here's a a condensed version of the list array:
[ {
"id": 100199,
"name": "Cable",
"sequencenumber": 0,
"p_propertyalphacode": "Overhead Clearance ",
"External Entity Identifier": "If the overhead obstruction does not cross a navigable waterbody, then 'Waterbody Overhead Obstruction' (OWO) does not apply and 'Vertical Clearance, Safe' (VCS) and/or 'Vertical Clearance, Open' (VCO) should not be measured for maritime-specific purposes. Instead, both 'Overhead Clearance' (OHC) and 'Underbridge Clearance' (UBC) should be measured for non-maritime purposes."
},
{
"id": 100199,
"name": "Cable",
"sequencenumber": 0,
"p_propertyalphacode": "Vertical Clearance Open ",
"External Entity Identifier": "If the overhead obstruction crosses a navigable waterbody, then 'Waterbody Overhead Obstruction' (OWO) applies and 'Vertical Clearance, Safe' (VCS) and/or 'Vertical Clearance, Open' (VCO) should be measured for maritime-specific purposes."
},
{
"id": 100199,
"name": "Cable",
"sequencenumber": 1,
"p_propertyalphacode": "External Entity Identifier ",
"External Entity Identifier": "When there is known to be a corresponding entry in the Vertical Obstruction database, specify the 'Codespace' of the 'External Entity Identifier' as 'Vertical Obstruction' (6) and populate the 'Code' value with the identifier of the corresponding entry."
},
{
"id": 100199,
"name": "Cable",
"sequencenumber": 2,
"p_propertyalphacode": "External Entity Identifier ",
"External Entity Identifier": "When there is known to be a corresponding entry in the Basic Encyclopedia database, specify the 'Codespace' of the 'External Entity Identifier' as 'Basic Encyclopedia' (7) and populate the 'Code' value with the identifier of the corresponding entry."
}
]
This renders the list like so...but notice how some items have their headings printed twice (e.g., External Entity Indentifier).
Is there a way to only print this item's table heading (again, in this case 'External Entity Identifier') once but display an ordered list containing the respective External Entity Identifier description?
Something like this is what I am aiming for:

One solution:
Use computed property to convert your data to one object, its format will be like {'Overhead Clearance':[{...}], 'Vertical Clearance Open':[{...}], 'External Entity Identifier':[{...}]}.
uses v-for to loop this object (or you can use v-for="(item index) in Object.entries(computedItem)"
If item.length ===0 ,use <ul>, if item.length > 0, use <ol>
for item.length > 0, uses another v-for to generate all <li>
Below is one demo:
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
items: [{
"id": 100199,
"name": "Cable",
"sequencenumber": 0,
"p_propertyalphacode": "Overhead Clearance ",
"External Entity Identifier": "If the overhead obstruction does not cross a navigable waterbody, then 'Waterbody Overhead Obstruction' (OWO) does not apply and 'Vertical Clearance, Safe' (VCS) and/or 'Vertical Clearance, Open' (VCO) should not be measured for maritime-specific purposes. Instead, both 'Overhead Clearance' (OHC) and 'Underbridge Clearance' (UBC) should be measured for non-maritime purposes."
},
{
"id": 100199,
"name": "Cable",
"sequencenumber": 0,
"p_propertyalphacode": "Vertical Clearance Open ",
"External Entity Identifier": "If the overhead obstruction crosses a navigable waterbody, then 'Waterbody Overhead Obstruction' (OWO) applies and 'Vertical Clearance, Safe' (VCS) and/or 'Vertical Clearance, Open' (VCO) should be measured for maritime-specific purposes."
},
{
"id": 100199,
"name": "Cable",
"sequencenumber": 1,
"p_propertyalphacode": "External Entity Identifier ",
"External Entity Identifier": "When there is known to be a corresponding entry in the Vertical Obstruction database, specify the 'Codespace' of the 'External Entity Identifier' as 'Vertical Obstruction' (6) and populate the 'Code' value with the identifier of the corresponding entry."
},
{
"id": 100199,
"name": "Cable",
"sequencenumber": 2,
"p_propertyalphacode": "External Entity Identifier ",
"External Entity Identifier": "When there is known to be a corresponding entry in the Basic Encyclopedia database, specify the 'Codespace' of the 'External Entity Identifier' as 'Basic Encyclopedia' (7) and populate the 'Code' value with the identifier of the corresponding entry."
}
]
}
},
computed: {
computedItems: function() {
return this.items.sort((a, b) => {
return a.sequencenumber - b.sequencenumber
}).reduce((pre, cur) => {
if (cur.p_propertyalphacode in pre) {
pre[cur.p_propertyalphacode].push(cur)
} else {
pre[cur.p_propertyalphacode] = [cur]
}
return pre
}, {})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<table>
<template v-for='(item, index) in computedItems'>
<tr v-if='item.length === 1' :key="index+'a'+item.id">
<th>{{item[0].p_propertyalphacode}}</th>
<td>
<ul>
<li>
{{item[0]['External Entity Identifier']}}
</li>
</ul>
</td>
</tr>
<tr v-else :key="index+'b'+item.id">
<th>{{item[0].p_propertyalphacode}}</th>
<td>
<ol>
<li v-for="(cell, index) in item" :key="index">
{{cell['External Entity Identifier']}}
</li>
</ol>
</td>
</tr>
</template>
</table>
</div>

Related

using while in v-for vue js or stop v-for after v-if is true

hey i have an array like this:
{
"id": 19,
"title": "jsj",
"orderName": "rendan",
"seller_id": null,
"totalPrice": "24000000",
"pricePaid": "4000000,20000000",
"paid": "1,0",
"dateNumber": [
"12",
"فروردین",
"1400"
],
"arrayCategories": [
"2",
"3"
],
"paidDetails": [
{
"pricePaid": "4000000",
"dateOfPayment": "1401/02/05",
"paid": "1",
"daysLeft": 3
},
{
"pricePaid": "20000000",
"dateOfPayment": "1401/02/20",
"paid": "0",
"daysLeft": 18
},
{
"pricePaid": "20000000",
"dateOfPayment": "1401/02/20",
"paid": "0",
"daysLeft": 25
}
],
"priceLeft": 20000000
}
as u can see there is a 'paidDetails' Which itself includes three other objects,
and i've used v-for in my html elements like this:
<tr v-else v-for="(project,index) in resultQuery" :key="'doctor'+index">
<th scope="row">#{{project.caseNumber}}</th>
<th scope="row">#{{project['seller']}}</th>
<td>#{{project['title']}}</td>
<td>#{{project['orderName']}}</td>
<th>#{{project.dateEnd+'-'+project.dateStart}}</th>
<td>کل:
#{{Number(project.totalPrice).toLocaleString()+' تومان '}}
<br>
باقی مانده:
#{{Number(project.priceLeft) ?
Number(project.priceLeft).toLocaleString()+' تومان ':project.priceLeft}}
</td>
<td>
<p v-if="payment.paid==0" v-for="(payment,index) in
project.paidDetails" :key="'daysLeft'+index" >
#{{ payment.daysLeft+' روز' }}
</p>
</td>
</tr>
so my question is how can i stop v-for in the last td element (which itself is a another v-for inside the parent v-for); when payment.paid is 0 and payment.daysLeft is less than the others objects inside the paidDetails.
<td>
<p v-if="payment.paid==0" v-for="(payment,index) in
project.paidDetails" :key="'daysLeft'+index" >
#{{ payment.daysLeft+' روز' }}
</p>
</td>
i think what i need is a while loop but i dont know how to do it,
any help would be appreciated
Your template is for rendering data. Do not try and manipulate your data in the template. This is an anti pattern.
Format your data in the way you want it presented and then render the formatted data in the template.
Your best bet in this instance - as Lissy 93 mentioned - is probably to create a computed property and iterate over that in your template.
Read up on computed properties here:
https://vuejs.org/guide/essentials/computed.html

V-for is not iterating through objects

I have an array of coupon objects named offers:
[{ "coupon":
{ "id": "1", "name": "Coupon 1" }
},
{ "coupon":
{ "id": "2", "name": "Coupon 2" }
}]
And i try to iterate trough it like this:
<div v-for="(coupon, $index) in offers" :key="$index">
<p>{{coupon.id}}</p>
</div>
The problem is that div does not iterate anything.
<div v-for="{ coupon } in offers" :key="coupon.id">
<p>{{ coupon.name }}</p>
</div>
Dont use index as key
you have to travel through the object, the First coupon is an element name so try this. This should work
div v-for="(coupon, $index) in offers" :key="$index">
<p>{{coupon.coupon.id}}</p>
</div>

How to read deep JSON data using Vuejs and Axios

How would I read deep JSON data nested deep inside a file? I've tried different methods and can't seem to get this to work.
<template>
<div>
<div v-for="edu in info" :key="edu">
<div>{{ edu.section.title }}</div> // this is what im trying to get to work
</div>
<div class="card container">
{{ info }}
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
info: null
}
},
mounted() {
axios
.get('./calculus.json') // Data Below
.then(response => (this.info = response.data.edu))
.catch(error => console.log(error))
}
}
</script>
My JSON looks like this:
{
"edu": {
"1": {
"title": "Title One",
"subtitle": "Subtitle One",
"description": "Description One",
"section": {
"1": {
"title": "Section One Title",
"content": "Section One Content"
}
}
},
"2": {
"title": "Title Two",
"subtitle": "Subtitle Two",
"description": "Description Two",
"section": {
"1": {
"title": "Section One Title",
"content": "Section One Content"
}
}
}
}
}
How can I use vue-for and get the data inside the section to get it to display under the title? For example: title, section>title, section>subtitle, etc.
Given each section is also an object with weird numeric keys, you can iterate them in the same way you do info.
I would also advise you to use identifiable values instead of the entire edu object in your :key bindings.
<div v-for="(edu, eduId) in info" :key="eduId">
<div v-for="(section, sectionId) in edu.section" :key="sectionId">
{{ section.title }}
</div>
</div>
If possible, I would alter the format of your JSON data to use actual arrays instead of objects with numeric keys.
One way to browse your object deeply is to cumulate v-for on your object (and children) entries.
ie:
<div v-for="([category, books], catkey) in Object.entries(info)" :key="`category-${catkey}`">
<div>{{ category }} :</div>
<div v-for="([num, book], numkey) in Object.entries(books)" :key=`book-${catkey}-${numkey}`>
<div v-for="([field, value], valkey) in Object.entries(book)" :key=`field-${catkey}-${numkey}-${valkey}`>
{{ field }} : {{ value }}
</div>
</div>
</div>
If you find it too verbose, you may try to flatten your computed data to have the following structure:
[
{
"category": "edu",
"id": "1",
"title": "Title One",
"subtitle": "Subtitle One",
"description": "Description One",
"section": {
"1": {
"title": "Section One Title",
"content": "Section One Content"
}
}
}
]

How to loop through nested objects using v-for loop

I'm working on some practice code that deals with card information, in which you can display the chosen card's detailed information by clicking one of the cards on the screen.
As demonstrated in the screenshots, if you choose one of the yellow cards, it displays more detailed information of the chosen card with green and blue background color.
I implemented this by using v-for loop, but the problem is that the detailed card information is a JSON object that contains multiple JSON objects inside, and I haven't been successful in displaying all of the members in non-JSON form.
I found some pages (like the link below) where some ways to loop through nested objects were discussed, but it was plain JavaScript code and I couldn't use the same strategy for v-for loop.
How to loop through a plain JavaScript object with the objects as members?
I understand the idea that you should just continue the loop in case the member is another object, not a primitive data type, but I don't know how to implement the same logic in v-for loop.
Could anyone tell me how to do it?
Here is my code.
(v-for loop part)
<div v-for="(obtainedCardInfo, index) in obtainedCardsInfo">
<span v-if="cardBtnChosen && card.id == selectedCard && obtainedCardInfo.id == selectedCard">
<span class="cardInfo">DETAILED CARD INFO:</span>
<div class="cardInfoDisplay">
<div v-for="(detailedInfo,index) in obtainedCardInfo" :key="index">
<p v-if="obtainedCardInfo[index]"> {{index}} : {{obtainedCardInfo[index]}} </p>
<p v-else> {{index}} : NULL </p>
</div>
</div> <br>
</span>
</div>
and the output for my current code.
DETAILED CARD INFO:
accountId : 3917674
id : 3918534
customerId : 998774
cardRole : MAIN
cardStatus : CARD_OK
truncatedCardNumber : 524804______9042
cardTemplate : MC_CARD
cardAddress : NULL
usageLimits : [ { "code": "WEEKLY", "values": null }, { "code": "DAILY", "values": [ { "code": "ATM", "singleAmount": 200, "count": 3, "sumAmount": 300 } ] }, { "code": "MONTHLY", "values": [ { "code": "ATM", "singleAmount": null, "count": 1000, "sumAmount": 1000000 } ] } ]
expiration : { "year": 2022, "month": 6 }
pinAddress : NULL
regionAndEcommBlocking : { "ecomm": false, "africa": false, "asia": false, "europe": false, "home": false, "northAmerica": false, "oceania": false, "southAmerica": false }
The v-for simply iterate through the array or the object keys.
v-for iterates through each element in the array
v-for also iterates through the keys in the object
You should also move your logic to a computed method
<template>
<p v-for:"item, index in arr" />
{{ item }}
{{ index }}
<p v-for:"item, key in obj" />
{{ item }}
{{ key }}
<br />
</template>
<script>
export default {
data() {
return {
arr:[1,2,3,4,5],
obj: { 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e' }
}
},
computed: {
// do computation here
doSomething() {
}
}
}
</script>

Loop over response objects based on condition angular 5

I have a rest response which i want to loop over but based on condition, here's my response:
{
"veichleType": "SUV",
"veichleBrand": "BMW",
"veichleModels": [{
"veichleModel": "M3",
"property1": "value",
"property2": "value",
"property3": "value"
},
{
"veichleModel": "M1",
"property": "value",
"property2": "value",
"property3": "value"
}
]
}
I want in the UI to show only one object based on veichleModel if user searched by M3 then display that object response in the table, else show M1.
<table>
<thead>
headings here
</thead>
<tbody *ngIf='serviceResponse.veichleModels'>
<tr *ngFor="let veichleModel of serviceResponse.veichleModels">
<td>{{veichleModels.property1}}</td>
<td>{{veichleModels.property2}}</td>
<td>{{veichleModels.property3}}</td>
</tbody>
</table>
You need to do two things here :
Add some variable to store the selected model M1 or M3
Add condition inside loop to display respective object properties (Make use of ng-container)
Doing so, you can scale your array to 100 or more objects
template
<tbody *ngIf='serviceResponse.veichleModels'>
<tr *ngFor="let veichle of serviceResponse.veichleModels">
<ng-container *ngIf="veichle.veichleModel == selected">
<td>{{veichle.property1}}</td>
<td>{{veichle.property2}}</td>
<td>{{veichle.property3}}</td>
</ng-container>
</tr>
</tbody>
ts file
export class AppComponent {
selected = 'M1';
serviceResponse = {"veichleModels": [{
"veichleModel": "M3",
"property1": "M3val1",
"property2": "M3val2",
"property3": "M3val3"
},
{
"veichleModel": "M1",
"property1": "M1val1",
"property2": "M1val2",
"property3": "M1val3"
}
]}
}