The docs say
<a #click="doSomething"> ... </a>
is short-hand for
<a v-on:click="doSomething"> ... </a>
Why does this short-hand not work in all cases? For example,
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on }">
<v-btn dark v-on="on">Working button</v-btn>
<v-spacer />
<v-btn dark #on="on">Non-working button</v-btn>
</template>
<v-card>
<v-card-title>Some Dialog</v-card-title>
<v-card-text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</v-card-text>
</v-card>
</v-dialog>
The "Working button" works, but the "Non-working button" doesn't. It seems like the v-on shorthand only works for events. But if that's true, I haven't seen it documented.
I'm using Vuetify in this example, but my question is about Vue.js in general.
Here's a CodePen of above: https://codepen.io/keeganwitt/pen/bGGVeZY
v-on:click short hand is #click not #on
so you can use #click instead of v-on="on". But here slotProp on is an Object with click (function) property. so you can use it like below
<v-btn dark #click="on.click">Now working button</v-btn>
but it is recommended to use v-on="on" since there could be multiple properties under on Object
Ex: if on is { click: fn(), mouseup: fn() }
<v-btn dark #click="on.click" #mouseup="on.mouseup" >Now working button</v-btn>
<!-- same as -->
<v-btn dark v-on="on">Now working button</v-btn>
So, #on in your example is shorthand for v-on:on, meaning that the event handler is listening for the child component to emit an on event. The v-btn component does not ever emit an on event, so nothing ever happens.
This isn't the thrust of your question, but you technically can use the v-on shorthand to bind to an on event:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('toggle', {
template: `
<button #click="onClick">
{{ isOn ? 'On' : 'Off' }}
</button>
`,
data() {
return {
isOn: false
};
},
methods: {
onClick() {
this.isOn = !this.isOn;
if (this.isOn) {
this.$emit('on');
} else {
this.$emit('off');
}
}
}
});
new Vue({
el: '#app',
methods: {
alert(val) {
alert(val);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<toggle #on="alert('on')" #off="alert('off')"/>
</div>
I think what is confusing you is the v-on="on" syntax. It's not obvious from the code, but the on variable being set as the value for v-on in this case is not a function, but an object.
From the vue docs on v-on:
Starting in 2.4.0+, v-on also supports binding to an object of event/listener pairs without an argument. Note when using the object syntax, it does not support any modifiers.
So the on variable in this case probably looks something like this:
{ click: onClick, mouseover: onMouseover }
Passing all of the event listeners this way allows for less boilerplate code. The way you would need to do this before v2.4 was like this:
<template v-slot:activator="{ onClick, onMouseover }">
<v-btn
dark
#click="onClick"
#mouseover="onMouseover"
>
Working button
</v-btn>
</template>
Related
I created a simple v-dialog using Vuetify. It uses v-slot:activator with destructured data, namely {on, attrs }. While I can understand on part then I can't get what's the purpose of attrs? If I remove it then dialog still works fine. How it works and what is it for?
Here some basic example from Vuetify docs:
<template>
<div class="text-center">
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
color="red lighten-2"
dark
v-bind="attrs"
v-on="on"
>
Click Me
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 grey lighten-2">
Privacy Policy
</v-card-title>
<v-card-text>
Lorem ipsum dolor sit amet...
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data () {
return {
}
},
}
</script>
Its purpose is to give you set of attributes/props that you can easily bind with v-bind="attrs" (using object binding syntax) to the component you choose as an activator
In the case of v-dialog its content is generated by default implementation of activatable mixin and generates some ARIA attribute but the component using the mixin can override or extend it (as for example v-menu does)
How can you set a boolean to true when the mouse click is held down, and false when you release the click? On Vue I am using v-on:mousedown="hold = !hold" But this toggles the boolean instead of having the user hold the click down.
You need to use the events mousedown and mouseup. Try something like this:
<template>
<div>
<div :style="{ 'background-color': color }">
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</div>
<button #mousedown="color = 'orange'" #mouseup="color = 'green'" >Click here</button>
</div>
</template>
<script>
export default {
name: 'App',
data: () => ({
color: 'yellow',
}),
};
</script>
Use both mousedown and mouseup events.
<div
v-on:mousedown="hold = true"
v-on:mouseup="hold = false"
>{{hold}}</div>
How can I mutate a prop the correct way, so that I don't get the [Vue warn]: Avoid mutating a prop directly message?
I already got the v-model to work on this v-dialog. However, I also want to provide a close button in the dialog itself, which causes this mutation warning, as it's the dialog itself that's mutating the variable. How best to approach this case and solve it?
Dialog.vue:
<template>
<v-dialog
:value="value" #input="$emit('input', $event)"
scrollable
width="80vw"
:transition="false"
>
<template v-slot:activator="{ on }">
<div v-on="on" #click="$emit('open')">
<slot name="button">
<v-btn color="primary">{{ buttonText == null ? title : buttonText }}</v-btn>
</slot>
</div>
</template>
<v-card
elevation="10"
height="80vh"
>
<v-system-bar
color="light-blue darken-3"
window
>
<span>{{title}}</span>
<v-spacer></v-spacer>
<v-icon #click="value=false">mdi-close</v-icon>
</v-system-bar>
<v-card-text> <!-- required here to make the scrollable v-dialog work -->
<slot></slot>
</v-card-text>
<slot name="actions"></slot>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
buttonText: String,
value: Boolean,
},
}
</script>
I can use it quite nicely like:
<mydialog title="Select something" button-text="A button!" v-model="dialog" #open="loadData()">
Content goes here...
</mydialog>
The <v-icon #click="value=false">mdi-close</v-icon> is what's incorrectly mutating the "value"-variable.
Sidenote #1: The open event is there so I can populate data (loadData) from a database when the dialog is opened (vs. when its created on the DOM).
UPDATE; I can get it to work by doing:
<v-icon #click="$emit('close', $event)">mdi-close</v-icon>
and
<mydialog title="Select something" button-text="A button!" v-model="dialog" #open="loadData()" #close="dialog=false">
However, I feel this is far from being elegant. Aren't there any solutions in where I don't need to add on-Handlers to close this dialog? I almost feel that this is worse than living with the warning.. :|
Use a computed property with get and set. The dialog computed will behave exactly as a normal variable and it will eliminate the warning. Now you can use it to get the value and also to set the value.
Try this:
<template>
<v-dialog
:value="dialog"
#input="$emit('input', $event)"
scrollable
width="80vw"
:transition="false"
>
<template v-slot:activator="{ on }">
<div v-on="on" #click="dialogOpened()">
<slot name="button">
<v-btn color="primary">{{ buttonText == null ? title : buttonText }}</v-btn>
</slot>
</div>
</template>
<v-card elevation="10" height="80vh">
<v-system-bar color="light-blue darken-3" window>
<span>{{title}}</span>
<v-spacer></v-spacer>
<v-icon #click="dialog=false">mdi-close</v-icon>
</v-system-bar>
<v-card-text>
<!-- required here to make the scrollable v-dialog work -->
<slot></slot>
</v-card-text>
<slot name="actions"></slot>
</v-card>
</v-dialog>
</template>
export default {
props: ['title', 'buttonText', 'value'],
data: () => ({
dlg_close: false,
}),
computed: {
dialog: {
get() {
return this.value;
},
set(selection) {
this.$emit("input", selection);
}
}
},
methods: {
dialogOpened(newVal) {
this.dialog = newVal;
},
},
}
Working with VueJS and trying to style some text based on Vuetify's viewport breakpoints I have achieved this by binding the style to a condition like this:
:style="$vuetify.breakpoint.name === 'xs' ? { 'font-size': '1.5rem !important' }: { 'font-size': '2.5rem !important' }"
however I would like to use computed property instead just to make it cleaner and according to Vuetify's docs this can be achieved using breakpoint object however I can't get it to work for some reason
I've looked at this discussion and trying to copy the answer from #raina77ow but not too sure what I'm doing wrong.
below is my code; I'm trying to style the h3 element inside v-card-title
<template>
<div>
<section>
<v-layout>
<v-flex xs12 sm10 offset-sm1>
<v-card flat width="auto">
<v-card-title primary-title>
<h3
class="text-xs-center headline mb-0"
:style="fontSize"
>
Some Header here
</h3>
<div class="text-xs-center pa-5 mx-5">{{ card_text }}</div>
</v-card-title>
</v-card>
</v-flex>
</v-layout>
</section>
</div>
</template>
<script>
export default {
computed: {
fontSize() {
switch (this.$vuetify.breakpoint.name) {
case "xs":
return "1.5rem !important";
default:
return "3rem !important";
}
}
},
data() {
return {
card_text:
"Lorem ipsum dolor sit amet, brute iriure accusata ne mea."
};
}
};
</script>
Looking at Vuejs devtool I can see the computed property value changing as intended but just can't figure out why it's not being applied to CSS
Can someone tell me what am I doing wrong please!
Looks like the computed property isn't returning a full style specification. So you could either change the computed function
case "xs":
return {"font-size": "1.5rem !important"};
default:
return {"font-size": "3rem !important"};
or change how it's being used
:style="{'font-size': fontSize}"
I understand how to use Vuetify's v-tooltip with the tooltip wrapping the component. However, I'm not quite sure how to have the activator button outside.
e.g. I have this (non-working code):
<v-tooltip bottom
:activator="$refs.filterBtn"
>
Filter displayed items
</v-tooltip>
<v-btn
ref="filterBtn"
icon
#click="isFilter = !isFilter"
>
<v-icon>fa-filter</v-icon>
</v-btn>
I also tried using prop activator without the v-bind:, same result
Idea: I want the button to be placed separately from tooltip in order to run unit tests. When testing, shallowMount strips anything inside <v-tooltip> so I can't test the button. The problem is I don't know how to make tooltip show up on hover (just like it does when wrapped), I do not want to trigger it with #click.
EDIT: here's codepen
How about using the v-hover UI Component. Wrap it around your button. Bind a boolean variable to the v-hover using v-model, call it buttonHovering. Then bind a boolean variable to the v-tooltip using v-model, call it showToolTip. Then use a watcher to toggle showToolTip true and false based on the value of buttonHovering. Or you can make showToolTip a computed property that always returns the value of buttonHovering. Lastly, bind the disabled attribute of the v-tooltip to the !buttonHovering property to ensure that the tooltip only displays when hovering over the button and not the tooltip's activator.
new Vue({
el: '#app',
data () {
return {
buttonHovering: false,
showToolTip: false
}
},
watch: {
buttonHovering (newVal) {
this.showToolTip = newVal
}
}
})
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.16/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-card>
<v-card-title>
<v-hover v-model="buttonHovering">
<v-btn large>
Hello
</v-btn>
</v-hover>
<v-spacer></v-spacer>
<v-tooltip left v-model="showToolTip" :disabled="!buttonHovering">
<span>Hi from over here!</span>
</v-tooltip>
</v-card-title>
</v-card>
</v-app>
</div>
Try this:
<v-tooltip bottom
v-model="filterBtnTTip"
>
Filter displayed items
</v-tooltip>
<v-btn
icon
#click="isFilter = !isFilter"
#mouseover="filterBtnTTip = true"
#mouseleave="filterBtnTTip = false"
>
<v-icon>fa-filter</v-icon>
</v-btn>
...
data () {
return {
...
filterBtnTTip: false
}
}
<v-app>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn
v-on="on"
>
Your button
</v-btn>
</template>
<span> Your hover message </span>
</v-tooltip>
</v-app>
In my case, it was necessary to place the tooltip together with the button in one component (a custom button with a tooltip function). Therefore, I used a prop activator and added the tooltip itself directly to the button:
<template>
<v-btn
:id="id"
>
<span>
<slot></slot>
<v-tooltip
v-if="tooltipText"
:activator="`#${id}`"
top
>
{{ tooltipText }}
</v-tooltip>
</span>
</v-btn>
</template>
<script>
let id = 0;
export default {
data() {
return {
id: `custom-button-${id++}`, //This is how we get a unique id for each created button
}
}
}
</script>
According to the same scheme, you can place v-tooltip anywhere, the main thing is that at the time of mounting the "activator" already exists.