Enable/Disable Button based on selected value - vue.js

I have a button in my view
<v-menu offset-y>
<v-btn>
Action Items
</v-btn>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
:disabled="item.disabled"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
<v-data-table
v-model="selected">
my data looks like
<script>
export default {
data: () => ({
selected: [],
items: [
{ title: 'Delete',disabled:false},
],
...
i am trying to enable or disable the v-list-tile based on whether the selected array has any values.
i tried something like:
items: [
{ title: 'Delete',disabled:this.selected.length=0},
],
but it gives me the following error:
[Vue warn]: Property or method "selected" is not defined on the instance but referenced during render.
please help me to resolve this issue.

I would say rather than have a disabled property on your model, you can have it as a computed property like so:
computed: {
disabled() {
return this.selected.length < 1; // or === 0
}
}
Then use the disabled property in your component.
<v-list-tile v-for="(item, index) in items"
:key="index"
:disabled="disabled">
<v-list-tile-title>
{{ item.title }}
</v-list-tile-title>
</v-list-tile>
PS: that props is passed into your v-list-tile as disabled property of that element. I am not sure if a custom component will be disabled (otherwise you know you'll use it on a real html element)

Related

Vuetify How to update component data when list item group select changed

I'm using v-list-item-group and I want to show data in another component when the list item is selected. clear data when I unSelect item, and change data when I click on another list item
how can I possibly do it in vue?
the list item which I select:
Here I want to clear curr step data if the list.eid changed or when index changed
<v-list-item-group v-model="wfs">
<v-list-item v-for="(list,index) in workflowStepsList" :key="index"
#click="getWorkflowStep(list.eid)">
<v-list-item-action-text class="pe-4"> {{ index+1 }}</v-list-item-action-text>
<v-list-item-content v-if="!list.title">
{{ list.stepTitle }}
</v-list-item-content>
<v-list-item-content v-if="!list.stepTitle">
{{ list.title }}
</v-list-item-content>
<v-list-item-icon>
<v-icon small color="red">mdi-delete</v-icon>
</v-list-item-icon>
</v-list-item>
<v-list-item v-if="!workflowStepsList.length">
مرحله ای وجود ندارد
</v-list-item>
</v-list-item-group>
and the list I render data based on what I selected:
<v-card>
<v-card-title class="bg-success text-white d-flex justify-space-between">
مرحله فعلی
<add-curr :getSteps="getSteps"/>
</v-card-title>
<v-card-text>
<v-list>
<v-list-item-group class="v-list-item-group" v-model="stepId">
<v-list-item
v-if="!currStep.length"
class="text-muted"
>
یک مرحله انتخاب کنید
</v-list-item>
<v-list-item
v-for="(element) in currStep"
:key="element.eid"
v-show="element.eid !== null"
>
{{ element.title }}
</v-list-item>
</v-list-item-group>
</v-list>
</v-card-text>
</v-card>
the function:
async getWorkflowStep(weid) {
await this.$store.dispatch("axiosGet",
{url: `folder/api/workflow-steps/${weid}`, params: {workflowId: this.id}}).then(response => {
if (response.status === 'error') return
this.workflowStepsObj = response.data.data
const x = response.data.data
const curr = {
title: x.stepTitle,
eid: x.stepEid
}
this.currStep.push(curr)
})
},
I just added an another component and passing the model value as a prop in that component.
Live Demo :
Vue.component('another-component', {
props: ['val'],
template: '<h3>{{ val }}</h3>'
});
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{ text: 'Inbox' },
{ text: 'Star' },
{ text: 'Send' },
{ text: 'Drafts' }
],
model: 1,
}),
});
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.12/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.12/dist/vuetify.min.css"/>
<div id="app">
<v-list>
<v-list-item-group v-model="model">
<v-list-item v-for="(item, i) in items" :key="i">
{{ item.text }}
</v-list-item>
</v-list-item-group>
</v-list>
<another-component :val="items[model].text"></another-component>
</div>
You can either use store value and watch it from the component you want to track the change (it is possible to watch store properties if they change). If using vuex: https://vuex.vuejs.org/api/#watch
Or you can use emits and listen to the change on desired component, if there's depth issue for the emit you could pass emits on top level using (v-on="$listeners"): https://v2.vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
Since there's no statement about Vue or Vuetify version, inferring it is the 2nd version.
Sometimes when using Vuetify or UI libraries we may forget the features that Vue provides. Despite Vuetify having built-in features most probably they can be overridden by implementing yours on top of them.

Remove active class from v-list-item when v-list-group item is selected

Using Vuetify v-list-item directive (outside) with others v-list-item inside the v-list-group for routing website's pages I'm encountering this issue:
The routing works fine, but every time I click on one of the submenues the index item is still active
When Index is selected
When another item is selected
I want to know if there is something I can do to deactivate the active class of index menu v-list-item when I select a v-list-item inside the v-list-group
Here is my code:
DOM:
<v-navigation-drawer v-model="drawer" app dark>
<v-list>
<div
v-for="item in pages"
:key="item.title"
>
<!-- Index -->
<v-list-item
v-if="!item.items"
link
:to="item.href"
v-on="on"
>
<v-list-item-icon>
<v-icon color="info">
{{item.icon}}
</v-icon>
</v-list-item-icon>
<v-list-item-title class="info--text" v-text="item.title">
</v-list-item-title>
</v-list-item>
<!-- group menues -->
<v-list-group
v-if="item.items"
item-color="primary"
no-action
>
<template v-slot:activator>
<v-icon color="info" style="padding-right:32px">
{{item.icon}}
</v-icon>
<v-list-item-content>
<v-list-item-title class="info--text" v-text="item.title"></v-list-item-title>
</v-list-item-content>
</template>
<v-list-item
item-color="primary"
v-for="child in item.items"
:key="child.title"
link
:to="child.href"
style="padding-left:40px"
>
<v-list-item-icon>
<v-icon color="info">mdi-circle-medium</v-icon>
</v-list-item-icon>
<v-list-item-content v-on="on">
<v-list-item-title class="info--text" v-text="child.title"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-group>
</div>
</v-list>
</v-navigation-drawer>
Script:
data: () => ({
drawer: true,
pages: [
{
icon: 'mdi-home',
title: 'Index',
href: '/main'
},
{
icon: 'mdi-bell',
items: [
{
title: 'submenu1',
href: '/main/menu1/submenu1'
},
{
title: 'submenu2',
href: '/main/menu1/submenu2'
}
],
title: 'menu1'
},
{
icon: 'mdi-cog-outline',
items: [
{
title: 'submenu3',
href: '/main/menu2/submenu3'
}
],
title: 'menu2'
},
{
icon: 'mdi-text-box-outline',
items: [
{
title: 'submenu4',
href: '/main/menu3/submenu4'
}
],
title: 'menu3'
}
] })
Thank you!
I found out what the problem was. I was misunderstanding the concept of routing using Vue.
The index item was routing to /main (/main/index), and the others pages included 'main' on their routes, like so: /main/submenu1. Vue understood /main/submenu1 as if it was over main, so /main was "active" too as it contained the same name in the route.
I fixed this by changing the /main route to /main/home. By forcing the route path to be different from the others. I tried using /main/index because Vue was pointing to index the whole time but it didn't work. I just needed to change the page name and the route path.

How do I overcome error 'Avoid using JavaScript keyword as "v-on" value'

How do I get rid of this error in my App.vue component?
Failed to compile.
./src/App.vue
Module Error (from ./node_modules/eslint-loader/index.js):
C:\xampp2\htdocs\exchproto\src\App.vue
15:69 error Avoid using JavaScript keyword as "v-on" value: "" vue/valid-v-on
✖ 1 problem (1 error, 0 warnings)
I tested the whole code (including sub-components) with CDN pull of vuejs/vuetify css/js etc and all worked well. This error only comes up as I am breaking down the code into components and doing everything by "yarn install". Been poking at it for some time with no luck.
<template>
<v-app>
<v-app-bar app>
<v-app-bar-nav-icon #click="drawer = !drawer"></v-app-bar-nav-icon>
<v-spacer></v-spacer>
<v-menu :offset-y="true">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on">
<v-icon>mdi-dots-horizontal</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item v-for="(item, i) in rightMenuitems" :key="i" #click="">
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<!--router-view></!--router-view-->
</v-app>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
return {
drawer: false,
rightMenuitems: [
{ title: 'Choice 1' },
{ title: 'Choice 2' },
{ title: 'Choice 3' },
{ title: 'Choice 4' }
],
}
}
}
</script>
You can't have an empty #click="" You must have the click point to a function:
<v-list-item
v-for="(item, i) in rightMenuitems"
:key="i"
#click="doSomething($event, i)"
>
<v-list-item-title>
{{ item.title }}
</v-list-item-title>
</v-list-item>
// ...
methods: {
someFunction(event, i) {
console.log("clicked item " + i);
}
}
// ...
Remove 'click event' and add to property in v-list-item where v-for is being used.
Note:
You can bind links with the to props, and also links in every objects of rightMenuitems.
Here is an example:

Problem when creating a menu by iteration

I'm new to vue and vuetify. I need to create a submenu and for that I am using v-menu. Its construction is by iteration, where I need each sub menu to assign it a method. But it turns out that the way I'm doing it generates an error
[Vue warn]: Error in v-on handler: 'TypeError: handler.apply is not a function'
. What am I doing wrong?
https://codepen.io/maschfederico/pen/vMWBPV?editors=1011
<div id="app">
<v-app id="inspire">
<div class="text-xs-center">
<v-menu>
<template #activator="{ on: menu }">
<v-tooltip bottom>
<template #activator="{ on: tooltip }">
<v-btn
color="primary"
dark
v-on="{ ...tooltip, ...menu }"
>Dropdown w/ Tooltip</v-btn>
</template>
<span>Im A ToolTip</span>
</v-tooltip>
</template>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="item.f"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Click Me1',f:'login'},
{ title: 'Click Me2',f:'login' },
{ title: 'Click Me3',f:'login' },
{ title: 'Click Me4' ,f:'login' }
]
}),
methods: {
login(){console.log('login')}
}
})
Try to pass the method name to another one and handle it inside the last one like :
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="handle(item.f)"
>
inside the methods :
methods: {
handle(f){
this[f]();
},
login(){console.log('login')}
}
check this codepen
You are passing the method's name - a string - instead of a function. The click event listener generated by vue is trying to call a function using apply, this is why you are getting that error.
One solution would be to pass directly the function when the Vue instance is created (before that, the method might not be available, so passing it directly to the data { title: 'Click Me1', f: this.login } would not work).
For example, you could keep having method names in the data, and replace them with the actual methods at create:
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Click Me1', f: 'login' }
]
}),
created () {
this.items.forEach(item => {
item.f = this[item.f]
})
},
methods: {
login (){
console.log('login')
}
}
})

VueJS Bind prop only if data variable is true

How can I bind a property only when a variable is set to true?
<template>
<v-list-tile :class="{[$color]: isItemSelected, [$primaryText]: isItemSelected}" :href="route">
<v-list-tile-action>
<v-icon :color="$primaryIcon">{{ icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{ title }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
<script>
export default {
name: 'list-tile-text-icon',
props: ['icon', 'title', 'route'],
data: () => ({
isItemSelected: false
}),
created() {
this.isItemSelected = window.location.href === this.route;
}
}
</script>
On line 4 I need to bind in :color="$primaryColor" only when the isItemSelected variable is equal to true.
The values used in v-bind are JavaScript expressions, thus you can use the Conditional (ternary) operator:
<v-icon :color="isItemSelected ? $primaryIcon : null">
Note: I'm calling it v-bind because : is just a shorthand to v-bind. I.e. :color="" is the same as v-bind:color=""
Vue class and style binding doc
this is the offical document about vue class and style binding,
detail:
<v-list-title :class="{someClassYouDefine: isItemSelected}"> </v-list-title>
then write the style you want
<style>
.someClassYouDefine {
color: $primaryColor;
}
</style>