How to loop through nested objects using v-for loop - vue.js

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>

Related

Checking if something exists in json in Vue

I've got a json API that I'm using like this:
[{
"name": "Thing",
"id": 1234,
"total": 1,
"stock": [
{
"size": "Small",
"id": 1,
"count": 10
},{
"size": "Medium",
"id": 2,
"count": 5
},{
"size": "Large",
"id": 3,
"count": 5
}
]
}]
I'm looping over these in Vue, but want to check if anything exists in the 'stock' element outside of the v-for loop.
I can use v-if to check if the overall json exists, but can't seem to narrow it down to checking if the stock element contains anything.
Any pointers?
What about a v-if & v-else condition on the length of the stocks array? If the length is greater than zero we have stocks, so display them, else, display a message. Something like this.
Vue.component('your-component', {
template:
`
<div v-for="(item, i) in items" :key="i">
<p>{{ item.name }}<p>
<p>{{ item.id }}<p>
<div v-if="item.stock && item.stock.length > 0">
<p v-for="(stock, j) in item.stock" :key="j">
There'are {{ stock.count }} of size {{ stock.size }}.
<p>
</div>
<div v-else>
Ops... stocks not available.
</div>
</div>
`,
data () {
return {
items: []
}
},
created () {
fetch('your-api')
.then((res) => res.json())
.then((res) => { this.items = res })
}
})

How to put just a value from a radio input into an array in Vuejs and Vuex

state: {
questions: [
{
"id": 1,
"name": "q1",
"category": "English Language",
"type": "multiple",
"question": "What is a name of any person, animal, place, thing and feeling?",
"correct_answer": "Noun",
"incorrect_answers": [
"Pronoun",
"Noun",
"Adverb",
"Adjective"
]
}
]
answer = "",
answer = []
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I'm working on a quiz app and I'm using Vuex for state management. I'm having four radio values (answers) for each question and I want to be putting just the last selected value (answer) into an array that's in my Vuex state, it's working fine but whenever the use chooses another radio input (from the same question) it enters the array too, whereas I want only the selected value from each question (no matter the number of toggle in the options).
My "questions" array of 10(in length) in the state looks like this:
state: {
questions: [
{
"id": 1,
"name": "q1",
"category": "English Language",
"type": "multiple",
"question": "What is a name of person, animal, place or thing?",
"correct_answer": "Noun",
"incorrect_answers": [
"Pronoun",
"Noun",
"Adverb",
"Adjective"
]
}
...
}
and my template looks like this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
```
<div class="card-text ml-4" v-for="(answer, index) in question.incorrect_answers" :key="index" >
<label class="form-check-label">
<!-- <input type="radio" name="answer" class="mb-2" v-model="getAnswers[index]" :value="answer"> {{answer}} -->
<input type="radio" :name="question.name" class="mb-2" :value="answer" #change.stop="newAnswer(question.id, answer)" /> {{answer}}
</label> <!-- work on postanswer -->
</div>
```
and my mutation looks like this:
mutations:{
ANSWER(state, id, ans){
state.answer = id;
if(id === "q1"){
state.answers.push(state.answer);
} else {
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
I've been on this for weeks but I've not gotten it. How do I do it?
Logics for putting the answer into the array
Check whether the answer already inputted or not using the array index
If exists remove the old one and insert the latest choice
Here the code for the above logics
// Trying to find index of chosed answer
const indexOfExistingChoice = this.choosedAnswers.findIndex(
answer => answer.id === selectedAnswer.id
);
if (indexOfExistingChoice >= 0) {
// Choice already selected
// Removing the choice from the array
this.choosedAnswers.splice(indexOfExistingChoice, 1);
}
// Pushing into the array
this.choosedAnswers.push(selectedAnswer);

How to bind(v-model) data when a list with radio buttons in each list in Vue JS

I am fetching data from API. My data looks like -
[
{
id:1,
name:nameOfTheGroup1,
participants:[
{
id:1,
name:participant1
},
{
id:2,
name:participant2
}
]
},
{
id:2,
name:nameOfTheGroup2,
participants:[
{
id:3,
name:participant1
},
{
id:4,
name:participant2
}
]
}
]
As you can see its an array of objects. and in each object has nested array of objects. Basically i am trying to fetch all the groups for current user with its participants.
Now i am showing those in the browser using v-for like this -
<h3>Please assign an admin to given groups</h3>
<div v-for="group in groups">
{{ group.name }}
<div v-for="participant in group.participants">
<input type="radio" value="" v-model=""/>
<label>{{ participant.name }} </label>
</div>
</div>
Now, my question is how can i bind this data using v-model to get object/array with
group id and assigned user (radio checked ).
This is my best how i could explain))
Thanks.
First of all, your model doesn't have any field for the assigned participant. So, you need to add something like that:
id:1,
name:'nameOfTheGroup1',
assignedId: '',
participants:[
{
id:1,
name:'participant1'
},
{
id:2,
name:'participant2'
}
]
},
{
id:2,
name:'nameOfTheGroup2',
assignedId: 3,
participants:[
{
id:3,
name:'participant1'
},
{
id:4,
name:'participant2'
}
]
Then you need to provide binding:
<div v-for="participant in group.participants">
<input type="radio" v-model="group.assignedId" :value="participant.id" :name="group.id"/>
<label>{{ participant.name }} </label>
</div>
Do not forget to add "name" attribute to the radio.
Working example is here https://jsfiddle.net/7x46mtr1/
Try to use a property on participant.
Something like this
v-model=“participant.status”

How to trigger methods on created() instead of v-on:click for tags with conditions?

With the code bellow I'm displaying an element's info on click.
Right now when I click an element on the page it gets all the details about this object using this method:
<div #click="getFolderDetails(props.item)>
...
</div>
The question is, how do I get that data (run the method) not on #click but when the element is loaded (on created())?
Method:
getFolderDetails (value) {
this.objectTypeClicked = 'folder'
this.folderDetails = [
{title: "name", value: value.name},
{title: "type", value: "folder"},
{title: "created", value: this.$options.filters.prettyDateTime(value.stat.birthtime)},
{title: "modified", value: this.$options.filters.prettyDateTime(value.stat.mtime)},
{title: "path", value: value.path}
]
}
Then it displays it within other elements on the page:
<!-- display folder image if clicked has property value "folder" -->
<div v-if="objectTypeClicked == 'folder'">
<img src="/static/folder.png" alt="">
</div>
...
<!-- display specified properties of the clicked object -->
<div v-if="objectTypeClicked == 'folder'">
<div v-for="n in folderDetails" :key="n.id">
<h2 v-if="n.title == 'name'">{{n.value}}</h2>
<h3 v-if="n.value == 'folder'">folder</h3>
</div>
</div>
...
<!-- display only some of the values of the clicked object (cut off the first 2) -->
<div v-if="objectTypeClicked == 'folder'">
<div v-for="(i, index) in folderDetails" :key="index.id">
<h3 v-if="index > 1">
{{i.title}} - {{i.value}}
</h3>
</div>
</div>
Computed (takes data from store):
folderDetails: {
get () {
return this.$store.state.Info.folderDetails
},
set (value) {
this.$store.commit('loadFolderDetails', value)
}
},
objectTypeClicked: {
get () {
return this.$store.state.Info.objectTypeClicked
},
set (value) {
this.$store.commit('getObjectTypeClicked', value)
}
}
I know the code is far from optimal, but I'm still learning Vue.
I would suggest to use a computed for this. Use the method to change the folder you want to display, let's say currentFolder and assign a default on mount, like folderDetails[0], directly in the computed if this.currentFolder is null.
The computed would look like:
folderDetails() {
let value = this.folderDetails[0];
if (this.currentFolder != null) {
value = this.currentFolder;
}
return [
{title: "name", value: value.name},
{title: "type", value: "folder"},
{title: "created", value: this.$options.filters.prettyDateTime(value.stat.birthtime)},
{title: "modified", value: this.$options.filters.prettyDateTime(value.stat.mtime)},
{title: "path", value: value.path}
]
}
and the method to set on click it would become
getFolderDetails (value) {
this.objectTypeClicked = 'folder';
this.currentFolder = value;
}
In general, I always prefer to set a default state of the loaded component so that everything "works" from start.

Vue js Filters in List Rendering: Best Practices

I'm studying VueJs and as programming allows multiple ways to get to the same result (some more or less elegant)...I'm pondering on what is the best way to filter lists.
Here are a few ways i've noticed lists could be filtered:
I will refer to this new Vue instance:
new Vue({
el: '#app',
data() {
return {
selected:['diesel','petrol'],
cars: [
{
"color": "blue",
"type": "diesel",
},
{
"color": "red",
"type": "petrol",
},
{
"color": "blue",
"type": "diesel",
},
]
}
}
Method #1: the "v-show".
Where any car type pushed into the "selected" array will be rendered.
<div id="app>
<span v-for="(car, key) in cars"
v-show="selected.includes(car.type)"
>
{{car.color}} <br>
{{joke.type}} <br>
</span>
</div>
Method #2: Using a computered Property and a filter
<div id="app>
<span v-for="(car, key) in filteredCars">
{{car.color}} <br>
{{joke.type}} <br>
</span>
</div>
computed: {
filteredCars: function(){
var selected = this.selected
return this.cars.filter(function(car){
if (selected.indexOf(car.type) >= 0) {
return joke.type
}
}
)}
}
Any thoughts and best practices??
Thanks again.