I am not sure why I get a Vue warning when accessing nested object.
{{ user.area.name }}
[Vue warn]: Error in render: "TypeError: Cannot read property 'name' of undefined"
TypeError: Cannot read property 'name' of undefined
Just accessing the object has no warning.
{{ user.name }}
Any advice?
Totally guessing here but lets see if I'm right...
Say you've got something like this in your component / Vue instance data initialiser...
data () {
return {
user: {}
}
}
and you then populate that object asynchronously, eg
mounted () {
setTimeout(() => { // setTimeout is just an example
this.user = {
...this.user,
area: {
name: 'foo'
}
}
}, 1000)
}
If your template has
{{ user.area.name }}
when it initially renders before the asynchronous task has completed, you will be attempting to access the name property of area which is undefined.
Example ~ http://jsfiddle.net/tL1xbmoj/
Your options are...
Initialise your data with a structure that won't cause errors
data () {
return {
user: {
area: {
name: null
}
}
}
}
Example ~ http://jsfiddle.net/tL1xbmoj/1/
Use conditional rendering to prevent the error
<span v-if="user.area">{{ user.area.name }}</span>
Example ~ http://jsfiddle.net/tL1xbmoj/2/
Your user doesn't contain an area, so when you try to read that, it's undefined. You're not allowed to use the . operator on things that are undefined, so when you do .name on that, you get the error that you got.
You could also use a ternary operator {{ user.area ? user.area.name : "" }}
Related
I want to implement a props checker function, which is used to handle the situation with unexpected props problem. Below is an example,
<GrandFater :scope="scope" :id="id" v-bind="$attrs" #error="error" v-on="$listeners">
<Father>
<GrandSon />
<Father/>
<GrandFather />
Then, in the component GrandSon, I will pass these two props to a function to make further process, let's say authorize. Now, what I want to do is to implement a props checker function to check whether the two props is valid. For example, the prop scope is a string, but it cannot be missed and it also cannot be an empty string.
It cannot be missed means it must be declared in <GrandFather> component, such as
<GrandFater :id="id" v-bind="$attrs" #error="error" v-on="$listeners">
<Father>
<GrandSon />
<Father/>
<GrandFather />
In above example, the prop scope missed and I want to capture the error in my function.
class GrandFather extends Vue {
#Prop({ type: String }) id!: string;
#Prop({ type: String }) scope!: string;
mounted(){
if(propChecker(this)) return;
authorize(this);
}
}
In another JS file, I implement the propChecker like this:
export function parameterChecker(vm) {
let error: String = "";
switch (vm) {
case !vm.id:
error = "Parameter Id missing.";
break;
case !vm.id.trim():
error = "Parameter Id cannot be empty.";
break;
case !vm.scope:
error = "Parameter scope missing.";
break;
case !vm.scope.trim():
error = "Parameter scope cannot be empty.";
break;
default:
return false;
}
vm.$emit("error", error);
return true;
}
But now the question is if I miss a prop, such as scope, it will report an error:
[Vue warn]: Error in mounted hook: "TypeError: Cannot read properties of undefined (reading 'trim')".
Now the question is how could I implement my goal, and why this error will happen?
I would recommend using the prop validation (see the website here).
You can pass the props of your component using the props object which also contains object. Then you can either specify default parameters (if those aren't pass) or even use the required prop (which will throw an error if the props isn't pass).
All props are optional by default, unless required: true is specified.
Example :
props: {
myFirstProp: {
type: Object,
default: () => {scope: null},
required: true,
},
mySecondProp: {
type: String,
default: 'defaultValue'
},
myThirdProp: {
type: Boolean,
default: true
},
}
I'm new to vue/promise and I am struggling to understand why when I try to display the result of a promise I end up with the expected data but when I try to find out its length, it says undefined
When I try to display the alerts from displayAlerts() , I can see a list of alerts, 2 in total. However in computed within the title function ${this.displayAlerts.length} appears as undefined, I was expecting to see 2.
Does it have something to do with displayAlerts() resulting in a promise? How do I fix the code such that I get 2 instead of undefined?
The code is below:
<template>
<div>
{{displayAlerts}}
<li v-for="alert in alerts" class="alert">
{{alert['name']}}
</li>
</div>
</template>
export default {
data () {
return {
alerts: null,
alert: new Alert(),
updatedAlert: new Alert(),
deletedAlert: new Alert(),
};
},
computed: {
...mapGetters("authentication",['token']),
...mapGetters("user",['profile']),
displayAlerts() {
return getUserAlert({
user_id: this.profile.user_id,
token: this.token
}).then(response => (this.alerts = response.data)).catch(
error => console.log(error)
)
},
title () {
return `My Alerts (${this.displayAlerts.length})`
},
test2() {
return [1,2,3]
},
}
};
</script>
Something like this should work:
<template>
<div v-if="alerts">
<h4>{{ title }}</h4>
<li v-for="alert in alerts" class="alert">
{{ alert.name }}
</li>
</div>
</template>
export default {
data () {
return {
alerts: null
}
},
computed: {
...mapGetters('authentication', ['token']),
...mapGetters('user', ['profile']),
title () {
// Handle the null case
const alerts = this.alerts || []
return `My Alerts (${alerts.length})`
}
},
methods: {
// This needs to be in the methods, not a computed property
displayAlerts () {
return getUserAlert({
user_id: this.profile.user_id,
token: this.token
}).then(response => (this.alerts = response.data)).catch(
error => console.log(error)
)
}
},
// Initiate loading in a hook, not via the template
created () {
this.displayAlerts()
}
}
</script>
Notes:
Computed properties shouldn't have side-effects. Anything asynchronous falls into that category. I've moved displayAlerts to a method instead.
Templates shouldn't have side-effects. The call to load the data should be in a hook such as created or mounted instead.
title needs to access this.alerts rather than trying to manipulate the promise.
While the data is loading the value of alerts will be null. You need to handle that in some way. I've included a v-if in the template and some extra handling in title. You may choose to handle it differently.
I've added title to the template but that's just for demonstration purposes. You can, of course, do whatever you want with it.
I've assumed that your original displayAlerts function was working correctly and successfully populates alerts. You may want to rename it to something more appropriate, like loadAlerts.
I have a error at my vue project.I use computed to return a object.
computed: {
getOpLog() {
if (this.product_menu) {
this.product_menu.forEach(opLogItem => {
if(opLogItem.id === 'menu_item_oplog') {
return opLogItem;
}
});
}
}
},
and my debugger shows that I have the right return object.
But when i run it in brower, it just not work.
[Vue warn]: Error in render: "TypeError: Cannot read property 'hidden' of undefined"
Here is my html.
<el-menu-item v-if="getOpLog.hidden" :id="getOpLog.id">
...
</el-menu-item>
But when I use this
getOpLog() {
if (this.product_menu) {
return this.product_menu[8]
}
}
It work.I want to know how can i fix this.Thx
When your if condition inside the getter is false i.e. if (this.product_menu), then getter will return undefined object. And thus, Vue.js complains.
As a simple remedy, add an extra check in v-if like:
<el-menu-item v-if="getOpLog && getOpLog.hidden" :id="getOpLog.id">
...
</el-menu-item>
Further, using return inside the forEach function of an array doesn't really cause a return from actual getter function. It is just returning from the inner arrow function. You will need to modify your code using Array.prototype.find method:
computed: {
getOpLog() {
if (this.product_menu) {
const item = this.product_menu.find(opLogItem => {
return opLogItem.id === 'menu_item_oplog';
});
return item;
}
}
};
But, you still should have v-if check for getOpLog in case find method returns undefined value.
I need to display value for month april. Code:
{{my_dates['2018-04-23']}}
displays:
{
"april":0,
"may":0,
"june":0,
"july":0,
"august":0,
"september":0,
"october":0,
"income_trips":"0.00",
"med_services":"0.00",
"food_services":"0.00",
"accom_services":"0.00",
"collegium":"0.00",
"parking":"0.00",
"wash":"0.00",
"other":"0.00",
"balance":"0.00",
"employees":0,
"season_employees":0,
"complaints":0,
"reviews":0
}
I tried:
{{my_dates['2018-04-23']['april']}}
it's display 0, but I am getting error:
[Vue warn]: Error in render: "TypeError: Cannot read property 'april' of undefined"
The follow json should be in my my_dates http://vpaste.net/3A0Th
Code of app:
var app = new Vue({
el: '#app',
data: {
my_dates: {},
},
methods:
{
getTableData: function()
{
// GET /someUrl
this.$http.get('http://127.0.0.1:8000/ponizovka_get_data').then(response => {
// get body data
this.my_dates = response.body;
}, response => {
// error callback
});
}
},
created: function(){
this.getTableData()
}
})
{{ my_dates['2018-04-23']['april'] }}
You're right. This is the correct way to access this property, but the error could be related to this value been display before assignment or async operation. You can fix by checking on v-if, using short-circuit, using a computed property (#EmileBergeron suggestion) or even a method.
Fix by checking on v-if
<div v-if="my_dates && my_dates['2018-04-23']">
{{ my_dates['2018-04-23']['april'] }}
</div>
Fix using short-circuit
{{ my_dates && my_dates['2018-04-23'] && my_dates['2018-04-23']['april'] }}
Fix with a computed property (#EmileBergeron suggestion)
<span>{{ april }}</span>
computed: {
april () {
if (!this.my_dates || !this.my_dates['2018-04-23'])
return null;
return this.my_dates['2018-04-23']['april'];
}
}
Fix using a method
{{ getMyDate('2018-04-23', 'april') }}
methods: {
getMyDate (date, month) {
if (!this.my_dates || !this.my_dates[date])
return null;
return this.my_dates[date][month];
}
}
There are other approaches like optional-chaining purpose or idx.
https://github.com/tc39/proposal-optional-chaining
https://github.com/facebookincubator/idx
It is because, may be you are getting the data from an async source(fetching from API or something). Unless that data is available vue would try to find my_dates['2018-04-23'] which will be undefined so you are getting that error.
Wait until you have fetched you data and then display it.
You can use v-if for that:
<div v-if="my_dates">{{my_dates['2018-04-23']['april']}}</div>
You might still get that error, even after using v-if, so try using computed for getting the values after that.
I hope it helps.
I have a component that renders it's template with for example a span that looks like:
<span>{{item.name}}</span>
the item is passed in as a property:
props: {
item: null
},
when the component is first mounted item is null, so when the render happens I get this in the logs:
[Vue warn]: Error in render: "TypeError: Cannot read property 'name'
of null"
If I create a computed property like this:
computed: {
name() {
if (this.item != null) {
return this.item.name
}
}
},
And then put name instead of item.name in the render it works fine, but is there a way to tell Vue to stop complaining about subproperties being null since it doesn't seem to care about root properties being null?
Maybe #LinusBorg knows?
That's not Vue-specific. Vue is simply warning you that you have a javascript TypeError. The error is being caused because you can't access a property of a null value in javascript.
I would just set the default value of your item prop to an empty object instead of null:
props: {
item: { type: Object, default: () => ({}) }
}