When I click on the outer area of the modal, I want the same event as the close button of the modal. (Event that closes modal when clicking outside area of modal)
The current progress is that the modal is closed when the close modal button is clicked.
Carousel.vue
<template>
<div>
<div v-for="(item, index) in photos" :key="index">
<div #click="imgClick(item)" style="cursor:pointer;">
<img :src="item.thumbnail" />
</div>
<Modal v-if='item.show' #close="item.show = false">
<div slot='body'>
<img :src="item.thumbnail" :class="`img-index--${index}`"/>
</div>
</Modal>
</div>
</div>
</template>
<script>
import Modal from './Modal.vue'
export default {
props: {
items: { type: Array, default: () => [] }
},
data() {
return {
photos: {}
}
},
created() {
this.photos = this.items.map(item => {
return { ...item, show: false }
})
},
methods: {
imgClick(item) {
item.show = true
}
},
components: {
Modal: Modal
}
}
</script>
Modal.vue
<template>
<transition name="modal">
<div class="modal-mask" #click="$emit('close')">
<div class="modal-wrapper">
<div class="app__phone">
<div class="feed">
<div class="post">
<div class="header headroom">
<div class="level-left">
<img src="../assets/imgs/user.gif" class="modal-header-img"/>
<div class="user">
<span class="username">username</span>
<button class="modal-default-button" #click="$emit('close')">Close</button>
</div>
</div>
</div>
<slot name="modal-img"></slot>
<div class="content">
<div class="content-title">
<slot name="modal-tit"></slot>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</transition>
</template>
When I add a click event to the bottom <div>, it closes when I click outside the modal, but it closes when I click anywhere in the modal.
<div class="modal-mask" #click="$emit('close')">
And this link has a Fiddle example in the accepted answer to the question.
https://stackoverflow.com/a/58023701/12066654
You need to add a handler to the outer modal div like so:
<template id="modal">
<div class="modal" #click.self="close">
<div class="close" #click="close">×</div>
<div class="body">
<slot name="body" />
</div>
</div>
</template>
Related
I just started learning Vue2.
I use bootstrap accordion.
I want to be able to click the 'hello button' without triggering the 'collapse' event.
How to do this?
<template>
<div class="accordion" id="accordionExample">
<div class="accordion-item" v-for="i in 3" :key="i">
<h2 class="accordion-header" :id="`headingOne${i}`">
<button class="accordion-button"
type="button" data-bs-toggle="collapse"
:data-bs-target="`#collapseOne${1}`"
aria-expanded="true"
:aria-controls="`collapseOne${1}`"
>
Accordion Item #{{ i }}
<button class="btn-primary" #click=test>hello button</button>
</button>
</h2>
<div :id="`collapseOne${i}`" class="accordion-collapse show"
:aria-labelledby="`headingOne${i}`">
<div class="accordion-body">
<strong>This is the first item's accordion body.</strong>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Learn',
methods: {
test () {
console.log('hello')
}
}
}
</script>
You have to make sure that your hello-button is not nested inside the accordion-button. Just put both buttons right next to eachother. You might have to adjust the css to position them properly.
<template>
<div class="accordion" id="accordionExample">
<div class="accordion-item" v-for="i in 3" :key="i">
<h2 class="accordion-header" :id="`headingOne${i}`">
<button class="accordion-button"
type="button" data-bs-toggle="collapse"
:data-bs-target="`#collapseOne${1}`"
aria-expanded="true"
:aria-controls="`collapseOne${1}`"
>
Accordion Item #{{ i }}
</button>
<button class="btn-primary" #click=test>hello button</button>
</h2>
<div :id="`collapseOne${i}`" class="accordion-collapse show"
:aria-labelledby="`headingOne${i}`">
<div class="accordion-body">
<strong>This is the first item's accordion body.</strong>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Learn',
methods: {
test () {
console.log('hello')
}
}
}
</script>
I have four components Dashboard.vue(parent component) it contains Three child components(DisplayBooks.vue,sortBooksHightoLow,sortBooksLowtoHigh).
Now i want to import one more component called Cart.vue inside the Dashboard.vue .By default only DisplayBooks.vue component is only visible,if i click on cart-icon inside the Dashboard component it displays the Cart.vue component and hides the DisplayBooks.vue component .
How to acheive this thing please help me to fix this thing..
Dashboard.vue
<template>
<div class="main">
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<img src="../assets/education.png" alt="notFound" class="education-image" />
</div>
<ul class="nav navbar-nav">
<li>
<p class="brand">Bookstore</p>
</li>
</ul>
<div class="input-group">
<i #click="handlesubmit();" class="fas fa-search"></i>
<div class="form-outline">
<input type="search" v-model="name" class="form-control" placeholder='search...' />
</div>
</div>
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<li><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content" >cart <span class="length" v-if="booksCount">{{booksCount}}</span></p>
</ul>
</div>
<div class="mid-body">
<h6>Books<span class="items">(128items)</span></h6>
<select class="options" #change="applyOption">
<option disabled value="">Sort by relevance</option>
<option value="HighToLow">price:High to Low</option>
<option value="LowToHigh">price:Low to High</option>
</select>
</div>
<div v-if="flam==false">
<h2>Hello</h2>
</div>
<DisplayBooks v-show="flag==='noOrder'" #update-books-count="(n)=>booksCount=n"/>
<sortBooksLowtoHigh v-show="flag==='lowToHigh'" />
<sortBooksHightoLow v-show="flag==='highToLow'" />
<Cart />
</div>
</template>
<script>
import sortBooksLowtoHigh from './sortBooksLowtoHigh.vue'
import sortBooksHightoLow from './sortBooksHightoLow.vue'
import DisplayBooks from './DisplayBooks.vue'
import Cart from './Cart.vue'
export default {
components: {
DisplayBooks,
sortBooksLowtoHigh,
sortBooksHightoLow,
Cart
},
data() {
return {
booksCount:0,
flag: 'noOrder',
brand: 'Bookstore',
name: '',
flam: true,
visible: true,
}
},
methods: {
flip() {
this.flam = !this.flam;
},
applyOption(evt) {
if (evt.target.value === "HighToLow") {
this.flag = 'highToLow';
} else this.flag = 'lowToHigh';
},
}
}
</script>
Cart.vue
<template>
<div class="main">
<div v-for="book in books" :key="book.id" class="container">
<div class="content">
<h5>My Cart({{booksCount}})</h5>
</div>
<div class="mid-section">
<img v-bind:src="book.file" alt="not found">
<p class="title-section">{{book.name}}</p>
</div>
<div class="author-section">
<p>by {{book.author}}</p>
</div>
<div class="price-section">
<h6>Rs.{{book.price}}</h6>
</div>
<button class="close-btn" #click="handlesubmit();" type="submit">close</button>
<div class="btn-grps">
<button class="btn" type="submit" >Place Order</button>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default{
data(){
return {
booksCount:0,
books: [{
id: 0,
file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
name: 'Dont Make me think',
author: 'Sai',
price: '1500'
},]
}
},
methods:{
}
}
</script>
It's recommended to use vur-router, but for a simple situation you could define a property called shownComp then update when you click on cart icon :
data() {
return {
shownComp:'DisplayBooks',
booksCount:0,
flag: 'noOrder',
then <li #click="shownComp='Cart'"><a><i class="fas fa-cart-plus"></i></a></li>
and :
<DisplayBooks v-show="flag==='noOrder' && shownComp==='DisplayBooks'" #update-books-count="(n)=>booksCount=n"/>
<Cart v-show=" shownComp==='Cart'" />
...
I try to use VUE3 and Bootstrap to open a Modal by clicking one of those two images.
I created 3 Components.
Left DIV (Image)
Right DIV (Image)
Modal to open
Now, i want to click on one of those images and open the modal.
At Singlepage it works. But now i am using Components.
How do i listen in the Component "ModalContact" to the clickevent in other Components?
I uploaded the complete package to github: https://github.com/Gismo1337/wtf-i-am-doing-here
Using Vue 2 and the Vue CLI, I created a couple of Single File Components to demonstrate how to open and close a Bootstrap 4 Modal without jQuery, wrapping the modal in it's own component.
Although this is not using Vue 3, you should be able to port it.
Parent.vue
<template>
<div class="parent">
<h4>Bootstrap Modal Parent</h4>
<div class="row">
<div class="col-md-6">
<button class="btn btn-secondary" #click="showModal">Open Bootstrap Modal</button>
</div>
</div>
<bootstrap-modal-no-jquery v-if="displayModal" #close-modal-event="hideModal" />
</div>
</template>
<script>
import BootstrapModalNoJquery from './BootstrapModalNoJquery.vue'
export default {
components: {
BootstrapModalNoJquery
},
data() {
return {
displayModal: false
}
},
methods: {
showModal() {
this.displayModal = true;
},
hideModal() {
this.displayModal = false;
}
}
}
</script>
BootstrapModalNoJquery.vue
<template>
<div class="bootstrap-modal-no-jquery">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" #click="saveChanges">Save changes</button>
<button type="button" class="btn btn-secondary" #click="closeModal">Close</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
methods: {
closeModal() {
this.$emit('close-modal-event');
},
saveChanges() {
this.closeModal();
}
}
}
</script>
<style scoped>
/* Override default value of 'none' */
.modal {
display: block;
}
</style>
I am trying to emit an event from a child and listen for it on the parent, but I am always getting errors like Invalid handler for event "new-tab": got undefined.
My app:
<div class="ibox" id="app">
<div class="ibox-content">
<h2>Table</h2>
<div class="details">
<table-tabs
v-on:new-tab="newTab"
v-on:close-tab="closeTab"
ref="tableTabs"
name="table-tabs"
></table-tabs>
</div>
</div>
</div>
TableTabs component:
<template>
<div>
<b-card no-body>
<b-tabs card>
<b-tab active>
<template slot="title">
<i class="fa fa-tablet-alt"></i> Orders
</template>
<orders-table
name="orders-table"
></orders-table>
</b-tab>
<b-tab v-for="order in tabs" :key="i">
<template slot="title">
<div>{{ order.name }}</div>
<b-button type="button" class="close float-right" aria-label="Close" #click="closeTab(order.id)">
<span aria-hidden="true">×</span>
</b-button>
</template>
<items-table
name="items-table"
:api-url="'/api/items/' + order.id"
></items-table>
</b-tab>
</b-tabs>
</b-card>
</div>
</template>
<script>
export default {
name: 'table-tabs',
data() {
return {
tabs: [],
}
},
methods: {
closeTab(id) {
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].id === id) {
this.tabs.splice(i, 1);
}
}
},
newTab(item) {
this.tabs.push(item);
}
}
}
</script>
OrdersTable component:
<template>
<div>
<vuetable ref="vuetable"
:api-url="apiUrl"
:fields="fields"
pagination-path="pagination"
#vuetable:pagination-data="onPaginationData"
>
<div slot="name-slot" slot-scope="{ rowData }">
<a href="#" class="float-left" #click="newTab(rowData.order)">
{{ rowData.order.name }}
<span class="fa fa-search-plus"></span>
</a>
</div>
</vuetable>
<div class="row">
<div class="col-md-6">
<vuetable-pagination-info
ref="paginationInfo"
></vuetable-pagination-info>
</div>
<div class="col-md-6">
<vuetable-pagination-bootstrap
ref="pagination"
class="pull-right"
#vuetable-pagination:change-page="onChangePage"
></vuetable-pagination-bootstrap>
</div>
</div>
</div>
</template>
<script>
import Vuetable from 'vuetable-2/src/components/Vuetable';
import VuetablePaginationBootstrap from '../VuetablePaginationBootstrap';
import VuetablePaginationInfo from 'vuetable-2/src/components/VuetablePaginationInfo';
import TableFieldDef from './table-field-def';
export default {
name: 'orders-table',
components: {
Vuetable,
VuetablePaginationBootstrap,
VuetablePaginationInfo
},
data() {
return {
apiUrl: '/api/orders?include=items',
fields: TableFieldDef,
}
},
methods: {
newTab(order) {
this.$emit('new-tab', order);
}
}
}
</script>
Why does this.$emit('new-tab', order) always result in the handler newTab being undefined.
Vue 2.5.21
The parent in this case is the TableTabs component. If the parent needs to listen to events emitted by a child component then it needs to add the event listener to the child component, which is the OrdersTable component in this case. So instead of this ..
<table-tabs
v-on:new-tab="newTab"
v-on:close-tab="closeTab"
ref="tableTabs"
name="table-tabs"
></table-tabs>
You should do this (inside the TableTabs component) ..
<orders-table
name="orders-table"
v-on:new-tab="newTab"
></orders-table>
I have the following code for a component called navbar-base:
<template>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<slot name="brand"></slot>
<button class="button navbar-burger" v-if="burger" >
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="navbar-menu">
<div class="navbar-start">
<slot name="menu-left"></slot>
</div>
<div class="navbar-end">
<slot name="menu-right"></slot>
</div>
</div>
</nav>
</template>
<script>
export default {
props: {
burger: {
type: Boolean,
default: false
}
},
computed: {
hasBurger () {
this.burger = true
return this.burger
}
}
}
</script>
What I would like to do is to be able to toggle on or off the navbar-burger as follows:
Burger is visible (toggled on)
<navbar-base class="is-info" hasBurger>
Brand name
Courses
Videos
Login
Signup
</navbar-base>
Burger is NOT visible (toggled off)
<navbar-base class="is-info">
Brand name
Courses
Videos
Login
Signup
</navbar-base>
In other words, if I add hasBurger to the <navbar-base> tag then the burger code will be included. Otherwise it won't.
The problem is that my code is not working -- and I'm not sure how to get it to work.
Any ideas?
Thanks.
I got it to work. The key was to actually NOT use a computed property at all. This is the working code (in case anyone finds it helpful):
<template>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<slot name="brand"></slot>
<button class="button navbar-burger" v-if="hasBurger" >
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="navbar-menu">
<div class="navbar-start">
<slot name="menu-left"></slot>
</div>
<div class="navbar-end">
<slot name="menu-right"></slot>
</div>
</div>
</nav>
</template>
<script>
export default {
props: {
hasBurger: {
type: Boolean,
default: false
}
}
}
</script>