I've read quite a few docs and tutorials but I'm still not understanding what I'm doing wrong. I've tried rebuilding the simple component several times with no luck. I'm getting the following errors:
[Vue warn]: Error in data(): "ReferenceError: products is not defined"
found in
---> <Products> at resources/assets/js/components/Products.vue
<Root>
app.js:19613:7
ReferenceError: products is not defined
[Vue warn]: Property or method "data" 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.
found in
---> <Products> at resources/assets/js/components/Products.vue
<Root>
app.js:19613:7
[Vue warn]: Error in render: "TypeError: _vm.data is undefined"
found in
---> <Products> at resources/assets/js/components/Products.vue
<Root>
Here's my app.js file:
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('products', require('./components/Products.vue'));
const app = new Vue({
el: '.main-container',
data: {
products: []
}
});
and here's the Products.vue:
<template>
<div class="row">
<div class="columns large-3 medium-6" v-for="product in data.products" :key="product.product_key">
<div class="card">
<div class="card-divider">
#{{ product.title }}
</div>
<a :href="product.product_key" target="_blank"><img :src="product.image"></a>
<div class="card-section">
<p>#{{ product.product_description }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return products
},
mounted () {
this.getProducts();
},
methods: {
getProducts() {
axios.get('/products/').then((response) => {
this.products = response.data.results;
}).catch( error => { console.log(error); });
}
}
}
</script>
I know I've probably confused the .vue file with some of the properties I was attempting to set in the app.js-- can anyone set me straight on how the data should be accessed?
Change Products.vue from:
data: function () {
return products
},
To
data() {
return {
products: [],
}
}
There is a difference when it comes to setting the data() object when it's either in the Vue Root object or when it's in components.
In your case, you shouldn't put return products in app.js. The products object will exist in that Products.vue component and can be accessed by this.products.
Related
I made a component for a drag and drop element. What I want to do is use my .vue template file, instead of writing all the HTML on the single line after the template. (When I do write it in the component itself, it works fine, but just doesn't look good.) I need it to be a component because it can be that there is more than one component on one page.
Here is my .js file:
import Vue from 'vue';
import SingleFileUpload from './single-file-upload.vue'
Vue.component ('test', SingleFileUpload, {
delimiters: ['${', '}'], // Avoid Twig conflicts
data() {
return filelist // Store our uploaded files
},
methods: {
onChange() {
this.filelist = [...this.$refs.file.files];
},
remove(i) {
this.filelist = [];
},
dragover(event) {
event.preventDefault();
// Add some visual fluff to show the user can drop its files
if (!event.currentTarget.classList.contains('highlight')) {
event.currentTarget.classList.remove('bg-haze');
event.currentTarget.classList.add('highlight');
}
},
dragleave(event) {
// Clean up
event.currentTarget.classList.add('bg-haze');
event.currentTarget.classList.remove('highlight');
},
drop(event) {
event.preventDefault();
this.$refs.file.files = event.dataTransfer.files;
this.onChange(); // Trigger the onChange event manually
// Clean up
event.currentTarget.classList.add('bg-haze');
event.currentTarget.classList.remove('highlight');
}
},
template: SingleFileUpload
})
new Vue({
el: '#App',
})
This is my .vue file
<template>
<div class="file-upload__wrapper">
<div
class="file-upload__drop-area"
#dragover="dragover"
#dragleave="dragleave"
#drop="drop"
>
<input
type="file"
name="file-upload-bank"
id="file-upload-bank"
class="d-none"
#change="onChange"
ref="file"
accept=".pdf,.jpg,.jpeg,.png"
/>
<ul v-if="this.filelist.length" v-cloak>
<li v-for="file in filelist" v-bind:key="file.id">
<span>Bestand: ${ file.name }</span
><button
type="button"
#click="remove(filelist.indexOf(file))"
title="Verwijder bestand"
>
<i class="icon-cross"></i>
</button>
</li>
</ul>
<label for="file-upload-bank" class="block cursor-pointer" v-else>
<div>Kies een bestand of drag and drop het bestand</div>
</label>
</div>
</div>
</template>
In my page I use to place it.
Unfortunately, I get this error (which I get for all methods I use in the template).
vue.common.dev.js:630 [Vue warn]: Property or method "dragover" 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.
found in
---> <Test> at assets/scripts/vue-components/forms/single-file-upload.vue
<Root>
Who knows what I do wrong and how to fix it?
This question already has answers here:
[Vue warn]: Property or method is not defined on the instance but referenced during render
(27 answers)
Closed 2 years ago.
I'm not sure what happened during the updates that I've made but I've now received an error that I did not get previously. I've got a section of code in my pages folder under index.vue:
<section class="intro">
<h1>heading 1</h1>
<h2>heading 2</h2>
<nuxt-link to="/admin">
<AppButton
type="submit"
#click="onSave">
Start Here
</AppButton>
</nuxt-link>
<nuxt-link to="/connect">
<AppButton
type="submit"
#click="onSave">
Contact Us
</AppButton>
</nuxt-link>
</section>
<script>
import AppButton from '#/components/UI/AppButton'
export default {
components: {
AppButton
}
}
</script>
And this section of code under the default.vue file in layouts:
<template>
<div>
<TheHeader #sidenavToggle="displaySidenav = !displaySideNav" />
<Nuxt />
<TheFooter />
</div>
</template>
<script>
import TheHeader from '#/components/Navigation/TheHeader'
import TheFooter from '#/components/Navigation/TheFooter'
export default {
components: {
TheHeader,
TheFooter
}
}
</script>
I also have this code under the AppButton.vue file located under components:
<template>
<button
class="button"
:class="btnStyle"
v-bind="$attrs"
v-on="$listeners">
<slot />
</button>
</template>
<script>
export default {
name: 'AppButton',
props: {
btnStyle: {
type: String,
default: ''
}
}
}
</script>
The error states: "[Vue warn]: Property or method "onSave" 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." I have not had this issue before and do not know why it is appearing now. Has something changed in Vue and nuxt.js?
You should define onSave method.
export default {
components: {
AppButton
},
methods: {
onSave() {
// do something
}
}
}
How do I access a child component data from the parent component?
I have a parent component "MissionPlanner" which i want to access the child component "ChosenHeroes" array called "chosenHeroes". I want to ultimately render a div if an element is in that array - and be able to update the array in the parent
I am not sure if i should be using emit() and how exactly to use it. I tried making a custom event "addHero" and pass that back. But i am getting errors
ChosenHeroes.vue
<template>
<div>
<select v-model="chosenHero">
<!-- placeholder value -->
<option :value="null">Select a hero</option>
<!-- available heroes -->
<option v-for="hero in heroes"
:key="hero.name"
:value="hero.name">
{{ hero.name }}
</option>
</select>
<span> </span>
<button #click="addHero(chosenHero)"
:disabled="chosenHero === null || chosenHeroes.length >= 3">Add Hero</button>
<br>
<h3>Chosen Heroes</h3>
<div class="chosen-heroes">
<div v-for="(hero, i) in chosenHeroes"
:key="hero.name">
<strong>Slot {{ i + 1 }}:</strong>
<Hero :hero="hero"
#removeHero="removeHero(hero)" />
</div>
</div>
</div>
</template>
<script>
import Hero from "./Hero";
export default {
components: {
Hero
},
props: {
"heroes": Array
},
data() {
return {
chosenHero: null,
chosenHeroes: []
};
},
methods: {
addHero(name) {
if(this.chosenHeroes.length < 3) {
this.chosenHeroes.push({ name });
this.chosenHero = null;
}
this.$emit("add-hero",this.chosenHeroes);
},
removeHero(hero) {
this.chosenHeroes = this.chosenHeroes.filter(h => h.name != hero.name);
}
}
};
</script>
HeroPlanner.vue
<template>
<div>
<!-- justice leage application begins here -->
<h1 id="jl">Justice League Mission Planner</h1>
<ul class="roster">
<h3>Roster:</h3>
<li v-for="hero in heroes"
:key="hero.name">
<!-- to do: conditionally display this span -->
<span v-if="isInList(hero.name)">✔ </span>
<span>{{ hero.name }} </span>
<span class="edit"
#click="editHero(hero)">edit</span>
</li>
<br>
<input type="text"
placeholder="new name"
v-model="newName"
v-if="isEdit"
#keyup.enter="changeName"
#blur="clear">
<br>
<span v-if="isEdit">enter to submit, click outside the box to cancel</span>
</ul>
<chosen-heroes :heroes="heroes" :chosenHeroes="chosenHeroes" #add-hero="addHero" />
</div>
</template>
<script>
import ChosenHeroes from "./components/ChosenHeroes.vue";
export default {
components: {
"chosen-heroes" : ChosenHeroes
},
data() {
return {
heroes: [
{ name: "Superman" },
{ name: "Batman" },
{ name: "Aquaman" },
{ name: "Wonder Woman" },
{ name: "Green Lantern" },
{ name: "Martian Manhunter" },
{ name: "Flash" }
],
newName: "",
isEdit: false,
heroToModify: null,
chosenHeroes: ChosenHeroes.data
};
},
methods: {
...isInList(heroName) {
return this.chosenHeroes.map(heroObject => heroObject.name).includes(heroName);
}
And here are the errors I got when i ran it:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in render: "TypeError: this.chosenHeroes.map is not a function"
found in
---> <MissionPlanner> at src/MissionPlanner.vue
<App> at src/App.vue
<Root>
warn # vue.runtime.esm.js?2b0e:619
vue.runtime.esm.js?2b0e:1888 TypeError: this.chosenHeroes.map is not a function
at VueComponent.isInList (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/MissionPlanner.vue?vue&type=script&lang=js&:78)
at eval (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"aeb9565a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/MissionPlanner.vue?vue&type=template&id=e2c8c042&scoped=true& (app.js:946), <anonymous>:21:19)
at Proxy.renderList (vue.runtime.esm.js?2b0e:2630)
at Proxy.render (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"aeb9565a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/MissionPlanner.vue?vue&type=template&id=e2c8c042&scoped=true& (app.js:946), <anonymous>:19:15)
at VueComponent.Vue._render (vue.runtime.esm.js?2b0e:3548)
at VueComponent.updateComponent (vue.runtime.esm.js?2b0e:4066)
at Watcher.get (vue.runtime.esm.js?2b0e:4479)
at new Watcher (vue.runtime.esm.js?2b0e:4468)
at mountComponent (vue.runtime.esm.js?2b0e:4073)
at VueComponent.Vue.$mount (vue.runtime.esm.js?2b0e:8415)
I went through this article here on the emit() and how to emit data from child components to the parent components but I am not sure I used it properly
Change #add-hero="addHero" to #add-hero="anyMethodName"
and create a method:
anyMethodName(value) {
//do what you want with the chosenHeroes
}
value is the chosenHeroes that was passed through from the child component.
See link for example: https://forum.vuejs.org/t/passing-data-back-to-parent/1201/2
If you want to pass data from child to parent you can pass a parent's method as a prop to the child:
PARENT TEMPLATE SECTION
<child-component :dataHandler="dataHandler">
</child-component>
PARENT METHOD IN SCRIPT METHODS SECTION
dataHandler (input) {
// handle your new data in parent component
}
CHILD SCRIPT PROPS SECTION
props: ["dataHandler"]
register your prop. You can use dataHandler in child as normal method and pass there new data as argument - the method will be executed in parent, but with the data you provided as argument in the child.
The error that you are getting suggests that chosenHeroes is not an array (maybe it's undefined?).
The $emit will work when it is called, and in parent, it will be same as events (firing only when events are happened). In this case, you need the data from the child always render a div in the parent (if what I understand is correct).
It is better to use the Vuex store for your purpose. You can sync the data into the store from the child component. Since the store data is global, it is accessible from all components.
I am trying to update the props data sent to a component on a button click to a single component in vue.
Button click triggers an action loads the data from a config. But this throws the error and the error message was not clear. Find the error here https://imgur.com/a/0psUWKr
If I pass the data directly without the button actions, it works fine.
My Main component
<template>
<div>
<MyList v-if="listItems" :elements="listItems"/>
<button #click="showSlider">Show Slider</button>
</div>
</template>
<script>
// imports the components and config files
export default {
name: "ListView",
data() {
return {
listItems: []
};
},
components: {
MyList
},
methods: {
showSlider: function() {
this.listItems.push(configs['elements'])
},
</script>
NOTE: If i provide the data to listItems by default it works
And MyList file
<template>
<ul>
<li v-for="each in elements" :key="each.id">
{{each.name}}
</li>
</ul>
<template>
<script>
// imports the components and config files
export default {
name: "MyList",
props: {
elements: {
type: Array
}
}
</script>
It should work, in general. But in my case, the issue is with push in the following function.
showSlider: function() {
this.listItems.push(configs['elements'])
},
Storing the same from Vuex and using dispatcher to update the same works like a charm. And I use computed property to load the state from the vueX and pass it to MyList
I am using Laravel and vue-router.
<template>
<div class="content__inner">
<div class="forums">
<!-- Heading -->
<div class="forums__heading" :style="'border-bottom:2px solid #' + board.category.color">
<div class="lg-8 md-8 sm-12 column column__first">
<h2 class="forums__heading__title">{{ board.title }}</h2>
</div>
<div class="lg-1 md-1 sm-1 dtop column text-center">
<strong>Replies</strong>
</div>
<div class="lg-3 md-3 sm-4 column text-right">
<strong>Latest Reply</strong>
</div>
<div class="clearfix"></div>
</div>
<!-- Content -->
<div class="forums__content">
{{ board.category }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
board: [],
}
},
created() {
this.fetch_board(this.$route.params.slug);
},
methods: {
/**
* Fetch the board.
*
* #param string slug The slug for the board.
*/
fetch_board(slug)
{
this.$http.get('/api/forums/board/' + slug).then((response) => {
this.board = response.data;
});
},
}
};
</script>
The 'fetch_board' function returns an object like the following:
board:Object {
id:5,
title:"Game Discussion",
slug:"5-game-discussion",
description:"General talk about the game.",
restriction:null,
category_id:2,
category:Object {
id:2
title:"Community",
color:"2ECC71",
created_at:"2017-05-02 07:30:25",
updated_at:"2017-05-02 07:30:25",
}
created_at:"2017-05-02 07:30:25",
updated_at:"2017-05-02 07:30:25",
}
When I access the {{ board.category }} it displays the object correctly; but when I access {{ board.category.title }} it displays the title, but ALSO gives a TypeError.
Why I am getting this error if the data is being loaded correctly?
How can I avoid/fix this error?
You are seeing this error because you are initializing "board" to an empty array. The component tries to evaluate "board.category.title" when it binds the reactivity just prior to the created() hook.
With board set as an empty array, step by step the evaluation might look like this:
const board = [];
const category = board.category; // undefined
const title = category.title; // TypeError, because category is undefined
You should stop seeing this error if you initialize your data like so:
data() {
return {
board: {
category: {
title: ''
}
}
}
}
Here is the Vue lifecycle diagram which illustrates when the created() event is fired
This error is explained in the official Vue documentation:
Since Vue doesn’t allow dynamically adding root-level reactive properties, you have to initialize Vue instances by declaring all root-level reactive data properties upfront, even with an empty value:
var vm = new Vue({
data: {
// declare message with an empty value
message: ''
},
template: '<div>{{ message }}</div>'
})
// set `message` later
vm.message = 'Hello!'
If you don’t declare message in the data option, Vue will warn you that the render function is trying to access a property that doesn’t exist.