Vue.js/Vuex: How do I v-bind to a state value? - javascript

I'm trying to bind the text in a b-dropdown element to a value in the store. I tried binding to a computed property since the value in the store can change and the b-dropdown's text should change dynamically to reflect this change. I want to store the value in the store rather than as a data object because the value has to persist outside of the component where the b-dropdown exists.
Here's the b-dropdown element:
<b-dropdown v-bind:text="selectedSearchType" variant="outline-secondary">
...
</b-dropdown>
And the computed property
computed: {
selectedSearchType: function() {
return store.getters.getSelectedSearchType
}
},
The getter
getSelectedSearchType: state => {
return state.selectedSearchType
}
The state
state: {
selectedSearchType: "Item",
.....
}
I'm getting the following error:
[Vue warn]: Invalid prop: type check failed for prop "text". Expected String, got Function.
Instead if I do
<b-dropdown v-bind:text="selectedSearchType()" variant="outline-secondary">
I get
[Vue warn]: Error in render: "TypeError: Cannot read property 'selectedSearchType' of undefined"
How do I fix this to make the b-dropdown's text bind to the selectedSearchType ins the store?

Your best bet on binding to the store is to create a computed getter/setter and then use v-model in your input. That would look something like the following, you will have to adjust a little depending on your values.
This also assumes that b-dropdown is going to emit an input when the value changes.
<b-dropdown v-model="selectedSearchType" variant="outline-secondary">
computed: {
selectedSearchType: {
get() {
return //value from store
},
set(val) {
// set the value in the store
}
}
}

Related

Rendering properties of class objects in VueJS

today I came across a problem. I have element in template which is set up like this:
<div v-if="object">{{ object.property }}</div>
It was constantly throwing an error which said
cannot read properties of undefined reading 'property'
I was so confused why there is an error like this when there is the v-if and in console it read properly. After that I realised that the object is an instance of class I've defined, it's not a normal js object. Can someone explain why Vue can't read properties of a class instance ?
Most data properties in Vue have a value that will loosely equate to true unless it is a primitive value such as a number or a Boolean.
In your case what I resort to using is v-if="'property' in object", this works as long as the value of object never starts as or becomes null, An initial value of an empty object is required.
export default {
data: () => ({
object: {}
}),
beforeMount () {
console.log('property' in this.object) // Logs false
},
mounted () {
this.object = {
property: 'some value'
}
console.log('property' in this.object) // Logs true
}
}

How can i react to route change and set value of computed propery even if its value did not change in Nuxt - Vue

I have a list of articles on the page and a load more button, and I need to implement a scroll to article after I come back from the article page to the main page. To keep the articles that were loaded after clicking load more button I store them in state store and the array itself is in the computed property like so:
computed: {
items() {
return this.$store.getters["article-scrollto/getArticlesLoaded"];
},
}
in mounted the init() function is being run and in this function I populate store variable with initial data
this.$store.commit("article-scrollto/setInitialArticles", items);
//and this is the setter
setInitialArticles(state, articles) {
if (state.articlesLoaded.length === 0) {
state.articlesLoaded = [...articles];
console.log({ initsetter: state.articlesLoaded });
}
},
when the load more button is clicked in onCLick function I add data to the state like so:
setArticlesLoaded(state, articles) {
const benchmark = new Set();
state.articlesLoaded.forEach((item) => {
benchmark.add(item.title);
});
articles.forEach((article) => {
if (!benchmark.has(article.title)) {
state.articlesLoaded = [...state.articlesLoaded, article];
}
});
},
And then when I click on the article and the route is changed, and then I come back to the article list technically the store data does not change and my computed property getter does not trigger, and I get no articles even though there are many in the store.
I have tried to repopulate items() computed property in the mounted hook like so:
const storeArticles =
this.$store.getters["article-scrollto/getArticlesLoaded"];
if (storeArticles.length) {
console.log({ storeArticles });
this.items = [...storeArticles];
console.log({ itemsRefilled: this.items });
}
and it does show that there are items in this.items(), but there was no re-render and I get no articles on the page and in the console I have a warning saying that:
[Vue warn]: Computed property "items" was assigned to but it has no setter.
I read about computed setters, and it seems that I can set another data properties value from within computed property, not the value of computed property. Can you please hint me how should I deal with it?
Thanks a lot

Storing props locally (vue2)

Following:
Passing data from Props to data in vue.js
I have:
https://codesandbox.io/s/u3mr8
which gives the following warning:
(the idea is to avoid mutating props). What side effects can happen in a straightforward copy object operation? I don't get it. The function just saves props into data.
Drag and drop fails with:
Do you really need a setter for a computed prop?
Looking at:
Computed property was assigned to but it has no setter - a toggle component
I've come up with:
https://codesandbox.io/s/39sgo
which is great, no warnings, no errors; it's just that the component no longer renders (fails to get data saved from prop).
Any ideas/suggestions/help/advice would be really, really awesome.
I think the error is thrown because it is not allowed to set within the getter the value from which the computed property is generated. It is a logical loop to modify the initial value while getting the computed results. Instead you can just return the prop value on initial call to getter (if the local value is not yet set).
get() {
if (!this.itemSectionPropsLocal["itemSectionCategory"]) {
return Object.assign({}, this.itemSectionProps)[
"itemSectionCategory"
];
}
return this.itemSectionPropsLocal["itemSectionCategory"];
},
set(value) {
this.itemSectionPropsLocal = value;
},
Also, in setter, you should assign the received value not the prop. If you want to update the local values if the prop value changes after mount you should use a watcher.
watch: {
itemSectionProps: {
deep: true,
handler(val){
this.getPropsLocal = Object.assign({}, val["itemSectionCategory"])
}
}
}

How to access mapState property inside the method

How can I access the count property inside the method when I use vuex? Please see my code below.
Code screenshot:
Error
[Vue warn]: Computed property "count" was assigned to but it has no setter.
You can access computed properties just like you access your data properties in a component. But since you are mapping the state in this case. You should not increment or alter its value directly inside the component. Instead, you should dispatch an action with the updated/incremented value and use mutation to mutate the value of the count property in the state object.
More detail https://vuex.vuejs.org/guide/mutations.html#commit-with-payload
The mapState you wrote is inside a computed block. By default computed values are read-only, but you can make them read/write by giving them a setter (ie a function to call when modifying the computed value):
computed: {
count: {
get() { return this.$store.state.count; },
set(newValue) {
// This will most likely throw a warning, as it is bad practise.
this.$store.state.count = newValue;
}
}
}
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

Vue using v-for to render computed properties after loaded

I'm using v-for to iterate over a computed property, and that computed property depends on a data attribute, which is initiated as null. I will load it in beforeMount.
here is the pseudo-code:
<th v-for="item in computed_list">
{{ item.name }}
</th>
<script>
export default {
name: 'test',
data () {
return {
whole_list: null
}
},
beforeMount () {
this.load()
},
computed: {
computed_list: function() {
if (!this.series) return []
return this.whole_list.slice(1,3)
}
},
methods: {
async load () {
let res = await some_api_call()
this.whole_list = res['data']
}
}
}
</script>
But somehow it failed to render the list, and report TypeError: Cannot read property 'name' of null.
I'm new to Vue and not very familiar with its lifecycle. The basic idea is to render list of data, but those data are loaded somehow after the Vue instance is created. Not sure if it's the correct way to do this.
Initializing a data item to null breaks the VueJS state watching functionality so it won't know about changes to it. Initialize it as an empty object or array instead.
https://012.vuejs.org/guide/best-practices.html
The reason for this is that Vue observes data changes by recursively walking the data object and converting existing properties into reactive getters and setters using Object.defineProperty. If a property is not present when the instance is created, Vue will not be able to track it.
You don’t have to set every single nested property in your data though. It is ok to initialize a field as an empty object, and set it to a new object with nested structures later, because Vue will be able to walk the nested properties of this new object and observe them.

Categories

Resources