close all <b-collapse> when clicking on a button (starting visible) - javascript

I have following problem. I have multiple b-buttons connected with b-collapse, all are starting visible (so they are open).
Now I need to make them none visible when the toggle from my parent.vue will be triggered - so after clicking on my button in my parent.vue I need to close all, that when I retrigger it and open the complete collapse all collapse in my child.vue should be closed.
How can I achieve that?
Parent.vue
<b-button class="col-5" v-b-toggle="'New' + item.id" variant="danger">
<Child :idParent="item.id"/>
Child.vue
<b-collapse visible :id="'New' + idParent">
<b-button v-b-toggle="Toggle1"></b-button>
<b-collapse visible id="Toggle1"></b-collapse>
<b-button v-b-toggle="Toggle2"></b-button>
<b-collapse visible id="Toggle2"></b-collapse>
<b-button v-b-toggle="Toggle3"></b-button>
<b-collapse visible id="Toggle3"></b-collapse>
</b-collapse>

One way to achieve this is to set a 'visible' boolean in the parent, toggled by the button press, and then pass this to the child to use in the button elements. For example:
Parent.vue
<b-button class="col-5" v-b-toggle="'New' + item.id" variant="danger" #click="!buttons_visible">
<Child :idParent="item.id" :buttons_visible="buttons_visible" />
Child.vue
<b-collapse visible :id="'New' + idParent">
<b-button v-b-toggle="Toggle1"></b-button>
<b-collapse :visible="buttons_visible" id="Toggle1"></b-collapse>
<b-button v-b-toggle="Toggle2"></b-button>
<b-collapse :visible="buttons_visible" id="Toggle2"></b-collapse>
<b-button v-b-toggle="Toggle3"></b-button>
<b-collapse :visible="buttons_visible" id="Toggle3"></b-collapse>
</b-collapse>

Related

accordion plus and minus icon not changing

I have this accordion and a collapse (as a component because I need to loop it). Here's the code:
accordion.vue
<div class="accordion col-lg-8 mx-auto" role="tablist">
<b-card no-body class="mb-1 py-2" v-for="each in questions" :key="each.id">
<Collapses v-bind:each="each"/>
</b-card>
</div>
Collapses.vue
<div>
<b-button #click="isActive = !isActive" role="tab" block v-b-toggle="'accordion-'+each.id">{{ each.question }}
<i class="float-right fa" :class="{ 'fa-plus': !isActive, 'fa-minus': isActive }"></i>
</b-button>
<b-collapse v-bind:id="'accordion-'+each.id" visible accordion="my-accordion" role="tabpanel">
<b-card-body>
<b-card-text>{{ each.answer }}</b-card-text>
</b-card-body>
</b-collapse>
</div>
<script>
export default {
props: ["each"],
data() {
return {
isActive: false
}
}
}
</script>
The accordion works fine except the icons. The accordion only shows one (expanded) collapse at a time. Whenever I click another collapse, the previous collapse closes, but the icon doesn't change (because I did not click it). How do I automatically change the icon whenever the collapse closes?
The b-collapse events include show and hide, which is emitted when the component state changes. Thus, you could use a v-on directive (or # for shortand) to bind an event listener in the template that sets the isActive flag accordingly:
<b-collapse #hide="isActive = false" #show="isActive = true">
Then you could remove the button-click handler as it's already taken care of by the event binding above.
demo
Your data is defined like method, try to do it in more clear way:
{
data: () => ({
isActive: false
})
}

Sidebar component to render on main page (body)

I am using Quasar's q-drawer to display a menu list (using q-list and q-items).
Each item when clicked should render a new vue component on the body part of my page.
However, this is not the case. Clicking on the menu item renders the new vue component on the sidebar (drawer) as well.
How can I make it load on the body/main area instead of the sidebar?
Code snippet of my component (LeftSideBar):
<q-list v-for="(menuItem, index) in menuList" :key="index" align="left">
<q-item clickable v-ripple #click="selected = menuItem.label" active-class="button-highlighted"
:active="selected === menuItem.label" >
<q-item-section>
{{ menuItem.label }}
</q-item-section>
</q-item>
</q-list>
<div v-if="selected == 'Some Text'">
<SomeNewVue />
</div>
And it is in my main layout page.
<q-page-container>
<q-drawer bordered v-model="isShowLeftDrawer" :width="320" :breakpoint="100" content-class="bg-blue text-white">
<LeftSidebar/>
</q-drawer>
<router-view />
</q-page-container>
As you can see, LeftSidebar is in q-page-container.
Is there any way I can display the new vue component in the main page without using props/events??
Help please! Thanks in advance!
You need to put the component code in q-page-container. It will display in the body area.
eg.
<q-page-container>
<router-view/>
<div v-if="selected == 'Some Text'">
<SomeNewVue />
</div>
</q-page-container>
refer this - https://codepen.io/Pratik__007/pen/YzyXJYb?editable=true&editors=101%3Dhttps%3A%2F%2Fquasar.dev%2Flayout%2Fdrawer

VueJs: How to reuse bootstrap modal dialog

I've created three simple buttons that will trigger three different bootstrap modal dialog. The modal dialogs are "Add Product", "Edit Product" and "Delete Product". Both the Add and Edit modal dialogs contain a form with two input elements, whereas the Delete modal dialog contains a simple text. I realise that my code becomes very messy and hard to maintain. Hence, I have the following question:
1) How do I reuse the modal dialog, instead of creating 3 separate dialogs?
2) How do I know which modal dialog has been triggered?
Update: I've developed a soultion where I will include conditional statements such as v-if, v-else-if and v-else to keep track of which button the user click. However, I still feel that there is a better solution to this. Can anyone help/advice me?
Below is my current code:
<template>
<div>
<b-button v-b-modal.product class="px-4" variant="primary" #click="addCalled()">Add</b-button>
<b-button v-b-modal.product class="px-4" variant="primary" #click="editCalled()">Edit</b-button>
<b-button v-b-modal.product class="px-4" variant="primary" #click="deleteCalled()">Delete</b-button>
<!-- Modal Dialog for Add Product -->
<b-modal id="product" title="Add Product">
<div v-if="addDialog">
<form #submit.stop.prevent="submitAdd">
<b-form-group id="nameValue" label-cols-sm="3" label="Name" label-for="input-horizontal">
<b-form-input id="nameValue"></b-form-input>
</b-form-group>
</form>
<b-form-group id="quantity" label-cols-sm="3" label="Quantity" label-for="input-horizontal">
<b-form-input id="quantity"></b-form-input>
</b-form-group>
</div>
<div v-else-if="editDialog">
<form #submit.stop.prevent="submitEdit">
<b-form-group id="nameValue" label-cols-sm="3" label="Name" label-for="input-horizontal">
<b-form-input id="nameValue" :value="productName"></b-form-input>
</b-form-group>
</form>
<b-form-group id="quantity" label-cols-sm="3" label="Quantity" label-for="input-horizontal">
<b-form-input id="quantity" :value="productQuantity">5</b-form-input>
</b-form-group>
</div>
<div v-else>
<p class="my-4">Are You Sure you want to delete product?</p>
</div>
</b-modal>
</div>
</template>
<script>
export default {
data() {
return {
productName: "T-Shirt",
productQuantity: 10,
addDialog: false,
editDialog: false,
deleteDialog: false
};
},
methods: {
addCalled() {
this.addDialog = true;
},
editCalled() {
this.editDialog = true;
this.addDialog = false;
this.deleteDialog = false;
},
deleteCalled() {
this.deleteDialog = true;
this.addDialog = false;
this.editDialog = false;
}
}
};
</script>
<style>
</style>
As already mentionned, I would have use slots and dynamic component rendering to accomplish what you're trying to do in a cleaner way.
See snippet below (I didn't make them modals as such but the idea is the same).
This way, you can have a generic modal component that deals with the shared logic or styles and as many modalContentsub-components as needed that are injected via the dedicated slot.
Vue.component('modal', {
template: `
<div>
<h1>Shared elements between modals go here</h1>
<slot name="content"/>
</div>
`
});
Vue.component('modalA', {
template: `
<div>
<h1>I am modal A</h1>
</div>
`
});
Vue.component('modalB', {
template: `
<div>
<h1>I am modal B</h1>
</div>
`
});
Vue.component('modalC', {
template: `
<div>
<h1>I am modal C</h1>
</div>
`
});
new Vue({
el: "#app",
data: {
modals: ['modalA', 'modalB', 'modalC'],
activeModal: null,
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button v-for="modal in modals" #click="activeModal = modal"> Open {{ modal }} </button>
<modal>
<template slot="content">
<component :is="activeModal"></component>
</template>
</modal>
</div>
Update
Now, You might think how will you close your modal and let the parent component know about it.
On click of button trigger closeModal for that
Create a method - closeModal and inside commonModal component and emit an event.
closeModal() {
this.$emit('close-modal')
}
Now this will emit a custom event which can be listen by the consuming component.
So in you parent component just use this custom event like following and close your modal
<main class="foo">
<commonModal v-show="isVisible" :data="data" #close- modal="isVisible = false"/>
<!-- Your further code -->
</main>
So as per your question
A - How do I reuse the modal dialog, instead of creating 3 separate dialogs
Make a separate modal component, let say - commonModal.vue.
Now in your commonModal.vue, accept single prop, let say data: {}.
Now in the html section of commonModal
<div class="modal">
<!-- Use your received data here which get received from parent -->
<your modal code />
</div>
Now import the commonModal to the consuming/parent component. Create data property in the parent component, let say - isVisible: false and a computed property for the data you want to show in modal let say modalContent.
Now use it like this
<main class="foo">
<commonModal v-show="isVisible" :data="data" />
<!-- Your further code -->
</main>
The above will help you re-use modal and you just need to send the data from parent component.
Now second question will also get solved here How do I know which modal dialog has been triggered?
Just verify isVisible property to check if modal is open or not. If isVisible = false then your modal is not visible and vice-versa

Bootstrap-Vue Modal reopens everytime except in ESC

I am currently writing a Vue component for a project.
I encountered a Problem where a Bootstrap-Vue modal will re open again instead of closing.
I am using Vue.js in Version 2.6.10 and Bootstrap 4.
<template>
<div>
<b-button v-b-modal.rating-modal #click="showModal()">
Click me
<b-modal ref="rating-modal" no-close-on-backdrop centered title="Rate" class="rating-modal" #ok="hideModal" #cancel="hideModal" #close="hideModal">
<div>
Content of the modal...
</div>
</b-modal>
</b-button>
</div>
</template>
<script>
export default {
name: "Rating",
components: {
,
},
methods: {
showModal() {
this.$refs['rating-modal'].show();
},
hideModal() {
this.$refs['rating-modal'].hide();
},
}
}
;
</script>
I expect it to close when I hit either cancel, ok or the cross in the header.
Ok, I resolved the Issue by myself. All I had to do is to move the b-modal tag out of the b-button tag like this:
<template>
<div>
<b-button v-b-modal.rating-modal #click="showModal()">
Click me
</b-button>
<b-modal ref="rating-modal" no-close-on-backdrop centered title="Rate" class="rating-modal" #ok="hideModal" #cancel="hideModal" #close="hideModal">
<div>
Content of the modal...
</div>
</b-modal>
</div>
</template>

How to make the bootstrap table rows selectable and remain the selected rows on filter?

I want to have a bootstrap table and already added input checkboxes to select them as well as a filter to get all selected items (which is totally fine).
After selecting a row, the dataset property "selected" should be set to 1 or true.
But i want to remain all the selected values also when i filter (which should already be fixed by persisting the "selected" attribute in my dataset shouldn't it?)
Currently i can check the checkbox and the checkbox value itself is changed too, but the row.value variable remains the same even if i use v-model for row.value (2-way-databinding)
So. How can i change the value of an attribute in a bootstrap table?
<b-table show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
#filtered="onFiltered"
>
<template slot="selected" slot-scope="row">
<input type="checkbox" id="checkbox" v-model="row.value">
{{row.value}}
</template>
<template slot="name" slot-scope="row">{{row.value}}</template>
<template slot="sapNumber" slot-scope="row">{{row.value}}</template>
<template slot="createDate" slot-scope="row">{{ moment(row.value).format('dd DD.MM.YY, hh:mm:ss')}}</template>
<template slot="master" slot-scope="row">
<!-- We use #click.stop here to prevent a 'row-clicked' event from also happening -->
<b-button size="sm" #click.stop="info(row.item, row.index, $event.target)" class="mr-1">
Info modal
</b-button>
<b-button size="sm" #click.stop="row.toggleDetails">
{{ row.detailsShowing ? 'Hide' : 'Show' }} Details
</b-button>
</template>
<template slot="row-details" slot-scope="row">
<b-card>
<ul>
<li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value}}</li>
</ul>
</b-card>
</template>
</b-table>
The solution was quite simple.
Just change this part
<template slot="selected" slot-scope="row">
<input type="checkbox" id="checkbox" v-model="row.value">
{{row.value}}
</template>
to this one
<template slot="selected" slot-scope="data">
<input type="checkbox" id="checkbox" v-model="data.item.selected">
</template>
So the scope of this column does not effect the iterators dataset (which is "row" in my case) but the root data set of the data attribute.

Categories