Getters: Property was accessed during render but is not defined on instance - javascript

I have a component in which I want to show a data defined in the state. I have created the getter:
export default createStore({
state: {
foo: true,
},
getters: {
getFoo: state => state.foo
}
}
And in the component I call it from computed:
computed: {
...mapGetters(['getFoo']),
}
I use that variable in an if in the template:
<template>
<template v-if="foo">
<span>Bar</span>
</template>
</template>
And in the console I get the following warning: [Vue warn]: Property "foo" was accessed during render but is not defined on instance.
I have tried to do it without the getters but I get the same warning:
computed: {
getFoo() {
return this.$store.getters.getFoo;
}
}

Since you're mapping the getters in your computed option, you should that getter name getFoo :
<template>
<template v-if="getFoo">
<span>Bar</span>
</template>
</template>

Related

Modifying variable in method does not update child component

I'm struggling with how Vue updates props/child components.
Suppose the following component:
<template>
<v-card>
<Modification v-model="newObject"></Modification>
<OtherComponent #close="resetObject"></OtherComponent>
</v-card>
</template>
<script>
import { MyClass } from "classes";
import Modification from "b";
import OtherComponent from "a";
export default {
name: "MyForm",
components: { OtherComponent, Modification },
props: {
existingObject: {
type: [MyClass, typeof undefined],
required: false,
default: undefined
}
},
data() {
return {
newObject: undefined
};
},
created() {
this.newObject =
this.existingObject !== undefined
? this.existingObject.clone()
: new MyClass();
},
methods: {
resetObject() {
this.newObject =
this.existingObject !== undefined
? this.existingObject.clone()
: new MyClass();
}
}
};
</script>
How MyClass is defined:
export class MyClass {
constructor({ a= null, b=null} = {}) {
this.a = a;
this.b = b;
}
toPayload(){
return { a:this.a , b:this.b };
}
clone() {
return new MyClass(this.toPayload());
}
}
This component receives an existing class instance of MyClass, clones it (clone => new MyClass(...)) and passes it to the Modification component which does some modification upon user input. So far so good, the modification works. However once the customEvent is fired and the resetObject method is called the newObject is reset but the Modification component is not updated with the now reset newObject - it still displays the old, modified values. I also checked inside the Modification component wether or not the update happens: It doesn't.
Why is this the case? Am I missing a step? Am I not aware of a Vue specific mechanism?
Note: I found this blog which provides solutions to force the Modificationcomponent to update. For now it seems to hacky for me to be "THE" solution.
Thanks in advance.
EDIT:
Adding a computed property which includes a console.log(JSON.stringify(this.newObject)) fires everytime newObject is updated.
Also adding a <span> {{ newObject.a }} </span> to the template updates evertime.
Both these tests convince me that the variable not only should be but actually IS reactive.
EDIT 2:
The Modification component consists, for now, of 2 Input components.
It looks like this.
<template>
<v-card-text>
<ModifyA v-model="object.a" #input="handleInput" />
<ModifyB v-model="object.b" #input="handleInput" />
</v-card-text>
</template>
<script>
import { MyClass } from "classes";
import ModifyA from "...";
import ModifyB from "...";
export default {
name: "ShiftFormFields",
components: { ModifyA, ModifyB },
props: {
value: {
type: MyClass,
required: true
}
},
data() {
return { object: this.value };
},
methods: {
handleInput() {
this.$emit("input", this.object);
}
}
};
</script>
If I try adding the ModifyA Input into the component instead of the Modification component like this
<template>
<v-card>
<ModifyA v-model="newObject.a"></Modification>
<OtherComponent #close="resetObject"></OtherComponent>
</v-card>
</template>
the resetObject also resets the value shown in the ModifyA component.
You didn't show how MyClass clones your object.
I'm guessing something in there isn't reactive.
You can check by doing console.log() and see what it says on the console.
If it's reactive, it should show something like MyClass {__ob__: Observer}
You can probably use this.$set('propName', value) to fix your problem
Docs: https://v2.vuejs.org/v2/api/#vm-set
Adds a property to a reactive object, ensuring the new property is also reactive, so triggers view updates. This must be used to add new properties to reactive objects, as Vue cannot detect normal property additions (e.g. this.myObject.newProperty = 'hi').
Either there is a typo in your post, or the typo also exists in your code and is the source of your problem.
In your post you're binding "newObjekt" to the Modification component, but your parent component has the property "newObject"
is this the source of your issue?
I found the solution in this answer.
As I edited my original post with the definition of the Modification component
<template>
<v-card-text>
<ModifyA v-model="object.a" #input="handleInput" />
<ModifyB v-model="object.b" #input="handleInput" />
</v-card-text>
</template>
<script>
import ModifyA from "...";
import ModifyB from "...";
export default {
name: "ShiftFormFields",
components: { ModifyA, ModifyB },
props: {
value: {
type: MyClass,
required: true
}
},
data() {
return { object: this.value };
},
methods: {
handleInput() {
this.$emit("input", this.object);
}
}
};
</script>
it shows the "problem" why the Fields ModifyA and ModifyB do not update if the value updates in the parent component.
As seen in the above definition the variable object is only set to the value once the Component is initialized. It follows that object is not reactive on behalf of value.
To solve this one can use the approach of the above mentioned answer:
<template>
<v-card-text>
<ModifyA v-model="object.a" />
<ModifyB v-model="object.b" />
</v-card-text>
</template>
<script>
import { Shift } from "classes";
import ModifyA from "...";
import ModifyB from "...";
export default {
name: "ShiftFormFields",
components: { ModifyA, ModifyB },
props: {
value: {
type: MyClass,
required: true
}
},
data() {
return { object: this.value };
},
watch: {
value(val) {
this.object = val;
},
object(value) {
this.$emit("input", value);
}
}
};
</script>
Due to the watcher, the object variable is updated whenever the value get's updated by the parent.

Vue md-input with Vuex updating only in one direction

I have a Vue app with an input element bound to a like this:
<template>
<input v-model="this.$store.state.myvalue"/>
</template>
and VueX store/index.js:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
myvalue: null
},
mutations: {},
actions: {},
modules: {}
});
When I modify myvalue with Vue devtools, the input's value changes too, but when I change the value in the input field, the state variable does not change. What am I doing wrong? I'm new to VueX.
Although it's not suggested to use vuex state directly bound with view layer, instead vuex is better to use for business logic, you can achieve changing the state on user input by below mentioned ways:
[1] two-way data binding: use v-model directive & bind the state in
it. On user input, the state will be updated. On changing state
programmatically, the element's value will be updated & reflect on
dom.
.vue file
<template>
<input v-model="$store.state.myvalue"/>
</template>
[2] manually create two-way data-binding.
.vue file
<template>
<input :value="getMyValue" #input="handleInput"/>
</template>
<script>
export default {
methods: {
handleInput (value) {
this.$store.commit('UPDATE_MY_VALUE', { value })
}
},
computed: {
getMyValue () {
return this.$store.state.myvalue
}
}
}
</script>
store file
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
myvalue: null
},
mutations: {
UPDATE_MY_VALUE (state, { value }) {
state.myvalue = value
}
},
actions: {},
modules: {}
});
when I change the value in the input field, the state variable does not change.
It does change, Dev tools just don't show the change. You can validate by changing the template to this:
<template>
<div>
<input type="text" v-model="$store.state.myvalue">
<div>{{ $store.state.myvalue }}</div>
</div>
</template>
But you should not mutate Vuex state like this! Vuex allows it but it's not recommended. Reason is your state changes should be traceable (easier to find which component changed the state and when). That's why Vuex recommends changing the state only by using mutations. Mutation is basically a function which is called when state change is needed.
Best way to do 2-way data binding against Vuex state is using computed properties with getter/seter like this:
<template>
<div>
<input v-model="myvalue">
<div>{{ myvalue }}</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
computed: {
myvalue: {
get: function() {
return this.$store.state.myvalue;
},
set: function(value) {
this.$store.commit("change_myvalue", value);
}
}
}
};
</script>
You need to define a mutation in your store to make it work like this:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
myvalue: ""
},
mutations: {
change_myvalue(state, value) {
state.myvalue = value
}
},
actions: {},
modules: {}
});
You can read more about mutations here

Vue mapState non reactive

I try to get the state from the store using the mapState function, But I can't use the generated code that returns the values into my template code ...
<template>
// Some code
<template v-if="!isLoggedIn">
// Some code
</template>
<template v-else>
// Some code
{{ currentUser.name }}
</template>
// Some code
</template>
<script>
import { mapState } from "vuex";
export default {
// Some code
computed: {
...mapState({auth : ['currentUser', 'isLoggedIn','customers']})
}
}
</script>
instead the following code work
<script>
import { mapState } from "vuex";
export default {
// Some code
computed: {
currentUser() {
return this.$store.state.auth.currentUser
},
isLoggedIn() {
return this.$store.state.auth.isLoggedIn
},
}
}
</script>
Warning message
[Vue warn]: Property or method "isLoggedIn" 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.
Thanks in advance
The right syntax to access non-root properties is the following (using arrow functions) :
computed: {
...mapState({
currentUser: state => state.auth.currentUser,
isLoggedIn: state => state.auth.isLoggedIn,
customers: state => state.auth.customers
})}
Check the documentation.
If you're trying to access values from a namespaced vuex module called auth, pass the name of the module as the first argument and the array of values to map as the second argument:
computed: {
...mapState('auth', ['currentUser', 'isLoggedIn','customers'])
}
You can mapState the module and then use, say this.auth.isLoggedin

Vue: Set child-component data with props values not working

Simply, I have two components:
Parent component which passes a prop object called "profile"
Child component which receives the profile prop
The profile value is an object like this:
{
name: "Something",
email: "some#thing.com"
}
What happens?
The child component receives perfectly the profile value in the template, but it seems impossible to retrieve and set it to the component data.
What is the goal?
I want to initialise the value "email" with the profile email prop.
What did I expect?
export default {
props: ["profile"],
data() {
return {
email: this.profile.email
}
}
}
UPDATE
I haven't specified that email is a data value used as model.
I have just tried to remove it and simply print the value of email in the template and it doesn't work as well.
<!-- PARENT COMPONENT -->
<template>
<dialog-settings ref="dialogSettings" :profile="profile"></dialog-settings>
</template>
<script>
import Auth from "../services/apis/auth";
import DialogSettings from "../components/dialog-settings";
export default {
name: "app",
components: {
"dialog-settings": DialogSettings
},
beforeCreate() {
Auth.checkToken()
.then(profile => {
this.profile = profile;
})
.catch(err => {
});
},
data() {
return {
title: "App",
drawer: true,
profile: {},
navItems: []
};
}
}
</script>
<!-- CHILD COMPONENT -->
<template>
{{profile}} <!-- All the fields are passed and available (e.g. profile.email)-->
{{email}} <!-- Email is not defined -->
</template>
<script>
import Auth from "../services/apis/auth";
import DialogSettings from "../components/dialog-settings";
export default {
name: "dialog-settings",
props: ["profile"],
data() {
return {
email: this.profile.email
}
}
}
</script>
UPDATE 2
I have tried several things and I think that the problem is the asynchronous call to the API in the beforeCreate().
your child component email property should be a computed value
<!-- CHILD COMPONENT -->
<template>
<div>
{{profile}} <!-- All the fields are passed and available (e.g. profile.email)-->
{{email}} <!-- Email is not defined -->
</div>
</template>
<script>
import Auth from "../services/apis/auth";
import DialogSettings from "../components/dialog-settings";
export default {
name: "dialog-settings",
props: ["profile"],
data() {
return {
}
},
computed: {
email () {
return this.profile ? this.profile.email : 'no email yet'
}
}
}
</script>
That's because parent component property is set after rendering child component.
"Data" is not reactive, it's set once when component is created. Prop 'profile" is reactive so first when you render component you should see {} and after response from Auth is set.
If you still want to keep it in data, you could display child component like that:
<dialog-settings ref="dialogSettings" :profile="profile" v-if="profile.email"></dialog-settings>
But i wouldn't recommend that!

Using root functions inside of prop object

I'm using Vuejs2.
I have a prop that contains some validation. I want to use a Lang object that is mixed into all my components. One of my props has a default value that needs to access this lang object. I can access the lang object in the create() function but not in the props.foo.default() option.
app.js
import Vue from 'vue';
import messages from '../lang/messages';
import lang from 'lang.js';
const Lang = new lang({
'messages': messages,
'locale': 'en'
});
Vue.mixin({
data: function () {
return {
lang: Lang
}
}
});
const app = new Vue({
el: '#app'
});
bar.component.vue
<template>
<div>{{ foo }}</div>
</template>
<script>
export default {
props: {
foo: {
type: String,
default: this.lang.get('some.lang') // Cannot read property 'get' of undefined
}
},
created () {
console.log(this.lang.get('some.lang'); // returns correct value
}
}
</script>
So to clarify I can access this.lang inside my created() function but I cannot get to it in my props object.
From what I could derive the context of this is different depending on the object / function you are in. I've tried to edit the props in the created hook but could not find a way to get access. I managed to get it working by creating a filter but it's inconsistent with some other code that I have in my component.
What would be a good way for a prop to have a default value that can access the root vm or be set outside of the props object?
You can't set a prop's default value based on a data property in a Vue instance. You can't access the Vue instance in the context of the props object, and you can't set the default value after the component has been instantiated.
Either import the lang object directly into the bar.component.vue file:
<script>
import lang from 'lang.js'
export default {
props: {
foo: {
type: String,
default: lang.get('some.lang')
}
}
}
</script>
Or, create a computed property fooVal which will return this.lang.get('some.lang') if the foo prop is not defined, and then use that in your template instead:
<template>
<div>{{ fooVal }}</div>
</template>
<script>
export default {
props: {
foo: { type: String }
},
computed: {
fooVal() {
return (this.foo === undefined) ? this.lang.get('some.lang') : this.foo;
}
}
}
</script>

Categories

Resources