How to remove Vuejs "scope is undefined' warning? - vue.js

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 : '' }}

Related

How to make single property in array reactive when using `ref` instead of `reactive`?

I have a component that displays rows of data which I want to toggle to show or hide details. This is how this should look:
This is done by making the mapping the data to a new array and adding a opened property. Full working code:
<script setup>
import { defineProps, reactive } from 'vue';
const props = defineProps({
data: {
type: Array,
required: true,
},
dataKey: {
type: String,
required: true,
},
});
const rows = reactive(props.data.map(value => {
return {
value,
opened: false,
};
}));
function toggleDetails(row) {
row.opened = !row.opened;
}
</script>
<template>
<div>
<template v-for="row in rows" :key="row.value[dataKey]">
<div>
<!-- Toggle Details -->
<a #click.prevent="() => toggleDetails(row)">
{{ row.value.key }}: {{ row.opened ? 'Hide' : 'Show' }} details
</a>
<!-- Details -->
<div v-if="row.opened" style="border: 1px solid #ccc">
<div>opened: <pre>{{ row.opened }}</pre></div>
<div>value: </div>
<pre>{{ row.value }}</pre>
</div>
</div>
</template>
</div>
</template>
However, I do not want to make the Array deeply reactive, so i tried working with ref to only make opened reactive:
const rows = props.data.map(value => {
return {
value,
opened: ref(false),
};
});
function toggleDetails(row) {
row.opened.value = !row.opened.value;
}
The property opened is now fully reactive, but the toggle doesn't work anymore:
How can I make this toggle work without making the entire value reactive?
The problem seems to come from Vue replacing the ref with its value.
When row.opened is a ref initialized as ref(false), a template expression like this:
{{ row.opened ? 'Hide' : 'Show' }}
seems to be interpreted as (literally)
{{ false ? 'Hide' : 'Show' }}
and not as expected as (figuratively):
{{ row.opened.value ? 'Hide' : 'Show' }}
But if I write it as above (with the .value), it works.
Same with the if, it works if I do:
<div v-if="row.opened.value">
It is interesting that the behavior occurs in v-if and ternaries, but not on direct access, i.e. {{ rows[0].opened }} is reactive but {{ rows[0].opened ? "true" : "false" }} is not. This seems to be an issue with Vue's expression parser. There is a similar problem here.

Vue - Unable to use variable in template

I have this code in my VUE file:
<template>
<div class="row">
<div class="col-12">
<section class="list">
<draggable class="drag-area" :list="picsNew" :options="{animation:200, group:'status'}" :element="'article'" #add="onAdd($event, false)" #change="update">
<article class="card" v-for="(photo, index) in picsNew" :key="photo.id" :data-id="photo.id">
<header>
{{ this.galCode }}{{ photo.filename }}
</header>
</article>
</draggable>
</section>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable
},
props: ['myPics', 'galId', 'phCode', 'galCode'],
data() {
return {
picsNew: this.myPics,
}
},
methods: {
update() {
this.picsNew.map((photo, index) => {
photo.order = index + 1;
});
let photos = this.picsNew;
console.log(this.galCode)
axios.put('/gallery/' + this.galId + '/updateAll', {
photos: photos
}).then((response) => {
console.log(response.data);
}).catch((error) => {
console.log(error);
})
}
}
}
</script>
in the template, photo.filename works, but this.galCode throws these two errors:
app.js:44152 [Vue warn]: Error in render: "TypeError: Cannot read property 'galCode' of undefined"
found in
---> <DraggablePic> at resources/js/components/draggablepic.vue
app.js:45415 TypeError: Cannot read property 'galCode' of undefined
The variable contains a value, as i am printing it to console. What am I doing wrong?
The problem is that you try to access props variable directly from your template. To solve this, initalize a state variable (under data {}) Within props value as default like you did With picsNeW.
another remark, avoid to use "this" from template, you should access directly datas form its name. Use default value in your props, this is recommended :)
The line {{ this.galCode }}{{ photo.filename }} should be {{ galCode }}{{ photo.filename }}. Your problem is the this you added
First you should define Default value as :
props: {
name: {
type: String,
default: 'default'
},
...
galCode: {
type: Number,
default: 0
},
},
Second be sure you receive desired data, specially in nested object you should check the object defined to prevent receive undefined property error, like use v-if :
<header>
<template v-if="galCode">{{ galCode }}</template> // can use v-else here too print default value
<template v-if="photo.filename"> {{ photo.filename }}</template>
</header>

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]}`)
...
}

Vuetify breadcrumbs scoped slot

I'm trying to incorporate breadcrumbs into my vuetify app however this is my code:
<v-breadcrumbs divider=">">
<v-breadcrumbs-item
v-for="breadcrumb in breadcrumbs"
exact
:to="breadcrumb.to">
{{ breadcrumb.text }}
</v-breadcrumbs-item>
</v-breadcrumbs>
And then I get this warning in the console. Im not really sure what it means..
default slot' is deprecated, use ':items and scoped slot "item"' instead
If i try the default template from vuetify docs: <v-breadcrumbs :items="items"> it only allows for a href not a :to which is what I need.
Can anyone help me out.
It seems that Veutify now provides an item scoped slot that can be used to customize breadcrumbs. Looking at their example here I think what you need to do is the following ..
<v-breadcrumbs :items="breadcrumbs" divider=">">
<v-breadcrumbs-item
slot="item"
slot-scope="{ item }"
exact
:to="item.to">
{{ item.text }}
</v-breadcrumbs-item>
</v-breadcrumbs>
alternative is use a method with a click event
<template>
<v-breadcrumbs divider=">">
<v-breadcrumbs-item
v-for="breadcrumb in breadcrumbs"
exact
#click="goTo(breadcrumb.to)">
{{ breadcrumb.text }}
</v-breadcrumbs-item>
</v-breadcrumbs>
</template>
<script>
export default {
methods: {
goTo (payload) {
this.$router.push(payload)
}
}
}
</script>
You can use below code which is much simpler compare to the above solutions:
<template>
<v-breadcrumbs :items="breadcrumbs">
<template v-slot:divider>
<v-icon>mdi-menu-right</v-icon>
</template>
</v-breadcrumbs>
</template>
<script>
export default {
data() {
return {
breadcrumbs: [
{
nuxt: true,
activeClass: "",
text: "Page A",
to: { name: "page-a" },
},
{
disabled: true,
text: "Page B",
},
],
};
},
};
</script>
Example from the official website,
<template>
<v-breadcrumbs :items="items">
<template v-slot:item="{ item }">
<v-breadcrumbs-item
:href="item.href"
:disabled="item.disabled"
>
{{ item.text.toUpperCase() }}
</v-breadcrumbs-item>
</template>
</v-breadcrumbs>
</template>

"Property or method is not defined on the instance but referenced during render"

I need to display the item.title outside the <v-carousel> but I get this error message:
[Vue warn]: Property or method "item" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I checked the results from the Stackoverflow search but I really struggle to understand it. I would be grateful if somebody could explain it to me by this example.
Here is my code:
<v-carousel>
<v-carousel-item v-for="(item,i) in items" v-bind:src="item.src" :key="i">
</v-carousel-item>
</v-carousel>
<h1>TITLE: {{ item.title }}</h1>
<script>
export default {
data () {
return {
items: [
{
src: '/static/a.jpg',
title: 'A',
text: 'texta'
},
{
src: '/static/b.jpg',
title: 'B',
text: 'textb'
}
{
}
}
}
</script>
This is what I need to archive:
As soon as an image changes to the next one - the a text outside of the scope should change too. I tried to check the value of the item array outside the scope but it didn't work:
<h1 v-if="(item,i) === 1">Lion</h1> <h1 v-if="(item,i) === 2">Ape</h1>
How to access the value of the current carousel item outside of the scope?
You need to add v-model on v-carousel component:
<v-carousel v-model="carousel">
<v-carousel-item
v-for="(item,i) in items"
v-bind:src="item.src"
:key="i"
></v-carousel-item>
</v-carousel>
//then set title like this:
<h1>TITLE: {{ items[carousel].title }}</h1>
And add carousel variable to data
data () {
return {
carousel: 0, //like this
items: [
...