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
Related
I am creating a general component based on v-data-table. This component has templates that are displayed anywhere, such as: ..template v-slot:item.index="{ item }"> ....
The idea is to be able to pass custom templates as children of the component, in this case I would like to pass "..template v-slot:item.state="{ item }"> ..."
As you can see, in the source code of the ..[DataTableFuntional>] <v-data-table... component there is a commented template (..template v-slot:item.state="{ item }">) which works perfectly.
But it does not suit my need since that way I would have to pass props to activate or deactivate the template because not all tables would always carry that field called "state"
DataTableFuntional component
<template>
<div>
<v-data-table
class="tableBackground"
:dense="dense"
:headers="headers"
:items="items.data"
:options.sync="options"
no-data-text="No hay datos disponibles"
:loading-text="$t('comunes.general.cargando')"
>
<!-- Custom templates -->
<template v-slot:body.append>
<slot name="body2"></slot>
</template>
<template v-slot:item.state="{ item }">
<slot name="state"></slot>
</template>
<!-- <template v-slot:item.state="{ item }">
<div class="">
<span v-if="item.state === 1" color="red">Close</span>
<span v-else-if="item.state === 2" color="green">Open</span>
</div>
</template> -->
<!-- Default templates -->
<template v-slot:item.index="{ item }">
{{ items.data.indexOf(item) + 1 }}
</template>
</v-data-table>
</div>
</template>
Now, if we see this code, which is the previous component imported in any page of the site.
We see that there are two "slots": v-slot: state" and "v-slot: body2" that are shown correctly, but if we see the code of the v-data-table> ... we can see that it is necessary to specify the v -slot in the template, for example:
<template v-slot: item.state = "{item}">
<slot name = "state"> </slot>
</template>.
now, if we see the first tamplate:
<template v-slot: item.state = "{item}">
<div class = "">
<span v-if = "item.state === 1" color = "red"> Close </span>
<span v-else-if = "item.state === 2" color = "green"> Open </ span
>
</div>
</template>
This would be the way I would more or less like to be able to assign new templates to the v-data-table component.
but it does not work.
if we see the following template
<template v-slot: state>
{{'Hello'}}
</template>
This does work but I don't know how to access the item to be able to set the conditions according to the state.
imported DataTableFuntional component whit child templates
<DataTableFuntional
:ref="'reference'"
:endpoint="item.endpoint"
:headers="item.headers"
:actions="item.actions"
:btsmall="true"
>
<!-- Custom templates -->
<template v-slot:item.state="{ item }">
<div class="">
<span v-if="item.state === 1" color="red">Close</span>
<span v-else-if="item.state === 2" color="green">Open</span
>
</div>
</template>
<template v-slot:state>
{{'Hello'}}
</template>
<template v-slot:body2>
<tr>
<td></td>
<td>
<v-text-field
type="number"
label="Less than"
></v-text-field>
</td>
<td colspan="4"></td>
</tr>
</template>
</DataTableFuntional>
I'm implementing an application with Vue Js and I've the following code:
<template>
<simple-page title="list-patient" folder="Patient" page="List Patient" :loading="loading">
<list-patients #patientsLoaded="onPatientsLoaded"/>
</simple-page>
</template>
Both simple-page and list-patients are custom components created by me. Inside ListPatients I've an HTTP request on Create callback, as follows:
created() {
axios.get("...").then(response => {
...
this.$emit('patientsLoaded');
})
},
Then, my objective is to handle the patientsLoaded event and uptade the loading prop on the top parent component, as follows:
data() {
return {
loading: true
}
},
methods: {
onPatientsLoaded(params) {
this.loading = false;
}
}
However, the created method is not being triggered inside the list-patients component. The only way I can make this work is by removing :loading.
Any one can help?
Edit 1
Code of simple page:
<template>
<section :id="id">
<!-- Breadcrumb-->
<breadcumb :page="page" :folder="folder"/>
<!-- Breadcrumb-->
<!-- Simple Card-->
<simple-card :title="page" :icon="icon" :loading="loading" v-slot:body>
<slot>
</slot>
</simple-card>
<!-- Simple Card-->
</section>
</template>
Code of simple card:
<b-card>
<!-- Page body-->
<slot name="body" v-if="!loading">
</slot>
<!--Is loading-->
<div class="loading-container text-center d-block">
<div v-if="loading" class="spinner sm spinner-primary"></div>
</div>
</b-card>
Your list-patients component goes in the slot with name "body". That slot has a v-if directive so basically it is not rendered and hooks are not reachable as well. Maybe changing v-if to v-show will somehow help you in that situation. Anyway, you have deeply nested slots and it is making things messy. I usually declare loading variable inside of the component, where fetching data will be rendered.
For example:
data () {
return {
loading: true;
};
},
mounted() {
axios.get('url')
.then(res => {
this.loading = false;
})
}
and in your template:
<div v-if="!loading">
<p>{{fetchedData}}</p>
</div>
<loading-spinner v-else></loading-spinner>
idk maybe that's not best practise solution
v-slot for named slots can be indicated in template tag only
I suppose you wished to place passed default slot as body slot to simple-card component? If so you should indicate v-slot not in simple-card itself but in a content you passed it it.
<simple-card :title="page" :icon="icon" :loading="loading">
<template v-slot:body>
<slot>
</slot>
</template>
</simple-card>
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
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>
Working with an e-commerce store application with polymer
I'm loading products array using polymer core-ajax and using core-animated pages to display product thumbnail and product detail page (full view) but I only wanted to load the product details when clicking on each product thumb, How can I do this
Find the HTML
<div id="article-content" >
<template is="auto-binding" id="page-template" >
<core-ajax
id="ajaxpolo" auto
url="./json/products.json"
handleAs="json"
on-core-response="{{handleResponse}}" response="{{headerList}}" on-core-response="{{postsLoaded}}">
</core-ajax>
<core-animated-pages id="fpages" flex selected="{{$.polo_cards.selected}}" on-core-animated-pages-transition-end="{{transitionend}}" transitions="cross-fade-all slide-from-right">
<section vertical layout>
<div id="noscroll" fit hero-p>
<div id="container" flex horizontal wrap around-justified layout cross-fade >
<section on-tap="{{selectView}}" id="polo_cards" >
<template repeat="{{item in headerList}}">
<div class="card" vertical center center-justified layout hero-id="item-{{item.id}}" hero?="{{$.polo_cards.selected === item.id || lastSelected === item.id }}" > <span cross-fade hero-transition style="">{{item.name}}</span></div>
</template>
</section>
</div>
</div>
</section>
<template repeat="{{item in headerList}}">
<section vertical layout>
<div class="view" flex vertical center center-justified layout hero-id="item-{{item.id}}" hero?="{{$.polo_cards.selected === item.id || $.polo_cards.selected === 0}}" >
<core-icon-button class="go_back" icon="{{$.polo_cards.selected != 0 ? 'arrow-back' : 'menu'}}" on-tap="{{goback}}"></core-icon-button>
{{item.name}} <span cross-fade class="view-cont" style="height:1000px; overflow:scroll;"></span></div>
</section>
</template>
</core-animated-pages>
</template>
first you would set the auto attribute of core ajax to false. auto="false" that would stop core-ajax from grabbing data by it's self. then set up a on-tap or on-click attribute on the element you want to be the click / tap handler. on-tap="{{getThem}}" then create the function.
getThem: function () {
this.$.ajaxpolo.go();
}
that should get it. hope it helps.
edit: you will want to grab a few more things with your event.
on the click / tap handler add the id of the item you wish to get to a generic attribute. (stay away from normal attributes. ie id, title and so forth) dataId I will call it.
<div on-tap="{{getThem}}" dataId="{{product_id}}"></div>
then in your function you get a few more things with the event as i said before.
getThem: function (event, detail, sender) {
var id = sender.attributes.dataId.value;
// do something with id
}
i just realized i may have misunderstood when you were talking about php. sorry.