Vue Checkbox filter component not working - vuejs2

In a single file component I made a checkbox array:
<label
v-for="(category, index) in categories"
:key="index"
>
<input type="checkbox" :value="category.value"
#change="emitSearchValue">
<span class="ml-2 text-gray-700 capitalize">{{ category.value }}</span>
</label>
With EventBus I transfer the data to the List component:
methods: {
emitSearchValue() {
EventBus.$emit('checked-value', 'this.checkedCategories')
}
}
In the List component I listen for the EventBus:
created() {
EventBus.$on('checked-value', (checkedCategories) => {
this.checkedvalue = checkedCategories;
})
}
Then my computed property look like this:
computed: {
filteredData() {
return this.blogs.filter(blog =>
// if there are no checkboxes checked. all values will pass otherwise the category must be included
!this.checkedCategories.length || this.checkedCategories.includes(blog.category)
)
}
},
In the console I get:
[Vue warn]: Error in render: "TypeError: Cannot read property 'length' of undefined"
Here is a link to a sandbox.
Whats wrong here?

Change the computed prop filteredData to:
computed: {
filteredData() {
if(!this.checkedvalue.length) return this.experiences
return this.experiences.filter(experience =>
this.checkedvalue.includes(experience.category)
);
}
}

Related

Vue warn]: You may have an infinite update loop in a component render function

Getting an infinite loop error when looping through <a> attributes in vue.js.
I have a method that loops through and adds attributes dynamically but when I use the method by binding it to an attribute in the <a> I get the error from the title. The attributes are a nested object within the original products array of objects.
Vue Code
<template>
<div>
<p>
<a
v-for="product in products"
:href="product.product_url"
type="submit"
v-bind:additionalAttrs="addAttributes()"
>
Click Me
</a>
</p>
</div>
</template>
<script>
export default {
data () {
return {
addedAttributes: [],
};
},
props: {
products: Array,
},
methods: {
addAttributes() {
this.products.forEach(product => {
for (const [key, value] of Object.entries(product.attributes)) {
this.addedAttributes.push(`${key}: ${value}`);
}
});
}
}
}
</script>
You use a method call to pass its result to additionalAttrs prop but it's not reactive and potentially can be called as many times as you have elements in products array
You just need one computed prop instead of an array and a method because they simply depend on products prop:
<a
v-for="product in products"
:href="product.product_url"
type="submit"
v-bind:additionalAttrs="addedAttributes"
>
Click Me
</a>
computed: {
addedAttributes() {
const addedAttributes = []
this.products.forEach(product => {
for (const [key, value] of Object.entries(product.attributes)) {
addedAttributes.push(`${key}: ${value}`);
}
});
return addedAttributes
}
}

VueJS handling dynamic input

I have an vs-input with :id=info.tag. How can I change the input value based on the :id tag? Data is dynamic
<template>
<vs-row vs-type="flex" vs-justify="space-around">
<div v-for="info in information" :key="info.id">
<vs-col vs-type="flex" vs-justify="center" vs-align="center" vs-w="3">
<vx-card :title="info.user_1" :subtitle="info.desc">
<div slot="footer">
<vs-row vs-justify="center">
<vx-input-group class="mb-base">
<vs-input :id="info.tag" placeholder="Data Output" readonly />
<br>
<div class="text-center">
<vs-button color="primary" #click="grabData($event, generator.tag)">Grab data</vs-button>
</div>
</vx-input-group>
</vs-row>
</div>
</vx-card>
</vs-col>
</div>
</vs-row>
</template>
<script>
export default {
data() {
return {
information: null
}
},
created () {
this.$store.dispatch('user/getInformation').then(this.$$nextTick).then(() => {
this.information = this.$store.state.user.information
})
},
methods: {
grabData(data, tag) {
this.$store.dispatch('user/grabData', {tag})
.then(res => {
//Nice! Set input value based on the tag
})
.catch(error => {
//hmm
})
},
}
}
</script>
I'll assume that each item in information has a field named value
Then you can just change vs-input to this
If you don't have a field name value you can add it
created () {
this.$store.dispatch('user/getInformation').then(this.$$nextTick).then(() => {
this.information = this.$store.state.user.information.map(item => Object.assign({value:''}, item))
})
}
So given vs-input I assume you are using VueSax.
It has v-model property which will line up to the value.
add v-model to vs-input
<vs-input :id="info.tag" v-model="infoInput" placeholder="Data Output" readonly/>
make sure to include property into data
data() {
return {
information: null,
infoInput: '',
}
},
then when you have an event just change the value.
this.infoInput = "new value"
This will update the value in vs-input
More info can be found v-model api

DOM loading computed property before GET request is finished, how to delay?

I am working on a single page app (Vue-Cli). The DOM Is looking for a value before Vue is done with a GET request. Although the app/data loads fine, the browser gives me an annoying console error: Error in render: "TypeError: Cannot read property 'branch' of undefined".
Here's my Template/HTML code:
<template>
<div>
{{ branch[0].branch }}
</div>
</template>
Vue instance:
data() {
return {
branches: [],
issue: ''
}
},
async created() {
await this.getIssue()
await this.getBranches()
},
methods: {
async getIssue() {
this.issue = (await axios.getIssue(this.$route.params.id)).data[0]
},
async getBranches() {
this.branches = (await axios.getBranches()).data
}
},
computed: {
branch() {
return this.branches.filter(
branch => this.issue.branch === branch.branch_id
)
}
}
How would I correctly "wait" for the BRANCHES to finish loading and THEN filter the branches array and place it in the DOM?
Just use conditional rendering using v-if:
<div v-if="branch.length">
{{ branch[0].branch }}
</div>
https://v2.vuejs.org/v2/guide/conditional.html
Alternatively, use a ternary expression:
<div>
{{ branch.length ? branch[0].branch : '' }}
</div>

add comment using v-model inside v-for loop in posts

I'm getting a posts array from a vuex getter and looping through them with v-for to display each post and it's comment then i added an input field and binded it with v-model to get the input value and dispatch an action to send the value to the API
<div class="post-content" v-for="(post, index) in posts">
<div class="post-comment">
<input type="text" class="form-control" placeholder="Add a comment" v-model="comment" #keyup.enter="addComment(post.id)">
</div>
</div>
<script>
export default {
data() {
return {
postContent: '',
comment: ''
}
},
methods: {
addPost() {
this.$store.dispatch('addPost', {
content: this.postContent
})
this.postContent = ''
},
addComment(postID, index) {
this.$store.dispatch('addComment', {
body: this.comment,
post_id: postID
})
}
},
created(){
this.$store.dispatch( 'loadFeed' )
},
computed: {
postsLoadStatus(){
return this.$store.getters.getPostsLoadStatus
},
posts(){
return this.$store.getters.getFeed
}
},
}
</script>
but when i set the v-model to a data property and try to type something in the input it's assigned on all posts so what's the right way to grab the comment data
Create a getter that accepts a function:
getters () {
getCommentByPostId(state) => (post_id) => {
return state.posts.find((post) => post.id === post_id).comment
}
}
Then use that getter on that :value and not v-model:
<input type="text" class="form-control" placeholder="Add a comment" :value="$store.getters['getCommentByPostId'](post.id)" #keyup.enter="addComment(post.id)">
Make sure to handle scenarios where the comment doesn't exist and return an empty string, too.

Vuex - Computed property "name" was assigned to but it has no setter

I have a component with some form validation. It is a multi step checkout form. The code below is for the first step. I'd like to validate that the user entered some text, store their name in the global state and then send then to the next step. I am using vee-validate and vuex
<template>
<div>
<div class='field'>
<label class='label' for='name'>Name</label>
<div class="control has-icons-right">
<input name="name" v-model="name" v-validate="'required|alpha'" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" placeholder="First and Last">
<span class="icon is-small is-right" v-if="errors.has('name')">
<i class="fa fa-warning"></i>
</span>
</div>
<p class="help is-danger" v-show="errors.has('name')">{{ errors.first('name') }}</p>
</div>
<div class="field pull-right">
<button class="button is-medium is-primary" type="submit" #click.prevent="nextStep">Next Step</button>
</div>
</div>
</template>
<script>
export default {
methods: {
nextStep(){
var self = this;
// from baianat/vee-validate
this.$validator.validateAll().then((result) => {
if (result) {
this.$store.dispatch('addContactInfoForOrder', self);
this.$store.dispatch('goToNextStep');
return;
}
});
}
},
computed: {
name: function(){
return this.$store.state.name;
}
}
}
</script>
I have a store for handling order state and recording the name. Ultimately I would like to send all of the info from multi step form to the server.
export default {
state: {
name: '',
},
mutations: {
UPDATE_ORDER_CONTACT(state, payload){
state.name = payload.name;
}
},
actions: {
addContactInfoForOrder({commit}, payload) {
commit('UPDATE_ORDER_CONTACT', payload);
}
}
}
When I run this code I get an error that Computed property "name" was assigned to but it has no setter.
How do I bind the value from the name field to the global state? I would like this to be persistent so that even if a user goes back a step (after clicking "Next Step") they will see the name they entered on this step
If you're going to v-model a computed, it needs a setter. Whatever you want it to do with the updated value (probably write it to the $store, considering that's what your getter pulls it from) you do in the setter.
If writing it back to the store happens via form submission, you don't want to v-model, you just want to set :value.
If you want to have an intermediate state, where it's saved somewhere but doesn't overwrite the source in the $store until form submission, you'll need to create such a data item.
It should be like this.
In your Component
computed: {
...mapGetters({
nameFromStore: 'name'
}),
name: {
get(){
return this.nameFromStore
},
set(newName){
return newName
}
}
}
In your store
export const store = new Vuex.Store({
state:{
name : "Stackoverflow"
},
getters: {
name: (state) => {
return state.name;
}
}
}
For me it was changing.
this.name = response.data;
To what computed returns so;
this.$store.state.name = response.data;
I've had such an error when getting value from the store, in computed, via ...mapState(['sampleVariable']), as you. Then I've used the this.sampleVariable in <script> and sampleVariable in <template>.
What fixed the issue was to return this in data(), assign it to a separated variable, and reuse across the component the newly created variable, like so:
data() {
return {
newVariable: this.$store.state.sampleVariable,
}
}
Then, I've changed references in the component from sampleVariable to newVariable, and the error was gone.
I was facing exact same error
Computed property "callRingtatus" was assigned to but it has no setter
here is a sample code according to my scenario
computed: {
callRingtatus(){
return this.$store.getters['chat/callState']===2
}
}
I change the above code into the following way
computed: {
callRingtatus(){
return this.$store.state.chat.callState===2
}
}
fetch values from vuex store state instead of getters inside the computed hook