I'm starting to make a universal input vue component. Before I worry about changing the data in the input, I just wanted to get the initial value from the store.
I have something like this:
<template lang="html">
<input class="input-text" type="text" name="" :value="value">
</template>
<script>
import store from '#/store/index';
export default {
name: 'InputText',
props: [
'dataModel',
'propertyName',
],
computed: {
value() {
return store.state[this.dataModel][this.propertyName];
},
},
};
</script>
This works. Instead of the value function in computed. I wanted to leverage mapGetters. So I tried something like this instead:
...mapGetters({
value: `${this.dataModel}/${this.propertyName}`,
}),
As soon as I tried the latter I got undefined for both values this.dataModel and this.propertyName. The context of this changes when invoking mapGetters because were in a new object. Is there a way to pass component props into mapGetters? Is there a way to set the context of this to be the component rather than the object argument? Or was my original approach/another approach the correct way to tackle this problem?
First, don t import the store in components, its available as this.$store, so remove:
import store from '#/store/index';
You can't use this like you are in mapGetters because it doesn't exist in the context youre trying to use it yet. The component is still early in it's lifecycle when when setting up properties. As far as I know, the name must be defined beforehand.
Related
MyMixin.vue has the method beginEdit.
What I'm trying to do is to make onFirstLineClick to call myMixin's beginEdit depending on the value of this.folded.
When I console logged myMixin.beginEdit, it is undefined and not surprisingly myMixin.beginEdit() doesn't work.
Am I missing something needed to use the function? If so, why does beginEdit work perfectly on <span>?
<template>
<div>
<div>
<div
#click="onFirstLineClick"
/>
<span
#click="beginEdit"
/>
</div>
</template>
<script>
import myMixin from './MyMixin';
export default {
name: 'currComponent',
mixins: [myMixin],
data() {
return {
folded: false,
};
},
methods: {
onFirstLineClick(e) {
// myMixin.beginEdit() doesn't work
}
},
};
</script>
The great thing about mixin is that when a component uses a mixin, all options in the mixin will be "mixed" into the component's own options. Which means that inside your component you can call mixin method directly like:
methods: {
onFirstLineClick(e) {
this.beginEdit()
}
},
That is also the reason why you can use beginEdit() method on <span> directly like:
<span #click="beginEdit" />
Please also know that in future if you declare a method in this component with same name as mixin method name beginEdit, then the component's method will take priority and you might see different behaviour. So, make sure to give unique names to mixin methods.
Please see this example
If I have a component like this:
<template>
<div v-if="on">
Hello
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
on: Boolean
},
mounted() {
console.log(this.on)
}
};
</script>
And then I render that component and specify nothing on the prop
<template>
<div >
<HelloWorld/>
</div>
</template>
This will output false instead of undefined?
Why is that?
Is there anyway I can detect if people didn't specify the prop, so I can use lodash _.isBoolean to detect it?
Currently, the only way I know is manually set the default to undefined.
You could set a default value on it and check if the value is still the same then the user didn't pass a prop, but there are instance where you can't really compare your value to its default, like if the prop is a function.
You could have this.$options.propsData inside of your component. If the prop is present here, the user has explicitly set it; default values aren't shown in.
I'm using the created lifecycle hook in vue.js to load data from my store to the data for a vue component. I noticed that this.selectedType = store.state.selectedType successfully loads the data from the store. However, if I use the getter to load from the store (i.e. this.selectedType = store.getters.getType()), I get the following error:
Error in created hook: "TypeError: Cannot read property 'selectedType' of undefined"
I don't understand why it is saying that selectedType is undefined because selectedType has the value "Item" in the store and is correctly loaded on create if I use this.selectedType = store.state.selectedType.
The getter is defined as such:
getters: {
getSelectedType: state => {
return state.selectedType
}
}
And the state is defined as:
state: {
selectedType: "Item"
}
Could someone please explain why this occurs? I'm hunch is that there is something about the lifecycles that I don't fully understand that is leading to this confusion.
You are not supposed to call getters. Just like computed properties, you instead write it like you are reading a variable. In the background the function you defined in the Vuex store is called with the state, getters (and possibly rootState and rootGetters) and returns some value.
Beside that, it is usually an anti-pattern to use a lifecycle hook to initialise any variable. Local 'component' variables can be initialised in the data property of the component, while things like the vuex state usually end up in a computed property.
The last thing I want to point out is that, if you have correctly added the store to your Vue application, you can access the store in any component with this.$store. To use getters in your application, you can use the mapGetters helper to map getters to component properties. I would recommend using something like this:
import { mapGetters } from 'vuex';
export default {
// Omitted some things here
computed: {
...mapGetters({
selectedType: 'getSelectedType'
})
},
methods: {
doSomething () {
console.log(this.selectedType);
}
}
}
Which is functionally equivalent to:
computed: {
selectedType () {
return this.$store.getters.getSelectedType;
}
}
I have an application which has Vue components as children.
The components pass back data to the parent via a this.$emit (numberchnaged below), which is caught at the parent level by a v-on (or #) directive, which in turns triggers a method.
This method then updates a data() property of the parent:
<template>
(...)
<Users #numberchanged="doNumCh"></Users>
(...)
</template>
<script>
(...)
export default {
components: {
Users
},
data() {
return {
u: "hello"
}
},
methods: {
doNumCh(value) {
this.u = value
}
}
}
</script>
This solution works but is quite verbose for just updating this.u with what <Users> sent back.
Is there a way to make the update right in the <Users> tag, something like
<Users #numberchanged="u=theValueReturedByUsers"></Users>
My problem is that I do not know how to extract theValueReturedByUsers, I only get hold of it in the method as value (in my example above).
Functionally, you're looking to have v-model behavior on your component. Vue provides for that. So you can say
<template>
(...)
<Users v-model="u"></Users>
(...)
</template>
which is a tidy view, as long as your Users component (side note: you should always have a hyphen in custom component names) takes the value parameter and $emits the input event.
See also v-bind.sync to work with props other than value.
The payload is reachable via $event.
For the code above, the solution would therefore be
<Users #numberchanged="u=$event"></Users>
You can do it like this (without write a method in parent) with using the variable $event which contains the value returned (Object or literal variable) from child component:
<users #numberchanged="{u=$event}"></users>
Basically I want to be able to access some centralized data by setting up a dataBus as a vue instance object, and then access and tweak this data from different components.
I cant seems to access by data from my componenets, even basic string interpolation is not getting rendered to the DOM.
export const dataBus = new Vue({
data: {
numQuotes: 4,
stringVar: 'Hellow There'
}
});
I also tried setting up my data as the return of the function data(). But being as my data bus is an actual vue instance I don't think this is correct. (I could be wrong). Following is the component in which I import my dataBus and try to output my data.
<template>
<div>
<h1>Quotes Added</h1>
<div id="trackerBar">
<div id="trackerBarActual">
<h2>{{numQuotes}}/10</h2>
</div>
</div>
</div>
</template>
<script>
import { dataBus } from '../main.js';
export default{
}
</script>
I am getting the following error: Property or method "numQuotes" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Am I missing something obvious? Is it even possible to access data this way? Or do I have to access it as a method?
You're missing the part where you assign data to your component. Try
<script>
import { dataBus } from '../main.js';
export default{
data () {
return dataBus.$data
}
}
</script>
You should definitely look into Vuex for state management though.