<script>
export default {
data () {
return {
my_alert: false,
text: '',
color: 'success'
}
},
methods: {
openAlt (text, color) {
this.text = text
this.color = color
this.my_alert = true
}
}
}
</script>
<template>
<div>
<v-btn #click="openAlt('This is alert', 'error')">Click me!</v-btn>
<v-snackbar v-model="my_alert"
:timeout="2000"
:color="color"
top right>
{{ text }}
<v-btn icon ripple #click="my_alert = false">
<v-icon>close</v-icon>
</v-btn>
</v-snackbar>
</div>
</template>
I'm studying Vue.js and Vuetify. I wrote a code that shows alert after click on v-btn.
How can I reuse this alert outside of this page?
I want to modularise this code and use it for alerts for all of my pages.
Thanks for your answer.
I think creating a mixin works as well:
Say that you create alertMixin.js as below:
const alertMixin = {
data () {
return {
myAlert: false,
text: '',
color: 'success',
}
},
methods: {
openAlt (text, color) {
this.text = text;
this.color = color;
this.myAlert = true;
}
}
};
export default alertMixin;
And use it where ever you want like this:
<script>
import alertMixin from '#/path/to/mixin/alertMixin';
export default {
name: 'where-ever-you-want',
mixins: [alertMixin],
};
</script>
Hi welcome to vuetiful world of vue.
You can easily do that making the alert as an component and importing it where ever you want.
For any file where you want to use your alert code, you could just import your alert component and use it just like any other HTML component.
import alert from './path/to/component
<template>
<appAlert></appAlert>
</template>
<script>
components:{
appAlert: alert
}
</script>
There more you can do with components, Read about Vue components
I hope it helps.
Here's my new code.
App.vue
<template>
<v-app>
<v-content>
<router-view/>
</v-content>
<alt></alt>
</v-app>
</template>
<script>
export default {
name: 'App'
}
</script>
main.js
// ...
import alt from './components/alt'
Vue.prototype.$EventBus = new Vue()
Vue.config.productionTip = false
Vue.component('alt', alt)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
alt.vue
<script>
export default {
data () {
return {
my_alert: false,
text: '',
color: 'success'
}
},
created () {
this.$EventBus.$on('show_alt', (str, color) => {
var text = str
var clr = color
if (!text) text = '설정되지 않은 문구'
if (!clr) clr = 'error'
this.text = text
this.color = clr
this.my_alert = true
})
}
}
</script>
<template>
<div>
<v-snackbar v-model="my_alert"
:timeout="2000"
:color="color"
top right>
{{ text }}
<v-btn icon ripple #click="my_alert = false">
<v-icon>close</v-icon>
</v-btn>
</v-snackbar>
</div>
</template>
At last, altTest.vue
<template>
<v-btn #click="openAlt('This is alert', 'error')">Click Me!</v-btn>
</template>
<script>
export default {
data () {
return {
}
},
methods: {
openAlt (str, color) {
return this.$EventBus.$emit('show_alt', str, color)
}
}
}
</script>
I imported alt.vue to main.js as global, and it's added App.vue.
So, I can open alert in altTest.vue without import(but, it need a method openAlt()).
But, I think this is not simple..
Related
In my single page application, a screen consist of multiple components. I want to know is there any data has been modified/changed in the screen across all the component when we move to next screen.
For example, I am having Vue class "createProject.vue":
<template>
<Comments></Comments>
<ProjectSelection></ProjectSelection>
<MessageArea></MessageArea>
</template>
class Comments.vue,
<template>
<v-textarea
id="input--comments"
name="Comments"
></v-textarea>
</template>
class ProjectSelection.vue,
<template>
<div>
<v-text-field
id="input--email"
:label="Email"
></v-text-field>
</div>
</template>
class MessageArea.vue,
<template>
<v-textarea
id="input--message-area"
name="message"
></v-textarea>
</template>
When I move to the next screen, I want to know is there any data has been changed or not.
Kindly help me to identify the data changes across all the components.
In the root component:
<template>
<Comments #modified="dataUpdated = true"></Comments>
<ProjectSelection #modified="dataUpdated = true"></ProjectSelection>
<MessageArea #modified="dataUpdated = true"></MessageArea>
</template>
<script>
export default
{
data()
{
return {
dataModified: false,
nextRoute: null,
}
},
created()
{
window.addEventListener('beforeunload', this.pageLeave);
},
beforeDestroy()
{
window.removeEventListener('beforeunload', this.pageLeave);
},
beforeRouteLeave(to, from, next)
{
this.routeLeave(to, from, next);
},
methods:
{
routeLeave(to, from, next)
{
if(this.dataModified)
{
this.nextRoute = next;
// show dialog to the user that there is unsaved data - it might call this.ignoreUnsaved
}
},
ignoreUnsaved()
{
// hide the dialog
if (this.nextRoute) this.nextRoute();
},
pageLeave(e)
{
if(this.dataModified)
{
const confirmationMessage = 'There is unsaved data. Ignore it and continue?';
(e || window.event).returnValue = confirmationMsg;
return confirmationMessage;
}
},
}
}
</script>
In Comments.vue:
<template>
<v-textarea v-model.trim="newComment" />
</template
<script>
export default
{
data()
{
return {
trackThisForChanges:
{
newComment: '',
},
}
},
watch:
{
trackThisForChanges:
{
deep: true,
handler()
{
this.$emit('modified');
}
}
}
}
I'm searching to trigger event from a vue instance which is encapsulated inside a shadowDOM.
For the moment, my code create a new Vue instance inside a shadowDOM and print the template without the style (because nuxt load the style inside the base vue instance).
I can call methods of each instance
But, when i'm trying to add an event / listener on my component, it doesn't work.
DOM.vue
<template>
<section ref='shadow'></section>
</template>
<script>
import bloc from '#/components/bloc.vue'
import Vue from 'vue'
export default {
data() {
return {
vm: {},
shadowRoot: {},
isShadowReady: false
}
},
watch: {
isShadowReady: function() {
let self = this
this.vm = new Vue({
el: self.shadowRoot,
components: { bloc },
template: "<bloc #click='listenClick'/>",
methods: {
listenClick() { console.log("I clicked !") },
callChild() { console.log("I'm the child !") }
},
created() {
this.callChild()
self.callParent()
}
})
}
},
mounted() {
const shadowDOM = this.$refs.shadow.attachShadow({ mode: 'open' })
this.shadowRoot = document.createElement('main')
shadowDOM.appendChild(this.shadowRoot)
this.isShadowReady = true
},
methods: {
callParent() {
console.log("I'am the parent")
},
}
}
</script>
bloc.vue
<template>
<div>
<p v-for='word in words' style='text-align: center; background: green;'>
{{ word }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
words: [1,2,3,4,5,6]
}
}
}
</script>
Any idea ?
Thank you
Hi so after i watched at your code i think it's better to use the $emit to pass event from the child to the parent and i think it's more optimized then a #click.native
template: "<bloc #someevent='listenClick'/>",
<template>
<div #click="listenClickChild">
<p v-for='(word, idx) in words' :key="idx" style='text-align: center; background: green;'>
{{ word }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
words: [1,2,3,4,5,6]
}
},
methods: {
listenClickChild() {
this.$emit('someevent', 'someValue')
}
}
}
</script>
bloc #click.native='listenClick'/>
here's my code:
<v-btn #click="showDialog = true" >
<v-dialog
v-model="showDialog"
max-width="600"
>
<v-btn #click="showDialog = false">save</v-btn>
</v-dialog>
</v-btn>
I know that this is fairly simple code, but I would like to extract whole v-dialog to a component. I don't know how to manage showDialog. Should I use prop? I now I shouldn't modify prop from inside of the component. What's the other way?
You can use a function as a prop for changing value of the showDialog in the another component . ( To avoid getting error avoid mutating a prop directly )
exampleComponent.vue
<template>
<v-dialog v-model="showDialog" max-width="600">
<v-btn #click="hideDialog">save</v-btn>
</v-dialog>
</template>
<script>
export default {
props: {
showDialog : Boolean ,
hideDialog: Function,
},
};
</script>
mainFile.vue
<template>
<example :hideDialog="hideMethod" :showDialog="showDialog"></example>
</template>
<script>
export default {
data() {
return {
showDialog : false
}
},
methods: {
hideMethod() {
this.showDialog = false;
},
},
};
</script>
I've never been a fan of driving the visibility of a dialog with a reactive v-model value. Normally, dialogs need to do some amount of setup and state related things before displaying and before hiding.
So, what I do is I move showDialog to be a hidden internal value to the component itself, I put a ref= on the component, I implement an open() method on the component and call that when I want to show it.
This pattern feels more natural when a dialog is performing more complicated tasks than just showing static information.
So in your case:
<script id="myDialog" type="text/x-template">
<v-dialog
v-model="showDialog"
max-width="600"
>
<v-btn #click="save">save</v-btn>
</v-dialog>
</script>
[...]
<v-btn #click="openMyDialog">
<myDialog ref="myDialog">
</myDialog>
</v-btn>
On myDialog:
data: function () {
return {
[ other attributes ]
showDialog: false
}
},
methods: {
[ other methods ]
open: function (initializationData) {
[ initialization code ]
this.showDialog = true;
},
save: function (event) {
[ save code ]
this.showDialog = false;
}
}
On the parent component:
methods: {
[ other methods ]
openMyDialog: function (event) {
this.$refs.myDialog.open([ initialization data ]);
}
}
How can a Vue component click a button or check a checkbox to change tooltip text and button color using a function?
In the following code, these actions are being handled by the handle() function, but the tooltip text and button color are not being updated.
Any help would be greatly appreciated.
The Vue component:
<template>
<div class="text-center">
<b-button v-bind:v-b-tooltip.hover.right=tooltipText v-bind:variant=color #click="handler(state)">
{{ username }}
</b-button>
</div>
</template>
<script>
import EventBus from '../eventBus.js'
export default {
props: ['username', 'checkbox1'],
data() {
return {
tooltipText: null,
color: null,
user: null,
state: null,
user: this.username
}
},
methods: {
handler(bool) {
if (!this.state == null){
this.state = !this.state
}
if (!this.bool){
EventBus.$emit('handleUser', this.user)
this.color = 'outline-sucess'
this.state = false
this.tooltipText = 'block'
return
} else {
EventBus.$emit('handleUser', this.user)
this.color = 'outline-danger'
this.tooltipText = 'unblock'
this.state = true
return
}
},
},
created() {
this.handler(this.checkbox1);
},
watch: {
checkbox1() {
this.handler(this.checkbox1)
}
},
}
</script>
<style scoped>
.active {
color: red;
}
</style>
It looks like you have typos in your component. Those attributes are missing quotes ="":
<b-button
v-bind:v-b-tooltip.hover.right="tooltipText"
v-bind:variant="color"
#click="handler(state)"
>
{{ username }}
</b-button>
Just try to display the values outside of the button component and see if they update when you press the button:
<div>
{{tooltipText}}
{{variant}}
{{color}}
</div>
I have created the following component that wraps Vuetify VHover, VTooltip and VBtn to simplify my app.
<template>
<div>
<v-hover v-if="tooltip">
<v-tooltip
slot-scope="{ hover }"
bottom
>
<v-btn
slot="activator"
:theme="theme"
:align="align"
:justify="justify"
:disabled="disabled"
:depressed="type === 'depressed'"
:block="type === 'block'"
:flat="type === 'flat'"
:fab="type === 'fab'"
:icon="type === 'icon'"
:outline="type === 'outline'"
:raised="type === 'raised'"
:round="type === 'round'"
:color="hover ? colorHover : color"
:class="{ 'text-capitalize': label, 'text-lowercase': icon }"
:size="size"
#click="onClick()"
>
<span v-if="label">{{ label }}</span>
<v-icon v-else>{{ icon }}</v-icon>
</v-btn>
<span>{{ tooltip }}</span>
</v-tooltip>
</v-hover>
<v-hover v-else>
<v-btn
slot-scope="{ hover }"
:theme="theme"
:align="align"
:justify="justify"
:disabled="disabled"
:depressed="type === 'depressed'"
:block="type === 'block'"
:flat="type === 'flat'"
:fab="type === 'fab'"
:icon="type === 'icon'"
:outline="type === 'outline'"
:raised="type === 'raised'"
:round="type === 'round'"
:color="hover ? colorHover : color"
:class="{ 'text-capitalize': label, 'text-lowercase': icon }"
:size="size"
#click="onClick()"
>
<span v-if="label">{{ label }}</span>
<v-icon v-else>{{ icon }}</v-icon>
</v-btn>
</v-hover>
</div>
</template>
<script>
import VueTypes from 'vue-types'
export default {
name: 'v-btn-plus',
props: {
align: VueTypes.oneOf(['bottom', 'top']),
justify: VueTypes.oneOf(['left', 'right']),
color: VueTypes.string.def('primary'),
colorHover: VueTypes.string.def('secondary'),
disabled: VueTypes.bool.def(false),
icon: VueTypes.string,
label: VueTypes.string,
position: VueTypes.oneOf(['left', 'right']),
tooltip: VueTypes.string,
size: VueTypes.oneOf(['small', 'medium', 'large']).def('small'),
theme: VueTypes.oneOf(['light', 'dark']),
type: VueTypes.oneOf(['block', 'depressed', 'fab', 'flat', 'icon', 'outline', 'raised', 'round']).def('raised')
},
methods: {
onClick() {
this.$emit('click')
}
},
created: function() {
// Workaround as prop validation on multiple props is not possible
if (!this.icon && !this.label) {
console.error('[Vue warn]: Missing required prop, specify at least one of the following: "label" or "icon"')
}
}
}
</script>
<style scoped>
</style>
I want to test VHover and VTooltip and have defined the following spec file.
import { createLocalVue, mount } from '#vue/test-utils'
import Vuetify from 'vuetify'
import VBtnPlus from '#/components/common/VBtnPlus.vue'
describe('VStatsCard.vue', () => {
let localVue = null
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuetify)
})
it('renders with default settings when only label is specified', async () => {
const label = 'Very cool'
const defaultColor = 'primary'
const defaultType = 'raised'
const defaultSize = 'small'
const wrapper = mount(VBtnPlus, {
localVue: localVue,
propsData: { label }
})
expect(wrapper.text()).toMatch(label)
expect(wrapper.html()).toContain(`${defaultType}="true"`)
expect(wrapper.html()).toContain(`size="${defaultSize}"`)
expect(wrapper.html()).toContain(`class="v-btn theme--light ${defaultColor} text-capitalize"`)
expect(wrapper.html()).not.toContain(`v-icon"`)
wrapper.find('button').trigger('mouseenter')
await wrapper.vm.$nextTick()
const btnHtml = wrapper.find('.v-btn').html()
expect(btnHtml).toContain('secondary')
expect(btnHtml).not.toContain('primary')
const tooltipId = btnHtml.match(/(data-v-.+?)(?:=)/)[1]
const tooltips = wrapper.findAll('.v-tooltip_content')
let tooltipHtml = null
for (let tooltip of tooltips) {
const html = tooltip.html()
console.log(html)
if (html.indexOf(tooltipId) > -1) {
tooltipHtml = html
break
}
}
expect(tooltipHtml).toContain('menuable_content_active')
})
})
The wrapper.find('button').trigger('mouseenter') does not work as expected. When I look at the html code after the nextTick it is the same as before trigger was called. It looks like I'm missing a part of the html. I was excpecting to see the following html.
<div data-v-d3e326b8="">
<span data-v-d3e326b8="" class="v-tooltip v-tooltip--bottom">
<span>
<button data-v-d3e326b8="" type="button" class="v-btn v-btn--depressed theme--light orange text-lowercase" size="small">
<div class="v-btn__content"><i data-v-d3e326b8="" aria-hidden="true" class="v-icon mdi mdi-account theme--light"></i></div>
</button>
</span>
</span>
</div>
All I'm getting is the <button> part.
Any suggestions how to get this to work?
Explicitly triggering mouseenter events doesn't normally have an effect, likely because browsers ignore simulated/non-trusted events. Instead, you could set v-hover's value to true to force the hover state:
VBtnPlus.vue
<template>
<div>
<v-hover :value="hovering">...</v-hover>
</div>
</template>
<script>
export default {
data() {
return {
hovering: false,
}
},
//...
}
</script>
VBtnPlus.spec.js
it('...', async () => {
wrapper.vm.hovering = true;
// test for hover state here
})
I just ran into this myself using Nuxt, Vuetify, and Jest. I followed
this example for Vuetify 2.x.
Below is a very simple example of what I did with my code.
As a side note, when I tried to set the wrapper.vm.[dataField] = [value] directly, Jest threw an error not allowing direct set access on the object. In the example.spec.js below, calling wrapper.setData({...}) will allow you to set the data value without any issue.
example.vue:
<template lang="pug">
v-hover(
v-slot:default="{ hover }"
:value="hoverActive"
)
v-card#hoverable-card(
:elevation="hover ? 20 : 10"
)
</template>
<script>
export default {
data() {
return {
hoverActive: false
}
}
</script>
example.spec.js
import { mount, createLocalVue } from '#vue/test-utils'
import Vuetify from 'vuetify'
import example from '#/components/example.vue'
const localVue = createLocalVue()
localVue.use(Vuetify)
describe('example', () => {
const wrapper = mount(example, {
localVue
})
it('should have the correct elevation class on hover', () => {
let classes = wrapper.classes()
expect(classes).toContain('elevation-10')
expect(classes).not.toContain('elevation-20')
wrapper.setData({ hoverActive: true })
classes = wrapper.classes()
expect(classes).not.toContain('elevation-10')
expect(classes).toContain('elevation-20')
})
})
https://github.com/vuejs/vue-test-utils/issues/1421
it('2. User interface provides one help icon with tooltip text', async (done) => {
// stuff
helpIcon.trigger('mouseenter')
await wrapper.vm.$nextTick()
requestAnimationFrame(() => {
// assert
done()
})
})