I got "artists prop is Undefined" error in the console
Structure of components:
Discover > ArtistSlider > ArtistItem
Discover passes the prop artists to ArtistSlider and it does it only when all the data is received from API. But ArtistSlider throws the error that prop artists is undefined for some reason. At the same time, ArtistItem doesn't throw an error despite it is the child of ArtistSlider and it receives the prop artist from ArtistSlider.
I read about vue lifecycle but still cannot get why it happens. Especially in the middle of the ​component tree.
Discover:
<template>
<div>
<h1>Discover page</h1>
<artist-slider :artists="collection.new_artists"/>
</div>
</template>
<script>
import store from "#/store";
import Page from "#/mixins/Page";
import ArtistSlider from "#/components/ArtistSlider";
export default {
extends: Page,
async beforeRouteEnter(to, from, next) {
const collection = await store.dispatch("fetchUrl", {
url: "discover",
params: { location: "uae" }
});
console.log('Received collection from API', collection)
next(vm => (vm.collection = collection));
},
components: {
ArtistSlider,
},
created() {
console.log('Discover Created', this.collection)
}
};
</script>
ArtistSlider:
<template>
<section>
<h2>New Artists</h2>
<ul>
<artist-item
v-for="artist in artists"
:key="artist.slug"
:artist="artist"
/>
</ul>
</section>
</template>
<script>
import ArtistItem from './ArtistItem'
export default {
name: "AppArtistSlider",
props: {
artists: {
required: true,
type: Array
}
},
components: {
ArtistItem
},
created() {
console.log('Slider Created', this.artists)
}
};
</script>
<style lang="scss" scoped>
</style>
ArtistItem:
<template>
<li>
<app-image :src="artist.avatar.small" :alt="artist.full_name" />
<h3>{{artist.full_name}}</h3>
<p>{{artist.art_type.name}}</p>
</li>
</template>
<script>
import AppImage from './AppImage.vue'
export default {
name: 'ArtistItem',
props: {
artist: {
required: true,
type: Object
},
},
components: {
AppImage
},
created() {
console.log('Item Created', this.artist)
}
}
</script>
Will apreciate any help. Thanks!
Related
I'm having trouble getting a route param to pass directly into a component. I followed multiple sets of directions in the docs (including using the Composition API as in the following code), but I'm still getting undefined when the CourseModule.vue first renders.
Route Definition
{
path: '/module/:id',
name: 'Course Module',
props: true,
component: () => import('../views/CourseModule.vue'),
},
CourseModule.vue:
<template>
<div class="AppHome">
<CustomerItem />
<CourseModuleItem :coursemodule-id="this.CoursemoduleId"/>
</div>
</template>
<script>
import { useRoute } from 'vue-router';
import CustomerItem from '../components/customers/customer-item.vue';
import CourseModuleItem from '../components/coursemodules/coursemodule-item.vue';
export default {
setup() {
const route = useRoute();
alert(`CourseModule.vue setup: ${route.params.id}`);
return {
CoursemoduleId: route.params.id,
};
},
components: {
CustomerItem,
CourseModuleItem,
},
mounted() {
alert(`CourseModule.vue mounted: ${this.CoursemoduleId}`);
},
};
</script>
coursemodule-item.vue:
<template>
<div id="module">
<div v-if="module.data">
<h2>Course: {{module.data.ModuleName}}</h2>
</div>
<div v-else-if="module.error" class="alert alert-danger">
{{module.error}}
</div>
<Loader v-else-if="module.loading" />
</div>
</template>
<script>
import Loader from '../APILoader.vue';
export default {
props: {
CoursemoduleId: String,
},
components: {
Loader,
},
computed: {
module() {
return this.$store.getters.getModuleById(this.CoursemoduleId);
},
},
mounted() {
alert(`coursemodule-item.vue: ${this.CoursemoduleId}`);
this.$store.dispatch('setModule', this.CoursemoduleId);
},
};
</script>
The output from my alerts are as follows:
CourseModule.vue setup: zzyClJDQ3QAKuQ2R52AC35k3Hc0yIgft
coursemodule-item.vue: undefined
CourseModule.vue mounted: zzyClJDQ3QAKuQ2R52AC35k3Hc0yIgft
As you can see, the path parameter works fine in the top level Vue, but not it's still not getting passed into the component.
your kebab-cased :coursemodule-id props that you're passing to the CourseModuleItem component becomes a camelCased coursemoduleId props
Prop Casing (camelCase vs kebab-case)
try this
// coursemodule-item.vue
...
props: {
coursemoduleId: String,
},
...
mounted() {
alert(`coursemodule-item.vue: ${this.coursemoduleId}`);
this.$store.dispatch('setModule', this.coursemoduleId);
},
I'm emitting an object via event bus to the parent and then assigning that object to my detail variable which is to be passed as a prop to my ContactDetail component, but the prop isn't getting the updated object.
ContactCard is a component imported in ContactList.
Getting the following error:
[Vue warn]: Property or method "contactDetail" 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.
ContactCard
<script>
import {bus} from "../../bus.js";
export default {
name: "ContactCard",
methods: {
showDetails: function(event) {
bus.$emit('showDetails', { pfp: this.pfp, name: this.name, address: this.address });
}
}
}
</script>
AddressBook
<template>
<div class="container">
<ContactList v-if="!showDetail"></ContactList>
<ContactDetail v-else :contactDetail="details"></ContactDetail>
</div>
</template>
<script>
import { bus } from "../bus.js";
import ContactList from "../components/addressBook/ContactList.vue";
import ContactDetail from "../components/addressBook/ContactDetail.vue";
export default {
name: "AddressBook",
components: {
ContactList,
ContactDetail
},
data() {
return {
showDetail: false,
details: {}
}
},
created() {
bus.$on('showDetails', (data) => {
console.log(data);
this.showDetail = true;
this.details = Object.assign({}, this.details, data);
console.log(this.details);
});
}
}
</script>
ContactDetail
<template>
<div class="detail-container">
{{contactDetail}}
</div>
</template>
<script>
export default {
name: "ContactDetail",
data() {``
return {
details: ''
}
},
props: {
contactDetail: {
type: Object,
required: true
}
},
watch: {
contactDetail(newVal, prevVal) {
this.details = newVal;
}
}
}
</script>
Why isn't the contactDetails prop updated in ContactDetail?
Accidentally had two script sections instead of script and style.
Before
<template>
<div class="detail-container">
{{details}}
</div>
</template>
<script>
import { bus } from "../../bus.js";
export default {
name: "ContactDetail",
data() {
return {
}
},
props: {
details: {
type: String,
required: true
}
}
}
</script>
<script>
</script>
After
<template>
<div class="detail-container">
hellow
{{details}}
</div>
</template>
<script>
import { bus } from "../../bus.js";
export default {
name: "ContactDetail",
data() {
return {
pfp: "",
name: "",
address: ""
}
},
props: {
details: {
type: String,
required: true
}
}
}
</script>
<style>
</style>
I saw many answers telling to use vue-devtools to access the Vue Object but is there a way to do it in the browser console? Like in the tutorial, we enter
> app.data.sock
in console to get the data
Let's say:
main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld />
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
HelloWorld.vue
<template>
<div class="hello">
<ul>
<li v-for="title in titles" :key="title.id">{{ title.name }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
titles: [{
id: 0,
name: "a"
},
{
id: 1,
name: "b"
}]
}
}
}
</script>
How do I access the data of 'titles' in HelloWorld.vue? In other word, how to get this.data.titles[0].name in the HelloWorld.vue in the browser console? Thank you
You can access the value of data function in created our mounted hooks of Vue lifecycle or can create a function in methods. I am calling your data in created hook
<script>
export default {
name: 'HelloWorld',
data() {
return {
titles: [{
id: 0,
name: "a"
},
{
id: 1,
name: "b"
}]
}
},
created(){
console.log(this.data.tiles)
}
}
</script>
I'm getting started with Vue, I need to create a form of tiered select fields. That is the selected option for A, uses that to call the API to get the options for B, which determines options for C.
I'm still pretty new to frontend frameworks so this might be a terrible design. However not every inclusion of A (SelectState.vue) in a view requires all the children so making them modular was my first thought.
Currently I have a top level component that displays the select options:
SelectState.vue
<template>
<div id="select-state">
<span>{{ label }}</span>
<select v-model="selectedState">
<option v-for="state in states" :key="state">
{{ state }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'select-state',
data: function () {
return {
selectedState: '',
states: ['TX']
}
},
props: ['label']
// this.states = axios.get('xxx')
}
</script>
Index.vue
<template>
<div id="form">
<v-select-state label="State"></v-select-state>
<v-select-zip label="Zip"></v-select-zip>
</div>
</template>
<script>
import SelectState from './SelectState.vue'
import SelectZip from './SelectZip.vue'
export default {
name: 'Index',
components: {
'v-select-state': SelectState,
'v-select-Zip': SelectZip
}
}
</script>
Then I have a SelectZip.vue that is identical to SelectState.vue except that it has a parameter for its axios.get('XXX', params = {'state': ???}). But I'm stuck on how to "pass" that necessary parameter.
Thanks in advance!
edit: In conjunction with #dziraf's answer my working although verbose SelectedZip.vue is as follows:
<template>
<div id="select_zip">
<span>{{ label }}</span>
<select v-model="selected_zip">
<option v-for="zip in zips" :key="zip">
{{ zip }}
</option>
</select>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'select_zip',
data: function () {
return {
zips: []
}
},
props: ['label'],
computed: {
selected_zip: {
get () { return this.$store.state.formModule.zip },
set (value) { this.$store.commit('formModule/setZips', value) }
},
selected_state: {
get () { return this.$store.state.formModule.state }
}
},
methods: {
getValidZips (state) {
axios.post('/api/v1/get_valid_zips', {
params:{'state': state }})
.then(response => {
this.zips = response.data
})
.catch(error => {
console.log(error)
})
}
},
watch: {
selected_state (value) {
this.getValidZips(value)
}
}
}
</script>
You can pass it by adding 'state' props to your select components from your main form component, but I think it isn't a good long-term solution.
Instead, consider using Vuex. An example configuration would look like this:
#/store/modules/form.js
const Form = {
namespaced: true,
state: {
state: '',
zip: ''
},
getters: {},
mutations: {
setState (state, payload) {
state.state = payload
},
setZip (state, payload) {
state.zip = payload
}
},
actions: {}
}
export default Form
#/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import Form from './modules/form'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
formModule: Form,
}
})
export default store
#/main.js
// your impots
import store from './store/index'
// your configs
new Vue({
el: '#app',
router,
store, // add store to your main Vue instance so it's accessible with this.$store
axios,
components: { App },
template: '<App/>'
});
This would be your SelectState.vue:
<template>
<div id="select-state">
<span>{{ label }}</span>
<select v-model="selectedState">
<option v-for="state in states" :key="state">
{{ state }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'select-state',
data: function () {
return {
states: ['TX']
}
},
computed: {
selectedState: {
get() { return this.$store.state.formModule.state },
set(value) { this.$store.commit('formModule/setState', value) }
}
},
props: ['label']
}
</script>
Your SelectZip.vue would be the same, except you would instead use your store's zip as your v-model.
Your store variables are accessible across your app and you can access them with computed properties in your components.
I'm working on a blog project, fully static, fetching data from the wordpress rest api.
I'm tring to display the article page when clicking on the title displayed on the index file.
I need the route to be custom depending on the slug of the post.
But I get a "This page could not be found"
The route changed event info :
Structure
pages
--| article
----| _slug.vue
--| index.vue
index.vue
<template>
<div class="container">
<h1>Blog</h1>
<ul>
<li v-for="article in posts" :key="article.id">
<nuxt-link :to="{ name: 'article-slug', params: {slug : article.slug} }">{{ article.title.rendered }}</nuxt-link>
</li>
</ul>
</div>
</template>
<script>
import axios from 'axios'
export default {
asyncData({ req, params }) {
// We can return a Promise instead of calling the callback
return axios.get('https://dev.lesdeuxvagues.com/api/wp-json/wp/v2/posts/')
.then((res) => {
return { posts: res.data.slice(0, 5) }
})
},
head: {
title: 'List of posts'
}
}
</script>
_slug.vue
<template>
<div>
<h1>
{{ title.rendered }}
</h1>
<template>
{{ content.rendered }}
</template>
<p><nuxt-link to="/">Back to home page</nuxt-link></p>
</div>
</template>
<script>
import axios from 'axios'
export default {
validate({ params }) {
return !isNaN(+params.slug)
console.log(params)
},
async asyncData({ params, error }) {
try {
const { data } = await axios.get(`https://dev.lesdeuxvagues.com/api/wp-json/wp/v2/posts/?slug=${+params.slug}`)
return data
} catch (e) {
error({ message: 'User not found', statusCode: 404 })
}
}
}
router.js
export function createRouter () {
return new Router({
mode: 'history',
base: '/',
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [
{
path: "/article/:slug?",
component: _540807ba,
name: "article-slug"
},
{
path: "/",
component: _ac3e7d78,
name: "index"
}
],
fallback: false
})
}
Thanks for your help
I get it. I was trying to check is a string was NaN.
Solved.