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

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
}
}

Related

how to transfer and change a variable in different components? vue 3

how to transfer and change a variable in different components. I have an AllItems Page and a Header component. When clicking on the button in AllItems, I want to change the value in the Header. Is it possible to do this without passing the AllItems page itself to the Header component (<all Items #count='count'/>) - because they are duplicated.
AllItems button
<div class="btn me-md-2" id="btn" :count='count' #click="addTo(id)">Add to cart</div>
addTo(id){
this.$emit('addToCart');
this.cart.push(this.filteredItems[id])
}
Header button
<p>{{ count }}</p>
<allItems #addtoCount='countCart'/>
methods: {
countCart() {
this.count += 1
}
}
What you are looking for is a state management. Either define your own reactive object:
https://dev.to/vuesomedev/you-might-not-need-vuex-with-vue-3-52e4
Or use the recommended state management for Vue 3:
https://pinia.vuejs.org/

Make PrimeVue Splitter to have dynamic two-way panel size

I am using PrimeVue Splitter with Vue3 and composition API. I want to make the size of each panel dynamic , so when I click a button they return to their original size.
This is what I have so far
<button #click="fixPanels()" >fix</button>
<Splitter style="height: 100%" class="mb-3">
<SplitterPanel :size=smallPanelSize >
Panel 1
</SplitterPanel>
<SplitterPanel :size=bigPanelSize >
Panel 2
</SplitterPanel>
</Splitter>
export default {
setup(){
let smallPanelSize = ref(30)
let bigPanelSize = ref(70)
const fixPanels = ()=>{
message.value = 30;
bigPanelSize.value = 70
}
return{smallPanelSize, bigPanelSize, fixPanels}
}
}
I can resize the panels freely, but when fixPanels is clicked, they dont return to their original size. I guess I have to use two-way binding somehow or v-model but I dont know how, there is no input here. If this is impossible, can you suggest a similar resizable panels component that the panel size is dynamically controlled?
Thank you
It seems that the SplitterPanel component does not handle reactive sizes. As a result, the prefix sizes are determined when the component is loaded and cannot be reset via reactivity.
Therefore, the solution is to reload the components to force the sizes to be reset.
For this, I created a Panels component which will be used only to display the panels and its reset button. The sizes will be retrieved via props and not assigned within the component. The resize button will send an event via emit in order to signal to the parent component the will to reload the component, in order to reset the sizes.
The parent component will create the two default size variables with ref and send them to the props. The event will also be retrieved to call the fixPanels function. The only subtlety is to manage to force the reload of the Panels component. For this, I added a key value to the Panels component which will be incremented each time the fixPanels function is called. The purpose of this variable is only to make the child think that the values of the component have changed and to create the need to reload it.
Just below, the two coded components that correspond to my explanations. (I used Vue 3 with the composition API and TypeScript, but you will probably know how to convert it to Vue 3 Option, if needed)
Panels.vue
<template>
<button #click="$emit('reload')">fix</button>
<Splitter style="height: 100%" class="mb-3">
<SplitterPanel :size="smallPanelSize">
Panel 1
</SplitterPanel>
<SplitterPanel :size="bigPanelSize">
Panel 2
</SplitterPanel>
</Splitter>
</template>
<script setup lang="ts">
import Splitter from "primevue/splitter";
import SplitterPanel from "primevue/splitterpanel";
const props = defineProps<{smallPanelSize: number, bigPanelSize:number}>();
</script>
ManagePanel.vue
<script setup lang="ts">
import { ref } from "vue";
import Panels from "./components/Panels.vue";
let small = ref(30);
let big = ref(70);
let componentKey = ref(0);
function forceRender() {
componentKey.value += 1;
}
function fixPanels() {
small.value = 30;
big.value = 70;
forceRender();
}
</script>
<template>
<Panels
:smallPanelSize="small"
:bigPanelSize="big"
:key="componentKey"
#reload="fixPanels"
/>
</template>

VueJS + Vue.Draggable + Vuex Store + Computed Variables

Is there a way to use Vue.Draggable lists with computed variables in a Vuex Store? I'm fairly new to VueJS and this is my current component setup:
// Template
<draggable class="list-group" :list="list1" group="people">
<div
class="list-group-item"
v-for="(element, index) in list1"
:key="element.id"
>{{ element.name }} ({{ element.list }}) ({{ index }})</div>
</draggable>
// Script
computed: {
list1: {
return this.$store.getters.listItems.filter(item => item.list === 1)
}
}
// Store
const getters = {
listItems: (state) => {
return state.items
}
}
Without computed variables, everything works fine. When using computed variables, I can drag list items between lists, however the UI won't update because it's computed from the store.
Basically, what I need is to display multiple lists based on an input file (json). These lists have list items that can be dragged between different lists (with Vue.Draggable). Also, the user can create new list items / delete existing list items.
What's the best way to achieve this?
Thank you in advance.
In a complex draggable use-case, also using vuex, I ended up creating a variable in state called 'changes', and every time something changes, it gets incremented (through a vuex mutation). The data that's linked to dragging is in data, not in the store. I manually sync the two.
I use this - through a watcher - to trigger a rerender of my base component with completely fresh data.
This solution is ugly, and likely won't be necessary in vue3.
You probable need to do the following:
Have your draggable div into a new component
On this new component, have this #change.self="handleDragAndDrop($event)"
Handle the D&D
handleDragAndDrop: function (event){
const eventType = Object.keys(event)[0];
const chosenFrame = event[eventType].element;
store.dispatch(
"updateFramesOrder",
{
event: event,
listItemId: this.$props.itemId,
}
);
}
Finally in the store create the updateFramesOrder action that changes the list accordingly.
Hope it helps!

Add row styling when data populated, remove on mouse hover

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

Is it posible to delete `div` from template?

I have a component myHello:
<temlate>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
</template>
And main component:
<h1>my hello:</h1>
<my-hello><my-hello>
After rendering shows this:
<h1>my hello:</h1>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
How to delete <div> ?
With VueJS, every component must have only one root element. The upgrade guide talks about this. If it makes you feel better, you are not alone. For what it's worth the components section is a good read.
With the myriad of solutions to your problem, here is one.
component myHello:
<temlate>
<h2>Hello</h1>
</template>
component myWorld:
<temlate>
<p>world</p>
</template>
component main
<h1>my hello:</h1>
<my-hello><my-hello>
<my-world><my-world>
Vue gives you the tools to do so by creating templates or you can do it by having a parent div with two parent divs as children. Reset the data from the data function. Stick with convention (create templates). It's hard to get used to use Vue when you have a jQuery background. Vue is better
Ex.
data () {
message: 'My message'
}
When you click a button to display a new message. Clear the message or just set the message variable with a new value.
ex. this.message = 'New Message'
If you like to show another div. you should used the if - else statement and add a reactive variable. Ex. showDivOne
data () {
message: 'My message'
showDivOne: true,
showDivTwo: false
}
Add this reactive variables to the parent divs that corresponds to the div.
When clicking the button, you should have a function like...
methods: {
buttonClick () {
this.showDivOne = false
this.showDivTwo = true
}
}
I think you can use v-if directive to controll. Add a flag to controll status to show or hide