how to access nested json object property using nuxtjs and v-for - vue.js

I want to access a nested object using v-for in nuxtjs,I have a get request using Axios to an API and response data is stored in a variable named CategoriesData look at the code below.
asyncData(context) {
return axios
.get("https://lavazemesakhteman.com/wp-json/wc/v3/products/categories", {
params: {
per_page: 10,
page: 1,
},
})
.then((res) => {
return {
CategoriesData: res.data,
};
})
.catch((e) => context.error(e));}
at this point, everything is good and all API response is stored in CategoriesData, my problem starts when I want to iterate over the nested object and display or use its property, pay attention to the code below:
<ul>
<li v-for="categories in CategoriesData" :key="categories.id">
{{ categories.name }}
{{ categories.image }}
</li>
</ul>
the result is:
I want to display and use categories.image[0].src for example src only not the whole object, the API response is pictured below:
this is a part of JSON code:
"display": "default",
"image": {
"id": 10443,
"date_created": "2022-04-30T05:45:44",
"date_created_gmt": "2022-04-30T01:15:44",
"date_modified": "2022-04-30T05:46:36",
"date_modified_gmt": "2022-04-30T01:16:36",
"src": "https://lavazemesakhteman.com/wp-content/uploads/2022/04/Benkan-Connections2.jpg",
"name": "Benkan Connections",
"alt": "فروشگاه لوازم ساختمان عرضه کننده محصولاتی باکیفیت و همچنین با بهترین قیمت در تمام کشور هست."
},
when is used categories.image.src:
<ul>
<li v-for="categories in CategoriesData" :key="categories.id">
{{ categories.name }}
{{ categories.image.src }}
</li>
</ul>
I get the below error:
Cannot read properties of null (reading 'src')

You can access nested data using the same . notation you already use to get the first field, so you would need to write something like {{ categories.image.src }}.
<template>
<ul>
<li v-for="category in CategoriesData" :key="category.id">
{{ category.name }}
<img :src="category.image.src">
</li>
</ul>
</template>
Note: You named the variable in your v-for as a plural, while the data it holds is singular. As each item in the list of categories is an individual category.

Going to the API itself, located here: http://lavazemesakhteman.com/wp-json/wc/v3/products/categories
Showed that sometimes your image can be null, hence checking for this exception is a nice thing to do
<img v-if="category.image" :src="category.image.src">
Depending on your project and how it's setup, you could even use Optional Chaining like this
<img :src="category.image?.src">
Otherwise, this uglier form is also available
<img :src="category.image && category.image.src">

Related

View binded :key values in Vue3

<li v-for="(size, index) in sizes" :key="index">{{ size }}</li>
I'm new to VueJS and I'm playing around with Vue Directives. I wanna know where to get the list of :key values in the console log or developer tools. For now, I'm setting it to id attribute and reading it. Appreciate any kind of help
If you're just playing around with it and want to be able to log it to console, you could add a log function to your methods
methods:{
log(..args){
console.log(...args)
}
}
then you can use the log function anywhere and pass it the same value
<li v-for="(size, index) in sizes" :key="index">{{ size }}{{log(index)}}</li>
...but that only works if you can pass the same value to both
Example:
Vue.createApp({
data: () => ({
items: ['a', 'b', 'c']
}),
methods: {
log(...args) {
console.log(...args)
},
},
}).mount("#app");
<script src="https://unpkg.com/vue#3.2.0/dist/vue.global.prod.js"></script>
<div id="app">
<li v-for="(item, index) in items" :key="index">{{ item }}{{log(index)}}</li>
</div>

How to search within nested objects

I have done my research trying to figure out how to achieve what I am describing below, however I had no luck.
In my Algolia index, some records have nested objects.
For example, title and subtitle attributes are of the following format:
title:
{
"en": "English title",
"gr": "Greek title"
}
I would like to execute queries only for a specific subset (in our example "en" or "gr") of these attributes, withoute "exposing" any facet in the UI — language selection would ideally be done “automatically” based on a variable (lang) passed to the Vue component with props. I am using Laravel Scout package with default Vue implementation, as described in documentation here.
My InstantSearch implementation is pretty simple, I am not defining anything specific regarding queries and searchable attributes, I am currently using all the default functionality of Algolia.
<template>
<ais-instant-search
:search-client="searchClient"
index-name="posts_index"
>
<div class="search-box">
<ais-search-box placeholder="Search posts..."></ais-search-box>
</div>
<ais-hits>
<template
slot="item"
slot-scope="{ item }"
>
<div class="list-image">
<img :src="'/images/' + item.image" />
</div>
<div class="list-text">
<h2">
{{ item.title }}
</h2>
<h3>
{{ item.subtitle }}
</h3>
</div>
</template>
</ais-hits>
</ais-instant-search>
</template>
<script>
import algoliasearch from 'algoliasearch/lite';
export default {
data() {
return {
searchClient: algoliasearch(
process.env.ALGOLIA_APP_ID,
process.env.ALGOLIA_SEARCH
),
route: route,
};
},
props: ['lang'],
computed: {
computedItem() {
// computed_item = this.item;
}
}
};
</script>
I would like to somehow pass an option to query “title.en” and “subtitle.en” when variable lang is set to “en”. All this, without the user having to select “title.en” or “subtitle.en” in the UI.
Update
Maybe computed properties is the path to go, however I cannot find how to reference search results/hits attributes (eg item.title) within computed property. It is the code I have commented out.
I think, you can use computed property. Just transform current item according to the current language variable.
new Vue({
template: "<div>{{ computedItem.title }}</div>",
data: {
langFromCookie: "en",
item: {
title: {
en: "Hello",
ru: "Привет"
}
}
},
computed: {
computedItem() {
const item = JSON.parse(JSON.stringify(this.item));
for (value in item) {
if (typeof item[value] === "object" && Object.keys(item[value]).includes(this.langFromCookie))
item[value] = item[value][this.langFromCookie];
}
return item;
}
}
}).$mount("#app")
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
If lang variable is available via props, you can check that inside list-text class and return {{title.en}} or {{title.gr}} accordingly by passing a dynamic lang value title[lang] like below
...
<div class="list-text">
<h2>
{{ item.title[lang] }}
</h2>
<h3>
{{ item.subtitle[lang] }}
</h3>
</div>
If you want to make a request according to lang prop when component mounts ,then you can make a request inside mounted() method then query like below
mounted() {
axios.get(`/getSomethingWithLang/:${this.item.title[this.lang]}`)
...
}

Using Slots or slot-scopes in v-for loops to access properties?

I'm having a difficult time understanding slots for some reason and why they should even be used. The only reason I can think of that would be nice for usuage is if we can reference specific properties within a v-for loop of an element and output different templates quicker perhaps...
So, am thinking, and possibly I could be wrong in thinking this, but if I have a variable like so:
<script>
const items: [
{
label: 'My Label',
url: '#',
headerTitle: 'My Header Title'
},
{
label: 'My Label 2',
url: 'https://www.myurl.com',
headerTitle: 'My Header Title 2'
},
{
label: 'My Label 3',
url: 'https://www.myurl3.com'
}
]
export default {
data () {
return {
items: items
}
}
}
</script>
And than in the template, possibly this:
<template>
<div v-for="(item, index) in items" :key="item.id">
<template slot-scope="headerTitle">
<h1>{{ item.headerTitle }}</h1>
</template>
<template slot-scope="label">
<div class="mylabel">
{{ item.label }}
</div>
</template>
<template slot-scope="url">
<a :href="item.url">{{ item.label }}</a>
</template>
</div>
</template>
I don't know if this makes sense or not, but basically using the property as a slot-scope and than for everytime that property is defined, it will output something. But this doesn't work properly. Is this not what slot-scopes are for within component v-for loops? Is this not how to use these properties of an array of objects?
This kinda makes sense to me. Anyways to do it like this perhaps?

How to remove Vuejs "scope is undefined' warning?

As a beginner with Vue.js, I have a component that looks like this:
<template>
<div>
<a href="{{ data.uk_store_link }}">
{{ data.title }} - {{ data.artist.name }}
</a>
</div>
</template>
<script>
export default {
props: ['data']
}
</script>
"data.artist.name" is present and correct in the object that is passed to the data prop. But how can I get rid of the warning that pops up about the nested artist name?
[Vue warn]: Error when evaluating expression "data.artist.name":
TypeError: scope.data.artist is undefined (found in component:
<release>)
ETA: Ah, sorry, yes, it would have been helpful to show the "data":
{ "id": 23182, "title": "Music from 'Likely Stories'",
"uk_store_link":
"http://store.roughtraderecords.com/...html",
"artist": { "id": 1176, "name": "Jarvis Cocker" } }
I've stripped out some fields there for brevity, but hopefully this demonstrates that data.artist.name does actually exist. It certainly gets successfully output to the page, despite the warning.
{{ data.artist ? data.artist.name : '' }}

Vuejs v-for set unique data

I have a component that I can add when I click on the button.
<button type="submit" #click="components ++">add select box</button>
<div v-for="component in components">
<select class="selectpicker form-control" v-model="customized_exercise.name" value={{exercise.id}}>
<option v-for="exercise in exercises">{{ exercise.name }}</option>
</select>
</div>
In this template when I add a couple of them and select a value,all the other components (select box) update with the same value.How can I make them have a unique value?
Vue
import ExerciseSelectbox from './ExerciseSelectbox.vue'
export default {
methods: {
fetchexercises: function(){
this.$http.get('/api/exerciseinstructions').then(function (data) {
this.$set('exercises',data['data'])
})
},
},
comoponents: { ExerciseSelectbox },
data() {
return{
components: 1,
newCustomizedExercise : {
id:'',
name:'',
reps:'',
sets_duration:'',
weight:'',
},
numbers:[100]
}
},
ready() {
this.fetchexercises()
}
}
This is old, but I was also looking for this answer. I found that the Vue documentation mentions this:
Inside v-for blocks we have full access to parent scope properties.
v-for also supports an optional second argument for the index of the
current item.
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
So you can use this index parameter to set the id of each element in your loop to keep each one separate.
Or you can set the set your model to a name in the array. Something like v-model="customized_exercise[index].name". But I haven't actually tried this option.
I found another SO answer about how to use v-bind if you want to string concatenate the index variable in a data attribute.
<a v-bind:href="'uniqueName' + index">12312</a>