How to apply class to a specific cell in a Buefy Table? - vue.js

I would like to know if there is a way in which I can dynamically apply classes targeting a specific cell in a Buefy table. As an example, the following is the code I am working on:
Template:
<b-table :data="status.peerStatus">
<template slot-scope="props">
<b-table-column :class="classObject" v-for="(column, index) in columns" :key="index"
:label="column.label" :visible="column.visible" :width="200">
{{ props.row[column.field] }}
</b-table-column>
</template>
</b-table>
Script:
computed: {
classObject() {
return {
"has-background-info": true
};
}
Right now, the whole row is getting highlighted in blue color due the has-background-info being set to true.
What I would like to do, however, is to target only a particular cell and apply class conditionally by passing the value of the cell like so.
Right now, what I am trying is passing the value of the cell to classObject like this
<b-table-column :class="classObject(props.row[column.field])" v-for="(column, index) in columns" :key="index"
And trying to set the class accordingly
computed: {
classObject(cellValue) {
return {
"has-background-info": cellValue === "YES" ? true : false;
};
}
However, the above doe not work. Is there any other way of doing this?

You should put it in method instead of computed
methods: {
classObject(cellValue) {
return {
"has-background-info": cellValue === "YES" ? true : false;
};
}
}

Related

How can the value change immediately after I clicked add or minus

Hi currently I am making a add & minus function and the problem I am facing is once I clicked add or minus, the value still remain the same... I use the watch property also and it still not working
The get_list is calling from a API
<b-row v-for="(data,i) in get_list" :key="i" class="mt-3 mx-0">>
<b-button pill
class="quantity-btn btn-secondary"
#click="addQuantity(data.id_product,data.id_style)"
>
<span style="transform: translateY(-5%);">+</span>
</b-button>
<div class="px-2">
<b-form-input type="number" min="1" :max="data.quantity_available" :value="quantity" v-model="data.cart_quantity" class="quantity-input"></b-form-input>
</div>
<b-button pill
class="quantity-btn btn-secondary"
#click="minusQuantity(data.id_product,data.id_style)"
>
<span style="transform: translateY(-5%);">-</span>
</b-button>
</b-row>
script
data: () => ({
get_list: [],
quantity:0,
}),
method :{
addQuantity(id,style) {
this.quantity++
this.updateQuantity(id,style)
},
minusQuantity(id,style) {
this.quantity--
this.updateQuantity(id,style)
},
},
watch: {
quantity(newVal, oldVal) {
console.log('new'+newVal)
}
}
Take a look of this code in code proved above.
<div class="px-2">
<b-form-input type="number" min="1" :max="data.quantity_available" :value="quantity" v-model="data.cart_quantity" class="quantity-input"></b-form-input>
</div>
Here it seems cart_quantity is getting used in template instead of quantity due to this it's not getting updated.
to fix this issue, you can update the cart_quantity property of the data object instead of the quantity data property in the addQuantity and minusQuantity
methods: {
addQuantity(id, style) {
this.get_list.forEach(item => {
if (item.id_product === id && item.id_style === style) {
item.cart_quantity++;
}
});
},
minusQuantity(id, style) {
this.get_list.forEach(item => {
if (item.id_product === id && item.id_style === style) {
item.cart_quantity--;
}
});
}
}
Please remove watch property as well it seems not required.
The problem is that you are performing the add/minus operation on the quantity variable but in the template, you are using the cart_quantity variable which is not increasing/decreasing.
So, either use quantity or cart_quantity as per your logic.
addQuantity(id, style) {
this.cart_quantity++;
// If you are using quantity as v-model
// this.quantity++
this.updateQuantity(id, style);
},
minusQuantity(id, style) {
this.cart_quantity--;
// If you are using quantity as v-model
// this.quantity--
this.updateQuantity(id, style);
},
One more thing, you don't need to use value and v-model together at the same time. If you want to give some initial value to the input, simply assign that value to your cart_quantity on mounted and use it further.
The last thing, you don't need a watcher as well.

How do you use buefy's b-taginput in a custom component so that it works like v-model, It is only working one way binding?

I'm new to vue and decided to try out buefy for some useful components.
To try and keep my code organized I'm trying to make a custom component using the b-taginput.
I have it so that the component loads with the tags in someArrayofTags, but when I'm typing into the b-taginput, it does not add new tags into someArrayofTags. Hence I lose the two-way binding / updating. I would like to know where I am going wrong and how I could adjust this.
I'm not too well versed to understand how they have implemented it, but i do see that it is composed of autocomplete and b-tags https://github.com/buefy/buefy/blob/dev/src/components/taginput/Taginput.vue
I'm trying to use the custom component as such
<mytaglist v-model="someArrayofTags"></mytaglist>
I know v-model is just v-bind on value and #input events. My code is below.
<template>
<b-field label="tag inputs">
<b-taginput
:value="value"
#input=someMethod($event.target.value)
size="is-small"
ref="ti"
>
<template slot="selected" slot-scope="value">
<b-tag
v-for="(tag, index) in value.tags"
:key="index"
:type="getType(tag)"
rounded
:tabstop="false"
ellipsis
closable
#close="$refs.ti.removeTag(index, $event)"
>
{{ tag }}
</b-tag>
</template>
</b-taginput></b-field
>
</template>
<script>
export default {
props: ['value'],
data() {
return {
normal: ["wnl","clear"]
};
},
methods: {
someMethod(tags) {
alert(tags)
this.$emit("input", tags)
},
getType(tag) {
if (this.normal.includes(tag)) {
return "is-danger";
} else {
return "is-success";
}
},
},
};
</script>
Thanks
After going through the source for buefy, I found that I could watch and update the values based on a v-model within the new component.
The code below works for me, but if anyone could provide a better solution I will leave it open.
<template>
<b-field label="tag inputs">
<b-taginput
v-model="newValue"
size="is-large"
ref="ti"
>
<template slot="selected" slot-scope="value">
<b-tag
v-for="(tag, index) in value.tags"
:key="index"
:type="getType(tag)"
rounded
:tabstop="false"
ellipsis
closable
#close="$refs.ti.removeTag(index, $event)"
>
{{ tag }}
</b-tag>
</template>
</b-taginput></b-field
>
</template>
<script>
export default {
props: ['value'],
data() {
return {
normal: ["wnl","clear","deep & quiet"],
newValue: this.value
};
},
methods: {
getType(tag) {
if (this.normal.includes(tag)) {
return "is-danger";
} else {
return "is-success";
}
},
},
watch: {
newValue(value) {
this.$emit('input', value)
},
value(value) {
this.newValue = value
}
}
};
</script>
<style>
</style>

Why are checkboxes not reset by v-model?

This is what i have:
Template
<div
v-for="(filter, index) in filtersList"
:key="index"
class="option-block"
>
<label
v-for="value in filter.values"
:key="value.id"
class="option-block__container"
>
{{ value.title }}
<input
type="checkbox"
v-model="filtersValues[filter.name]"
:value="value.value"
>
<span class="option-block__checkmark"></span>
</label>
</div>
And the part of my vue code:
data() {
return {
filtersList: {},
filtersValues: {}
}
},
beforeMount() {
this.loadInitData();
this.initFilters();
},
methods: {
loadInitData() {
const data = JSON.parse(this.$el.getAttribute('data-data'));
this.filtersList = data.filters;
},
initFilters() {
for (let i in this.filtersList) {
if (!this.filtersList.hasOwnProperty(i)) {
continue;
}
this.filtersValues[this.filtersList[i].name] = [];
}
}
}
It works, but when i call initFilters() method again (for reseting) checkboxes are still selected, and i don't know why.
The way you are assigning new, empty arrays to filterValues is not reactive.
If you change your initFilters to assign an entire new value to filterValues, you don't need to worry about using Vue.set(). For example
initFilters() {
this.filtersValues = this.filtersList.reduce((vals, { name }) => ({
...vals,
[ name ]: []
}), {})
}
Demo ~ https://jsfiddle.net/cjx09zwt/
Where did filter.values come from in line 2 of template?
Anyways vue would not be able to track the changes you are making (judging from the visible code)
There are some caveats to vue 2's reactivity. Check here for more info.
TLDR; you will need to declare anything you want to be made reactive in the component's data option upfront.
HTH

dynamically show and hide button according to computed property in vuejs

i want to show delete button for each comment that a particular user made, but i am unable to do it, i am using v-bind to disable delete buttton for others comment, so that user cant delete others comment, but its still visible for all the comments i.e (for other users as well ). suppose i am a user i comment 3 times then delete button should show on my comments only not on others comment. can some one please help me to achieve this?
My code snippet is below
<div class="comment-list" v-for="comment in comments" :key="comment._id">
<div class="paragraph" :class="[name, onCaretButtonClick ? 'paragraph' : 'hide']">
<span class="names" id="comment-desc">
<label class="comm-name">{{ comment.name }}</label>
<button class="like-btn" id="like-comment"><i class="fas fa-heart"></i></button>
<label> {{ comment.likes + ' likes' }} </label>
<button v-bind:disabled="isCommentIdMatched" v-on:click="deleteComment(comment.userId)"
class="del-btn">delete</button>
</span>
<p>{{ comment.comment }}</p>
</div>
</div>
below is deleteComment function and computed properties that i am using
computed: {
comments() {
return store.state.comments
},
getUserId() {
return store.state.user._id
},
isCommentIdMatched() {
let comments = store.state.comments
let value = false
if(comments) {
comments.forEach((comment) => {
if(comment.userId.match(this.getUserId)) {
value = true
}
})
return value
}
else {
return value
}
}
},
methods: {
deleteComment: function(commentUserId) {
let userId = store.state.user._id
if(commentUserId.match(userId)) {
console.log('yes matched')
}
},
}
there is no need to write isCommentIdMatched property, instead you can use change some lines.
add v-bind="comments" in below line
<div v-bind="comments" class="comment-list" v-for="comment in comments" :key="comment._id">
and add v-if="comment.userId && comment.userId.match(getUserId)" in below button
<button v-if="comment.userId && comment.userId.match(getUserId)" v-on:click="deleteComment(comment.userId)" class="del-btn">delete</button>

how to create dynamic input filters for columns in bootstrap-vue 2.0

I'm trying to create some dynamic filters in bootstrap-vue 2.0.0-rc.11 for the bootstrap-table columns
In my example I did this
<b-table class="io-store-list-table table-striped" show-empty hover stacked="md" :items="stores" :fields="storeFields" :current-page="currentPage" :per-page="perPage" :filter="filter" :sort-by.sync="sortBy" :sort-desc.sync="sortDesc" :sort-direction="sortDirection" #filtered="onFiltered" empty-filtered-text="{l s='There are no records matching your request' mod='ioweb_slocator'}">
<template slot="top-row" slot-scope="data">
<th v-for="field in fields">
<input v-if="field.filterable" type="text" #click.stop value="" />
</th>
</template>
<template v-for='field in formatted' :slot='field' slot-scope='row'>
<span v-html='row.value'></span>
</template>
</b-table>
<b-row>
<b-container>
<b-col xs="12" class="my-1 io-pagination">
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0"/>
</b-col>
</b-container>
</b-row>
</b-table>
Which renders a table like this
Now I'm attempting to filter the items based on what value is entered in each column. For example if I type mypartnername at the input box below Partner column, I would like to dynamically filter the items based on the row key called partner but I'm not sure how to approach this.
Based on the accepted answer I was able to create a structure that helped me like this.
Step 1:
Create a property filteredResults and set it equal to my unfiltered items property
let app = new Vue({
el: "#app",
data() {
return {
...
stores: stores,
groups: groups,
totalRows: stores.length,
filters: [],
loop: 0,
filteredResults: stores,
}
},
Step 2
Used filteredResults in the :items slot
Step 3
Create a single function to set the filters
methods: {
...
setFilter(property, value) {
console.log("Filtering");
this.filters[property] = {};
this.filters[property].value = value;
this.filteredResults = this.stores.filter(item => {
let keep = true;
// This is a basic equality filter. What I did in the actual code was to have an object with filter functions for each key. If a key was missing, it defaulted to straight equality.
this.fieldKeys.forEach(key => {
keep =
keep &&
(this.filters[key] === undefined || this.filters[key].value === undefined || this.filters[key].value === "" || item[key].match(this.filters[key].value));
});
return keep;
});
},
},
what about something like this? I dont know if i got your problem right.
<input v-if="field.filterable" #onchange="setFilter(field.property, $event.target.value)" type="text" #click.stop />
in the setFilter function you could do something like this:
setFilter(property, value) {
this.filters[property].value = value
}
get your results with a computed property
filteredResults(){
return users = this.users.filter((item) => {
for (var key in this.filters) {
if (item[key].value === undefined || item[key].value != filter[key])
return false;
}
return true;
}
}
I dont know if it works, couldnt test it. I am curious about the solution here.