Add row styling when data populated, remove on mouse hover - vue.js

I'm currently using Bootstrap Vue's b-table to display my data from the database. I have SignalR implemented, where it will automatically update/refresh the table with the new data received from the server.
I would like to add some sort of highlight css styling or row variant
when new data gets populated into the table, and then remove the styling from the row on mouseover/hover. Currently, I can receive events in the console when a row gets hovered.
How am I able to achieve this functionality using Vue.js?
b-table
<b-table
...
:items="items"
:fields="fields"
#row-hovered="myRowHoverHandler"
...
> ... </b-table>
script tag
props: {
...
items: Array,
...
}
methods: {
myRowHoverHandler(record, index) {
console.log(this.items[index].id);
},
...
}

When the item gets updated, you can simply set the _rowVariant on the item that got updated, and then on hover remove it from the item again as shown in the codepen below.
Remember to use $set and $delete to keep Vue's Reactivity happy
https://codepen.io/Hiws/pen/WqKqdG

Related

Vuetify Treeview - set default selected when option items are retrieved asynchronously

I have a Vuetify TreeView component used across the app for which I need to be able to pass in a list of selected values alongside the option list.
The option list is retrieved using an http call. The problem is that the vuetify tree view component doesn't show the selected items when the option item list is not initialized before the selected list
See this codepen - https://codepen.io/amaieras/pen/eYVZJJq?editors=101
While if I initialize the items directly in the data, the default selected items appear as selected in the tree component - https://codepen.io/amaieras/pen/bGLpEBx?editors=101
<v-treeview
ref="tree"
v-model="selectedTerms"
:class="contentClass"
:filter="filter"
:indeterminate-icon="'far fa-minus-square'"
:items="applicabilityTagsValues"
:on-icon="'ph-square-logo-fill'"
:open.sync="expanded"
:search="searchValue"
class="dropdown-treeview"
selectable
selected-color="neutral lighten-4"
#input="emit('input', $event)"
>
</v-treeview>
store
.dispatch('businessContextRequirements/getApplicabilityTagsFilters',
filtersValuesPayload.value
).then((resp) => { applicabilityTagsValues.value = resp })
I can use a force mechanism to rerender the treeview component, but I am wondering if there is another way of making the component to render the selected nodes using vue's reactivity?
Using force mechanism
<v-treeview
ref="tree"
:key="dropdownKey"
v-model="selectedTerms"
:class="contentClass"
:filter="filter"
:indeterminate-icon="'far fa-minus-square'"
:items="applicabilityTagsValues"
:on-icon="'ph-square-logo-fill'"
:open.sync="expanded"
:search="searchValue"
class="dropdown-treeview"
selectable
selected-color="neutral lighten-4"
#input="emit('input', $event)"
>
</v-treeview>
setup() {
const dropdownKey = 0
store.dispatch('businessContextRequirements/getApplicabilityTagsFilters',
filtersValuesPayload.value
).then((resp) => {
dropdownKey++;
applicabilityTagsValues.value = resp
})
return {
dropdownKey,
applicabilityTagsValues
}
}

Expand all Bootstrap-Vue b-table rows

I have a b-table which looks something like this:
<b-table :fields="fields" :items="tableRows">
<template #head(expand_all+)="data">
//would like to make this a button to expand all row-details
<div class="text-right">{{data.label}}</div>
</template>
...
<template #cell(expand_all+)="data">
<div class="expand-arrow-container":class="{ 'expanded': data.detailsShowing }" #click="data.toggleDetails">
<font-awesome-icon icon="chevron-right" />
</div>
</template>
...
<template #row-details="row">
//display data here
</template>
</b-table>
Now, all of this works fine and and my row details expand/contract as I would expect (see b-table row-details for more information).
The problem arises in that I want to be able to click one button and expand/contract all the row-details. My plan is to change template #head(expand_all+)="data" into a button which can be clicked to accomplish this. However, I do not know how to do this. I have searched and read the b-table documentation and have not found any way of accomplishing what I want. I've even taken a look at the table object to see if it had any references to its rows and I didn't see anything.
Do any of you know a way to accomplish this? Getting a reference to the rows would make this a simple problem, but as I mentioned I didn't see anything. I am also not looking to swap out the b-table with a bunch of b-collapse elements since I have put a lot of work into the table.
As described on the docs, you can set a _showDetails property on your items passed to the table. Which if set to true will expand that row's details. This means you could loop through all your items and set that property to true to expand all rows. And do the same with false to collapse them.
If the record has its _showDetails property set to true, and a row-details scoped slot exists, a new row will be shown just below the item, with the rendered contents of the row-details scoped slot.
Here's two example methods you could use.
We're using $set because we're adding a new property to an already existing object. You can read more about why here
expandAll() {
for(const item of this.tableRows) {
this.$set(item, '_showDetails', true)
}
},
collapseAll() {
for(const item of this.tableRows) {
this.$set(item, '_showDetails', false)
}
}

Vuetify v-data-table drag and drop

I am using V-data-table along with vuex store. Below are the points how I configured my v-data-table
Disabled sort on each column
bind the v-data-table items with vuex state store data
using sortablejs to drag and drop the rows
Problem:
When I drag and drop the rows in the v-data-table the I am updating the vuex store(updating the index value on the objects in array with the table row index value). Vuex is updating properly but the data rendered in the v-data-table is not in the order as they are in the vuex state store
Could someone help me on this
The best way I tried to overcome this problem is to force re-render the v-data-table component, but when I do this I cannot drag and drop anymore
Force rendered using the following the template
<template>
<component-to-re-render :key="componentKey" />
</template>
// script
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}
This might not be the optimal solution, but I had a similar problem except I was just using a regular Array. I managed to fix it by calling the Sortable.create() method in updated() life cycle hook.
My guess is that when you call Sortable.create(table, ...) it refers to that instance of the table element. However, when you try to change the table by modifying the key, it changes that instance. Therefore, you need to call Sortable.create() again whenever the vue component is updated.

bootstrap-vue editable table revert value if error

I am using the vue js and bootstrap-vue to make an editable table, the user is allowed to make changes on the table with v-on:change assisting to axios update the database. The restriction is that the Languages are Unique and cannot be an empty string, I cannot seem to be able to revert the value if the user makes a mistake. What is the recommended approach to this? Any help is greatly appreciated.
I have tried to "refresh" the table by doing:
this.languages = this.languages;
Does not seem to refresh the value in the table.
a section of vue component on the table:
<b-table striped hover :items="filtered" :fields="fields">
<template slot="name" slot-scope="data">
<b-form-input type="text" :value="data.item.name" v-on:change="updateLanguage($event,data.item.id)"></b-form-input>
</template>
</b-table>
in the methods of vue export default:
updateLanguage(e,id) {
if(e.trim()){
const find_langauge = this.languages.find(language=>{
return language.name.toLowerCase() === e.toLowerCase();
});
find_langauge ? this.languages = this.languages : console.log('no match');
} else {
console.log('cannot be empty');
this.languages = this.languages;
}
}
in computed:
filtered() {
return this.search ? this.languages.filter(language=>
language.name.toLowerCase().includes(this.search.toLowerCase())) : this.languages;
}
I cannot find any solution to refresh the :value, so I ended up with force re-render of the component. Not ideal, but at least the entire component got refreshed. Will appreciate any better way to approach this instead of re-rendering.
Vue doesn't retain "initial" values for inputs, as it relies on your data modal as the source of truth.
You would need to store the initial value in data, and when detecting a reset, copy that initial value back to the v-model associated with the input.
Also, if you have inputs in multiple rows, it is a good idea to set a unique key on the input, and or if you have a unique key field (where the value is unique per item row), set the primary-key prop on b-table to have it generate a unique Vue key per TR element.

Vue-Bootstrap: how to trigger sorting of one b-table to trigger sorting of the another b-table?

I'm using VueBoostrap <b-table> components, in a combination with sorting routine applied. In my project I have some more complex sorting routine, but for this dummy example, I will use the default one.
When a sorting is applied for a b-table, the table is simply sorted based on the clicked field in the table header. But what I need to achieve is to split the table header from the table content (since I want to place the content in a scrollable div later, while the header to remain static at the top - as user will be scrolling).
The complete code is given on this link (check componets/TableTest.vue), where I have three <b-table> components. First one is just a dummy example, and the next two are identical as the first one, but for one of them the header is hidden and for the other the body is hidden.
What I want to achieve is:
If you take a close look at the docs (https://bootstrap-vue.js.org/docs/components/table) you can see that there are certain events emitted by the <b-table> component.
One of them is sort-changed. So if you listen for that on your header only component and then set a sortBy property that you pass into the body only component you're all set.
//header only
<b-table ... #sort-changed="sortChanged">
// body only
<b-table :sort-by="sortBy" ...>
sortChanged(e) {
this.sortBy = e.sortBy
}
Full example here: https://codesandbox.io/s/y30x78oz81
As I understand it, the OP is asking :
"How do I force a <b-table> component to (re)sort itself without requiring the user to click on that <b-table> component?"
My answer (in their case):
Detect the click event on the "visible header" mentioned
In the function handler for that click event, emit a sort-changed event from the target table
// Assuming an SFC (single file component)
<template>
<div>
<b-button #click="handleClick">Sort the table!</b-button>
<b-table ref="mytable" #sort-changed="handleSortChange"></b-table>
</div>
</template>
<script>
export default {
// ...
methods: {
handleClick(evt) {
/// this is called when you click the button
this.$refs.mytable.$emit('sort-changed')
}
handleSortChange(context) {
// this is called when b-table with ref "mytable" hears the 'sort-changed' event
// that it has emitted
// sorting logic goes here
}
}
//...
}
</script>