Expand and collapse treeview using button | Vuetify - vue.js

I got some problem with Vuetify treeview component. My goal is:
When I select some treeview's element and press expand /collapse button I want to see all children for this element, and then when I press the button once again I want to collapse all selected element.
Here's my code:
<template>
<v-container fluid>
<v-btn justify-center #click="expandCollapse"> Expand or collapse </v-btn>
<v-treeview
class="ml-4"
v-model="tree"
:open="items"
:items="items"
activatable
item-key="name"
>
<template slot="prepend" slot-scope="{ item }">
<v-list-tile-avatar
size="30"
style="min-width: 40px;"
tile
>
<img :src="imageType(item.type)" alt=""/>
</v-list-tile-avatar>
</template>
</v-treeview>
</v-container>
</template>
<script>
export default {
data: () => ({
items: [
{
name: 'Factory A',
type: 'board',
children: [
{
name: 'Line 1',
children: [{
name: 'Machine ABC',
type: 'machine'
}],
type: 'board'
},
{
name: 'Line 2',
children: [{
name: 'Machine ABC 02',
children: [{
name: 'Part A',
type: 'part'
},
{
name: 'Part B',
type: 'part'
},
{
name: 'Part C',
type: 'part'
},
{
name: 'Part D',
type: 'part'
}
],
type: 'machine'
}],
type: 'board'
},
{
name: 'Line 3',
children: [{
name: 'Machine ABC 03',
type: 'machine'
}],
type: 'board'
},
{
name: 'Line 4',
children: [{
name: 'Machine ABC 04',
type: 'machine'
}],
type: 'board'
}
]
}
]
}),
methods: {
imageType (type) {
switch (type) {
case 'board':
return require('#/assets/images/board.svg')
case 'machine':
return require('#/assets/images/machine.svg')
case 'part':
return require('#/assets/images/part.svg')
}
},
// ADDED
bfs: function (tree, key, collection) {
if (!tree[key] || tree[key].length === 0) return
for (var i = 0; i < tree[key].length; i++) {
var child = tree[key][i]
collection.push(child)
this.bfs(child, key, collection)
}
},
expandCollapse (item) {
const childs = []
const selectedIDs = []
childs.push(item)
this.bfs(item, 'children', childs)
}
}
}
</script>
Ok, I got the solution. I just added two if-statements to function.
if (this.open.indexOf(selectedIDs[0]) === -1) {
this.open = this.open.concat(childs.map(node => node.id))
} else {
this.open = this.open.filter((item) => !selectedIDs.includes(item))
}

Use the open and active event listeners to update the open/closed items.
To get the active items use the update:active event.
To get the open items use the update:open event.
on expandCollapse use the active items and the open items to determine whether to open or close, and then update the open to reflect the change. This part is just iterating through the items and running a comparing to active and open

Related

Show only enabled item in v-treeview

I'm using v-treeview and i have items like that:
[
{
id: 1,
name: 'name 1',
disabled: false,
children: [
{
id: 2,
name: 'name 2',
disabled: false,
children: [
{
id: 3,
name: 'name 3',
disabled: false
}
]
},
{
id: 4,
name: 'name 4',
disabled: false,
children: [
{
id: 5,
name: 'name 5',
disabled: true
}
]
},
{
id: 6,
name: 'name 6',
disabled: true,
children: [
{
id: 7,
name: 'name 7',
disabled: true
}
]
}
]
}
]
that i want is, only showing the items with disabled false; without passing by filter data to send to v-treeview;
I already tried on slot on not showing any data when disabled is true but the the item set blank space:
<template v-slot:label="{item}" >
<v-container v-if="!item.disabled">
{{ item.name }}
</v-container>
</template>
Some items is provided with v-select, it allow to switch the disabled value of children items, so can't use filter computed cause i have no access of the children with disabled true;
You can use computed in order to create a dynamic variable:
computed: {
visibleElements: function() {
// filter here only entries you are interested to show
let visibleElements = elements.map(el => {
:
:
});
return visibleElements;
}
}

Vuetify Select with options array of objects prevents update from occurring?

I've got a doughnut chart being displayed using vue-chartjs and am supplying color schemes via chartjs-plugin-colorschemes.
I'm working on the ability for the user to switch out color schemes and persist their chosen scheme to the db, however I've run into an odd issue. When adding a colors array to the options that are being supplied to the Vuetify select, it breaks the chart from updating to the new color scheme. I've narrowed it down to this specifically, because if I remove the colors array from options, it correctly updates the chart (however then I'm not able to display the colors like I'm trying to do in the select dropdown).
Why would adding properties to the Vuetify select break this functionality? Is it because they're being dynamically generated?
**ParentComponent.vue:**lang-vue
<template>
<div
class="chart-wrapper">
<child-graph
v-resize="onResize"
:chart-data="chartData"
:is-mobile="isMobile"
:styles="graphStyles"
:theme="localTheme" />
</div>
<pack-theme-modal
v-model="themeModalOpen"
:theme="localTheme"
#handle-update="handleUpdateTheme" />
</template>
<script>
export default {
data () {
localTheme: '',
themeModalOpen: false
},
methods: {
handleUpdateTheme (theme) {
this.localTheme = theme;
this.themeModalOpen = false;
}
}
}
</script>
ChildGraph.vue:
<script>
import { Doughnut, mixins } from 'vue-chartjs';
import Chart from 'chart.js';
import 'chartjs-plugin-colorschemes';
const { reactiveProp } = mixins;
export default {
extends: Doughnut,
mixins: [reactiveProp],
props: {
theme: {
type: String,
default: ''
}
},
data () {
const vm = this;
return {
options: {
cutoutPercentage: 75,
legend: {
display: true,
position: 'right'
},
plugins: {
colorschemes: {
scheme: this.theme
}
},
responsive: true,
maintainAspectRatio: false,
tooltips: {
enabled: false
},
}
};
},
mounted () {
this.renderChart(this.chartData, this.options);
},
watch: {
theme (newVal, oldVal) {
console.log({ newVal });
const chart = this.$data._chart;
chart.options.plugins.colorschemes.scheme = newVal;
chart.update();
}
}
};
</script>
ThemeModal.vue:
<template>
<v-dialog
v-model="show"
max-width="750">
<v-card>
<v-card-title>
Choose a New Chart Color Theme
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-select
v-model="localTheme"
color="primary"
dense
item-text="text"
item-value="value"
:items="options"
label="Theme Options"
outlined>
<template #selection="{ item }">
<p class="mb-0 mr-4 text-body-1">
{{ item.text }}
</p>
<color-swatch :colors="item.colors" />
</template>
<template #item="{ item, attrs, on }">
<v-list-item
v-bind="attrs"
v-on="on">
<v-list-item-content>
<v-list-item-title>
<span class="mr-4">{{ item.text }}</span>
<color-swatch :colors="item.colors" />
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-select>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions class="justify-space-between">
<v-btn
class="grey7--text"
:ripple="false"
text
#click="closeModal">
Cancel
</v-btn>
<v-btn
color="primary"
depressed
:ripple="false"
#click="handleUpdate">
<span>Update Theme</span>
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import 'chartjs-plugin-colorschemes/src/plugins/plugin.colorschemes';
import { Blue6, BlueGreen6, Breeze6, Celestial6, Circuit6, Damask6, Depth6, Flow6, Forte6, Genesis6, IonBoardroom6, Kilter6 } from 'chartjs-plugin-colorschemes/src/colorschemes/colorschemes.office';
import { Tableau10, ColorBlind10, JewelBright9, HueCircle19 } from 'chartjs-plugin-colorschemes/src/colorschemes/colorschemes.tableau';
import ColorSwatch from '~/components/ColorSwatch.vue';
export default {
props: {
theme: {
type: String,
default: ''
},
value: {
type: Boolean,
default: false
}
},
data () {
return {
localTheme: this.theme || 'tableau.Tableau10',
options: [
// COMMENTED OUT OPTIONS WORK, BUT DON'T DISPLAY COLOR SWATCH
// { text: 'Theme 1', value: 'office.Blue6' },
// { text: 'Theme 2', value: 'office.BlueGreen6' },
// { text: 'Theme 3', value: 'office.Breeze6' },
// { text: 'Theme 4', value: 'office.Celestial6' },
// { text: 'Theme 5', value: 'office.Circuit6' },
// { text: 'Theme 6', value: 'office.Damask6' },
// { text: 'Theme 7', value: 'office.Depth6' },
// { text: 'Theme 8', value: 'office.Flow6' },
// { text: 'Theme 9', value: 'office.Forte6' },
// { text: 'Theme 10', value: 'office.Genesis6' },
// { text: 'Theme 11', value: 'office.IonBoardroom6' },
// { text: 'Theme 12', value: 'office.Kilter6' },
// { text: 'Theme 13', value: 'tableau.Tableau10' },
// { text: 'Theme 14', value: 'tableau.ColorBlind10' },
// { text: 'Theme 15', value: 'tableau.JewelBright9' },
// { text: 'Theme 16', value: 'tableau.HueCircle19' }
// COMMMENTED IN OPTIONS SHOW COLOR SWATCH BUT DON'T UPDATE GRAPH
{ text: 'Theme 1', value: 'office.Blue6', colors: Blue6.map(color => color) },
{ text: 'Theme 2', value: 'office.BlueGreen6', colors: BlueGreen6.map(color => color) },
{ text: 'Theme 3', value: 'office.Breeze6', colors: Breeze6.map(color => color) },
{ text: 'Theme 4', value: 'office.Celestial6', colors: Celestial6.map(color => color) },
{ text: 'Theme 5', value: 'office.Circuit6', colors: Circuit6.map(color => color) },
{ text: 'Theme 6', value: 'office.Damask6', colors: Damask6.map(color => color) },
{ text: 'Theme 7', value: 'office.Depth6', colors: Depth6.map(color => color) },
{ text: 'Theme 8', value: 'office.Flow6', colors: Flow6.map(color => color) },
{ text: 'Theme 9', value: 'office.Forte6', colors: Forte6.map(color => color) },
{ text: 'Theme 10', value: 'office.Genesis6', colors: Genesis6.map(color => color) },
{ text: 'Theme 11', value: 'office.IonBoardroom6', colors: IonBoardroom6.map(color => color) },
{ text: 'Theme 12', value: 'office.Kilter6', colors: Kilter6.map(color => color) },
{ text: 'Theme 13', value: 'tableau.Tableau10', colors: Tableau10.map(color => color) },
{ text: 'Theme 14', value: 'tableau.ColorBlind10', colors: ColorBlind10.map(color => color) },
{ text: 'Theme 15', value: 'tableau.JewelBright9', colors: JewelBright9.map(color => color) },
{ text: 'Theme 16', value: 'tableau.HueCircle19', colors: HueCircle19.map(color => color) }
]
};
},
computed: {
show: {
get () {
return this.value;
},
set (value) {
this.$emit('input', value);
}
}
},
methods: {
closeModal () {
this.show = false;
},
handleUpdate () {
this.$emit('handle-update', this.localTheme);
}
},
watch: {
theme (val) {
this.localTheme = val;
}
},
components: {
ColorSwatch
}
};
</script>
On a Hail Mary attempt, I found something in the chartjs-plugin-colorschemes docs that mentioned setting override: true within the color schemes object on data. Adding that fixed it somehow!
plugins: {
colorschemes: {
scheme: this.theme,
override: true
}
},

Vuetify clone and filter v-select

I use vuetify v-select.
I want to clone the v-select with the array options from the first v-select and disabled (or remove) the selected values in the other v-select.
It can be clone multiple time and I want that if for example the 4 v-select is selected option X so this X option will be disabled in all the other v-select (also in the first one and reverse).
for example the options array:
[
{ title: 'title 1', id: '1', status: '0' },
{ title: 'title 2', id: '2', status: '0' },
{ title: 'title 3', id: '3', status: '0' },
{ title: 'title 4', id: '4', status: '0' }
]
Example
You can have vuetify v-select to clone the values to multiple select boxes as well as remove the one which is already selected from rest of the select boxes
Here is the working codepen : https://codepen.io/chansv/pen/wvvzbLX?editors=1010
You can have any number of select boxes just by looping through and assign the index as key to select box
Find the code below
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-btn #click="addSelectBox(true)">add select box</v-btn>
<div v-for="id in Object.keys(selectItems)" :key="id">
<v-select
v-model="selectItems[id].selected"
:items="selectItems[id].available"
label="Standard"
item-key="id"
item-value="id"
multiple
chips
deletable-chips
clearable
#change="modifyOthers"
></v-select>
<v-btn #click="deleteSelectBox(id)">delete select box</btn>
</div>
</v-container>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selectItems: {},
numberOfSelectBoxes: 4,
itemsBucket: [
{ title: 'title 1', id: '1', status: '0' },
{ title: 'title 2', id: '2', status: '0' },
{ title: 'title 3', id: '3', status: '0' },
{ title: 'title 4', id: '4', status: '0' }
],
allSelected: [],
allUnSelected: [],
}),
methods: {
modifyOthers(val, id) {
this.updateAllSelected();
this.updateAllUnselected();
this.updateAllAvailable();
},
updateAllSelected() {
this.allSelected = [];
var self = this;
Object.keys(self.selectItems).forEach(x => {
self.allSelected = self.allSelected.concat(self.selectItems[x].selected);
});
},
updateAllUnselected() {
this.allUnSelected = [];
var self = this;
this.allUnSelected = self.itemsBucket.map(x => x.id).filter(x => !self.allSelected.includes(x));
},
updateAllAvailable() {
var self = this;
Object.keys(self.selectItems).forEach(key => {
self.selectItems[key].available = self.itemsBucket.map(x => x.id).filter(x => {
return self.selectItems[key].selected.includes(x) || self.allUnSelected.includes(x);
});
});
},
addSelectBox(fromUI) {
var self = this;
if (fromUI) {
var currentLast = +Object.keys(self.selectItems)[Object.keys(self.selectItems).length -1];
var newIndex = currentLast + 1;
self.$set(self.selectItems, newIndex, {selected: '', available: []});
self.selectItems[newIndex].available = self.allUnSelected;
} else {
for (var i = 1; i <= this.numberOfSelectBoxes; i++) {
self.$set(self.selectItems, i, {selected: '', available: []});
self.selectItems[i].available = self.itemsBucket.map(y => y.id);
}
}
},
deleteSelectBox(id) {
delete this.selectItems[id];
this.modifyOthers();
}
},
created() {
this.addSelectBox(false);
this.updateAllUnselected();
}
})

Select Option 1 to Select Option 2 and view the result (text) in VueJs

Does anyone know how to actually display the text value from multiple dependent select fields?
I need option 1 to list a set of parent options. When an Option 1 is selected, it will determine the values available in Option 2 select field.
I then want the text value associated with the selected option to be displayed below the select field.
<template>
<div>
<panel class="main-content">
<div>
<b-form-select v-model="selected" :options="options" class="mb-3" />
<div>Selected: <strong>{{ selected }}</strong></div>
</div>
<div>
<b-form-select v-model="selected" :options="options" class="mb-3" />
<div>Selected: <strong>{{ selected }}</strong></div>
</div>
</panel>
</div>
</template>
<script>
export default {
data () {
return {
selected: null,
options: [
{ value: null, name: 'opt', text: 'Select One' },
{ value: 'option1', name: 'mys', text: 'Carbohydrates' },
{ value: 'option2', name: 'hkong', text: 'Fibre' },
{ value: 'option3', name: 'spore', text: 'Fat' }
]
}
}
}
</script>
Kindly assist since it's something new for me in VueJs.
This is working in VueJS (tested). I was a little off about how the b-form-select element works, but this will give you the results you need.
If you are building the options manually, maybe just make a JSON file or a Component that returns these sets of options and subOptions and import it rather than putting all the options into your template (Cleaner Code).
<script>
export default {
data () {
return {
option1Selected: null,
option2Selected: null,
options: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option1',
name: 'mys',
text:'Carbohydrates',
},
{
value: 'option2',
name: 'hkong',
text: 'Fibre',
},
{
value: 'option3',
name: 'spore',
text: 'Fat',
},
],
subOptions: {
option1: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option1-1',
name: 'o1-1Name',
text: 'Option 1-1 Text',
},
{
value: 'option1-2',
name: 'o1-2Name',
text: 'Option 1-2 Text',
},
{
value: 'option1-3',
name: 'o1-3Name',
text: 'Option 1-3 Text',
},
],
option2: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option2-1',
name: 'o2-1Name',
text: 'Option 2-1 Text',
},
{
value: 'option2-2',
name: 'o2-2Name',
text: 'Option 2-2 Text',
},
{
value: 'option2-3',
name: 'o2-3Name',
text: 'Option 2-3 Text',
},
],
option3: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option3-1',
name: 'o3-1Name',
text: 'Option 3-1 Text',
},
{
value: 'option3-2',
name: 'o3-2Name',
text: 'Option 3-2 Text',
},
{
value: 'option3-3',
name: 'o3-3Name',
text: 'Option 3-3 Text',
},
],
}
}
},
computed: {
option1text () {
for (const key in this.options) {
if (this.options[key].value === this.option1Selected) {
return this.options[key].text;
}
}
},
option2text () {
if (this.option1Selected != null) {
for (const key in this.subOptions[this.option1Selected]) {
if (this.subOptions[this.option1Selected][key].value === this.option2Selected) {
return this.subOptions[this.option1Selected][key].text;
}
}
}
}
},
}
</script>
<template>
<div>
<panel class="main-content">
<div>
<b-form-select v-model="option1Selected" :options="options" class="mb-3" />
<div>Selected: <strong>{{ option1text }}</strong></div>
</div>
<div v-if="option1Selected != null">
<b-form-select v-model="option2Selected" :options="subOptions[option1Selected]" class="mb-3" />
<div>Selected: <strong>{{ option2text }}</strong></div>
</div>
</panel>
</div>
</template>
Note I have updated code to properly reflect exactly what you were asking for in the question: the Text value of the option.

Vue.js vuetify i18n : How to translate dynamically the Toolbar items?

I don't have any problem in localizing the components and views strings but I am lock into finding a way to localize dynamically the Toolbar items ( and of course the same items in the navigation drawer..
Currently they are displayed in App.vue as menuItems[i].title
<v-toolbar-items class="hidden-xs-only">
<v-btn flat :to="menuItems[0].link">
<v-icon left>{{ menuItems[0].icon }}</v-icon>
<span>{{ menuItems[0].title }}</span>
</v-btn>
with the script:
<script>
export default {
data () {
return {
appName: 'myAPP',
sideNav: false,
menuItems: [
{ icon: 'home', title: 'Home', link: '/home' },
{ icon: 'info', title: 'About', menu: [{ title: 'Company', link: '/company' }, { title: 'Office', link: '/office' }] },
{ icon: 'people', title: 'Members', menu: [], link: '/members' },
{ icon: 'local_library', title: 'Blog', link: '/blog' },
{ icon: 'local_grocery_store', title: 'Shopping', link: '/shopping' }
]
}
},
methods: {
switchLocale: function (newLocale) {
this.$store.dispatch('switchI18n', newLocale)
}
}
}
</script>
Should I use a computed value ? or use directly $t() in the template ?
feedback, advices and links appreciated
UPDATE
main.js
Vue.filter('translate', function (value) {
if (!value) return ''
value = 'lang.views.global.' + value.toString()
return i18n.t(value)
})
locales/i18n/en_US
{
"views": {
"global": {
"Home": "Home",
"Section1": "Section 1",
..
Vue provides filter to help us to format the common text.
So I think it will be one of your choices.
You can click above link to follow the guide to set up your filters.
Edit:
I just realized Vue-filters should not be dependent on this context as the Vue author said. So updated my answer as below:
Then the codes will be like below:
// create vue-i18n instance
const i18n = new VueI18n({
locale: getDefaultLanguage(),
messages: langs
})
// create global filter
Vue.filter('myLocale', function (value) {
return i18n.t(value)
})
In your views or components:
<template>
<v-toolbar-items class="hidden-xs-only">
<v-btn flat :to="menuItems[0].link">
<v-icon left>{{ menuItems[0].icon }}</v-icon>
<span>{{ menuItems[0].title | myLocale }}</span>
</v-btn>
</template>
<script>
export default {
data () {
return {
appName: 'myAPP',
sideNav: false,
menuItems: [
{ icon: 'home', title: 'Home', link: '/home' },
{ icon: 'info', title: 'About', menu: [{ title: 'Company', link: '/company' }, { title: 'Office', link: '/office' }] },
{ icon: 'people', title: 'Members', menu: [], link: '/members' },
{ icon: 'local_library', title: 'Blog', link: '/blog' },
{ icon: 'local_grocery_store', title: 'Shopping', link: '/shopping' }
]
}
},
filters: {
myLocaleWhichNotWork: function (value) {
return this.$t(value) // this won't work because filters should not be dependent on this context
}
}
}
</script>