Vuejs undefined property error but it is defined - javascript

I have a simple Vue component that simply list server connection data:
<template>
<div class="container">
<div class="row">
<div class="col-xs-12">
<div class="page-header">
<h2 class="title">Data</h2>
</div>
<br>
</div>
<div class="col-xs-12">
<table class="table">
<tr>
<td>Server</td>
<td><strong>{{config.servers}}</strong></td>
</tr>
<tr>
<td>Port</td>
<td><strong>{{config.port}}</strong></td>
</tr>
<tr>
<td>Description</td>
<td><strong>{{config.description}}</strong></td>
</tr>
<tr>
<td>Protocol</td>
<td :class="{'text-success': isHttps}">
<i v-if="isHttps" class="fa fa-lock"></i>
<strong>{{config.scheme}}</strong>
</td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'Application',
data () {
return {
config: {
scheme: '',
servers: '',
port: '',
description: ''
}
}
},
computed: {
...mapState(['server']),
isHttps: () => this.config.scheme === 'https'
},
mounted () {
const matched = this.server.match(/(https?):\/\/(.+):(\d+)/)
this.config = {
scheme: matched[1],
servers: matched[2],
port: matched[3],
description: window.location.hostname.split('.')[0] || 'Server'
}
}
}
</script>
The server from Vuex is already defined and done when this component is mounted, and if I try to console.log(this.server), it shows the correct URL. The thing is, my computed property isHttps throws the following error:
[Vue warn]: Error in render function: "TypeError: Cannot read property 'scheme' of undefined"
found in
---> <Application> at src/pages/Aplicativo.vue
<App> at src/App.vue
<Root>
I've already tried to change config to something else, like configuration or details, and even changed mounted to created, but the error keeps popping up and my template is not rendered at all.
Firstly I began making config a computed property, but the error was already making its way to my console. By the way, using store as a computed property like this also throws an error saying my $store is undefined:
server: () => this.$store.state.server
What can I do?

You are using an arrow function for your isHttps computed. In that context, this refers to window not the Vue instance, so you will get a cannot read property of undefined message, the correct ES2015 syntax is:
isHttps() {
return this.config.scheme === 'https'
}
That is also the same problem with server: () => this.$store.state.server which should be:
server() {
return this.$store.state.server
}

Related

[Vue warn]: Property or method "list" is not defined on the instance but referenced during render. Make sure that this property is reactive [...]

I'm browsing characters of the Rick & Morty series app, using vue.js and I'm new to vue.js.
but I'm getting below mentioned error, please help me solve this
Error1 : [Vue warn]: Property or method "list" 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.
// App.vue file //
<template>
<div id="app">
<Nav />
<CharactersList />
</div>
</template>
<script>
import Nav from './components/Nav.vue'
import CharactersList from './components/CharactersList'
export default {
name: 'App',
components: {
Nav,
CharactersList
}
}
</script>
// CharactersList.vue file //
<template>
<div>
<h1>Rick and Morty characters</h1>
<table border="1px">
<tr>
<td>Character ID</td>
<td>Name</td>
<td>Species</td>
<td>Add to fav</td>
</tr>
<tr v-for="item in list" v-bind:key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}}}</td>
<td>{{item.species}}</td>
<button>Add to fav</button>
</tr>
</table>
</div>
</template>
<script>
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
export default {
name: 'CharactersList',
data: function () {
return {
list: undefined
}
},
mounted () {
Vue.axios.get('https://rickandmortyapi.com/api/character/')
.then((resp) => {
debugger
this.list = resp.data.results
})
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
First of all, you don't need to import there Vue and VueAxios, as they are no use there. And second, you need to modify your list variable value from undefined to []
I have changed your CharactersList component code, you can copy and paste all codes, and it will work as the way you want:
CharactersList.vue
<template>
<div>
<h1>Rick and Morty characters</h1>
<table border="1px">
<tr>
<td>Character ID</td>
<td>Name</td>
<td>Species</td>
<td>Add to fav</td>
</tr>
<tr v-for="item in list" v-bind:key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.species}}</td>
<button>Add to fav</button>
</tr>
</table>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'CharactersList',
data: function () {
return {
list: []
}
},
mounted () {
axios.get('https://rickandmortyapi.com/api/character/')
.then((resp) => {
this.list = resp.data.results
})
}
}
</script>

Accessing child component data vuejs

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.

Why variable is not defined in created() method

I've got code like this. I would like to split deals array into 3 separate arrays: dealsCol1, dealsCol2, dealsCol2 and I would like to have it done after the object is created. I'm a beginner in JavaScript. I previously programmed in C++ and the thing that I guess I need is something like a constructor. I've found out that created() function works as a constructor (it's called on object creation). I put the part of the code that splits the array in that function, but I get an error:
vue.esm.js:591 [Vue warn]: Error in created hook: "ReferenceError: deals is not defined"
I have no idea why it's not defined there, because I guess that it should be. Could you give me some hints how can I solve the problem?
<script>
export default {
props: {
deals: Array
},
data() {
return {
dealsCol1: [],
dealsCol2: [],
dealsCol3: []
};
},
created() { // why it doesn't work??
this.dealsCol1 = this.deals.slice(0, this.deals.length/3),
this.dealsCol2 = this.deals.slice(this.deals.length/3, 2*this.deals.length/3),
this.dealsCol3 = this.deals.slice(2*this.deals.length/3, this.deals.length-1)
}
};
</script>
EDIT:
If I use the trick with computed() from one of the answers everything works good. But I'm wondering why the deals are visible in every other method beyond constructed(). It is also visible in the template part. Why is that?
The parent component code looks like this:
<template>
<div>
<editDealsModal ref="editDealsModal" :deals="deals" #editDeals="editDeals" />
<table class="table table-sm color mb-2">
<caption class="caption table-caption">Users</caption>
<thead>
<th class="text-left text-nowrap">Deals</th>
<th></th>
<th></th>
<th></th>
<th></th>
</thead>
<tbody>
<tr v-for="user in users" :key=user.Id v-bind:class="[{ disabled: user.IsEnabled == false }]">
<td class="text-left align-middle">{{user.Username}}</td>
<td class="text-left align-middle">
<div v-for="role in user.Roles" :key=role>{{role}}</div>
</td>
<td class="text-left align-middle">
<div v-for="deal in user.Deals" :key=deal>{{deal}}</div>
</td>
<td class="align-middle">
<b-btn variant="link" #click="showEditDealsModal(user)" v-bind:disabled="!user.IsEnabled">Edit deals</b-btn>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import service from '../../services/HttpRequestService'
import EditDealsModal from './EditDealsModal'
export default {
props: {
users: Array,
deals: Array
},
methods: {
showEditDealsModal(user) {
this.$refs.editDealsModal.showModal(user, user.Deals || [])
},
async editDeals(user, data) {
try {
await service.editDeals(user.Id, data);
this.$emit("userEdited", { type: "success", msg: "Updated deals for " + user.Username })
} catch (err) {
this.$emit("userEdited", { type: "danger", msg: "Failed to edit deals for " + user.Username })
}
},
},
components: {
EditDealsModal
}
}
</script>
enter code here
Try to defined a default value for your myData array, like this
props: {
myData: {
default: [],
type: Array,
},
},
By the way props are used to pass data from parent to child, I don't think this is the best way to do this.
myDataCol1,2,3 should be computed properties :
props: {
myData: {
default: [],
type: Array,
},
},
computed: {
myDataCol1(){
return this.myData.slice(0, this.myData.length/3)
}
.
.
.
}

Laravel 5.5 and Vue.js

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.

Error in render function: "TypeError: Cannot read property of undefined" in Vue

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.

Categories