Laravel + Quasar #click to show modal not working - javascript

I'm trying to use quasar for the first time and I am trying to show a modal with a button but somehow the value won't turn to true which triggers my modal to show.
Home.vue
<template>
<div>
<q-layout>
<q-list highlight class="bg-white">
<q-list-header>Details
<q-btn color="green-5" #click="openAdd">Add New</q-btn>
</q-list-header>
<!-- <q-search/> -->
<q-item>
<q-item-side>
<q-item-tile avatar>
<img src="https://cdn.quasar.dev/logo/svg/quasar-logo.svg">
</q-item-tile>
</q-item-side>
<q-item-main label="John Doe" />
<q-item-side right>
<div>
<q-btn flat round icon="edit" color="yellow-8" small/>
<q-btn flat round icon="delete" color="red-8" small/>
<q-btn flat round icon="visibility" color="green-6" small/>
</div>
</q-item-side>
</q-item>
</q-list>
</q-layout>
<Add></Add>
</div>
</template>
Home.vue(script)
<script>
let Add = require('./Add.vue');
export default {
name: 'app',
components: {Add},
data(){
return{
addActive : ''
}
},
methods: {
openAdd() {
this.addActive = '';
}
}
}
</script>
Add.vue(modal):
<template>
<div>
<q-modal v-model="addActive" ref="layoutModal" :content-css="{minWidth: '50vw', minHeight: '50vh'}">
<q-modal-layout>
<q-toolbar slot="header" color="green-7">
<div class="q-toolbar-title">
Header
</div>
</q-toolbar>
<q-toolbar slot="footer" color="green-7">
<div class="q-toolbar-title">
Footer
</div>
<q-btn color="green-10" label="Save">Save</q-btn>
<q-btn color="red-9" #click="open = false" label="Close">Cancel</q-btn>
</q-toolbar>
</q-modal-layout>
</q-modal>
</div>
</template>
<script>
import {
QToolbar,
QToolbarTitle,
QBtn,
QModal,
QModalLayout
} from 'quasar-framework'
export default {
name: 'app',
components: {
QToolbar,
QToolbarTitle,
QBtn,
QModal,
QModalLayout
},
data() {
return {
layoutStore: {
view: 'lHh Lpr lFf',
reveal: false,
leftScroll: true,
rightScroll: true,
leftBreakpoint: 996,
rightBreakpoint: 1200,
hideTabs: false
}
}
},
data () {
return {
addActive: false
}
}
}
</script>
<style lang="stylus">
</style>
Any help is appreciated! Cheers and thanks!!
I tried turning the Add.vue's open value to return true and the modal shows up. But when I try to use the button to turn its value to true it does not work somehow.

You're defining
addActive as empty string in Home.vue
Have you tried
addActive: false (or true if you want it to open right away)
but you need to define in props as Boolean and pass those accordingly and not in data()

Related

Vue 3/Quasar: Handling opening and closing of modals

I have two modals:
One is a Sign Up modal which takes in information of an existing user. Another modal which allows an existing user to login.
The only way to get to the Login modal is through the Signup modal.
But what I would like to do is, if the use wants to open the Login, I would like to close the Sign up modal first.
Right now that seems impossible with my setup. With the code below (nextTick), it does not work and closes both modals... despite there being a unique v-model for each modal.
Sign up Modal
<template>
<AVModal
:title="$t('signup.create_an_account')"
:button-text="$t('signup.button_text')"
classes="hide-icon q-mt-sm"
modal-style="width: 350px"
v-model="modal"
>
<q-form
#submit="signUp(user)"
class="row column fitq-gutter-md q-gutter-md"
>
<q-input
outlined
v-model="signup_user.username"
:label="$t('signup.name')"
>
<template v-slot:append>
<q-icon name="person" color="grey" />
</template>
</q-input>
<q-input
outlined
v-model="signup_user.email"
type="email"
:label="$t('signup.email')"
>
<template v-slot:append>
<q-icon name="email" color="grey" />
</template>
</q-input>
<q-input
outlined
v-model="signup_user.password"
type="password"
:label="$t('signup.password')"
>
<template v-slot:append>
<q-icon name="lock" color="grey" />
</template>
</q-input>
<q-checkbox
v-model="signup_user.privacy_policy"
color="secondary"
:label="$t('signup.privacy_policy')"
/>
<q-checkbox
v-model="signup_user.newsletter"
color="secondary"
:label="$t('signup.newsletter')"
/>
<q-btn color="primary" class="q-py-sm" type="submit">{{
$t("signup.get_started")
}}</q-btn>
</q-form>
<div class="row q-my-sm q-mt-md fill">
<AVLoginModal #on-open="handleOpen" />
</div>
<!-- <AVSeperator text="or" /> -->
<!-- <AVSocialMediaButtons class="q-mt-md" /> -->
</AVModal>
</template>
<script setup>
import { ref, nextTick } from "vue";
import AVModal from "../atoms/AVModal.vue";
// import AVSeperator from "../atoms/AVSeperator.vue";
// import AVSocialMediaButtons from "../atoms/AVSocialMediaButtons.vue";
import AVLoginModal from "./LoginModal.vue";
import { useAuth } from "../../composables/useAuth";
const modal = ref(false);
const handleOpen = () => {
nextTick(() => (modal.value = false));
};
const { signUp, signup_user } = useAuth();
</script>
Login Modal:
<template>
<AVModal
:title="$t('login.welcome_back')"
:button-text="$t('signup.login_instead')"
classes="hide-icon q-mt-sm fit"
color="blue"
modal-style="width: 350px"
v-model="modal"
#on-open="emit('on-open')"
>
<q-form
#submit="login(login_user)"
class="row column fitq-gutter-md q-gutter-md"
>
<q-input
outlined
v-model="login_user.email"
type="email"
:label="$t('signup.email')"
>
<template v-slot:append>
<q-icon name="email" color="grey" />
</template>
</q-input>
<q-input
outlined
v-model="login_user.password"
type="password"
:label="$t('signup.password')"
>
<template v-slot:append>
<q-icon name="lock" color="grey" />
</template>
</q-input>
<q-btn color="primary" class="q-py-sm" type="submit">{{
$t("login.login")
}}</q-btn>
</q-form>
<!-- <AVSeperator text="or" class="q-my-md" /> -->
<!-- <AVSocialMediaButtons class="q-mt-md" /> -->
</AVModal>
</template>
<script setup>
import { ref, defineEmits } from "vue";
import { useAuth } from "../../composables/useAuth";
import AVModal from "../atoms/AVModal.vue";
// import AVSeperator from "../atoms/AVSeperator.vue";
// import AVSocialMediaButtons from "../atoms/AVSocialMediaButtons.vue";
const modal = ref(false);
const emit = defineEmits(["on-open"]);
const { login_user, login } = useAuth();
</script>
Maybe my design is bad? Is there a more conventional way of creating modals in Quasar?
Here is my reuseable Modal component:
<template>
<q-btn
:color="color"
:align="align"
flat
#click="openModal"
:icon="icon"
:class="[classes]"
:style="{ width }"
>{{ buttonText }}</q-btn
>
<q-dialog v-model="modal" :persistent="persistent">
<q-card :style="modalStyle">
<q-card-section>
<div class="row justify-between">
<div>
<div class="text-h6">{{ title }}</div>
<div class="text-subtitle2">
<slot name="subtitle" />
</div>
</div>
<slot name="top-right" />
</div>
</q-card-section>
<q-card-section class="q-pt-none">
<slot />
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<slot name="actions" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup>
import { computed, defineProps, defineEmits } from "vue";
const props = defineProps({
icon: {
type: String,
default: null,
},
color: {
type: String,
default: "",
},
title: {
type: String,
default: "",
},
buttonText: {
type: String,
default: "Open Modal",
},
modalStyle: {
type: String,
default: "width: 900px; max-width: 80vw",
},
width: {
type: String,
default: "",
},
align: {
type: String,
default: "around",
},
classes: {
type: String,
default: "",
},
persistent: {
type: Boolean,
default: false,
},
modelValue: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["on-open", "update:modelValue"]);
const modal = computed({
get: () => props.modelValue,
set: (val) => emit("update:modelValue", val),
});
const openModal = () => {
modal.value = true;
emit("on-open");
};
</script>
<style lang="scss">
.hide-icon {
i.q-icon {
display: none;
}
}
</style>
You can use the Dialog plugin with a custom component. By doing that, you can not just correctly open nested dialogs, but also simplify the prop/event management. Instead of a re-usable modal(AVModal.vue), you would create a re-usable card/container. Then, use that card in your Dialog plugin custom components.
Here is what closing the signup modal would look like in both approaches:
// Signup Modal
// Current way to close the modal (which doesn't correctly work)
modal.value = false; // v-model="modal"
// Custom dialog component way
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
onDialogCancel();
Instead of a button and a q-dialog inside the component, you would have the custom dialog component in a separate file, then when the button is clicked, invoke the Dialog plugin like this:
Dialog.create({
component: LoginModal,
})

Quasar: drawerClick not working when I click one of menus

I would like to have responsive drawer, so tried to code in accordance with Quasar Docs, however, it was not resized properly when the screen size is being changed. In order to solve this, I used computed in the script. I think due to this computed, the function drawerClick not working.
When the drawer is on minimode, and one of menus are clicked, then the menu should be expanded, but the problem I have is it never expanded.
template
<template>
<q-drawer
v-model="drawer"
show-if-above
:mini="!drawer || miniState || miniStatus"
#click.capture="drawerClick"
:width="220"
:breakpoint="500"
bordered
:content-style="{ backgroundColor: '#f5f7f9' }"
id="side-menu"
>
<q-scroll-area class="fit">
<q-list padding>
<q-btn v-if="!miniState" flat left class="logo-btn">
<img src="~assets/os_logo.png" width="144px" height="24px" contain />
</q-btn>
<q-btn v-else flat left>
<img src="~assets/os_logo_no_text.png" width="24px" contain />
</q-btn>
<q-expansion-item
default-opened
v-for="(menu, index) in menus"
header-class="header-bg text-black"
expand-icon-class="text-gray"
>
<q-expansion-item
v-for="(sub, index) in menu.subMenus"
:key="index"
:label="sub.title"
expand-icon="none"
class="sub-content"
:to="{ name: sub.link }"
/>
</q-expansion-item>
</q-list>
</q-scroll-area>
</q-drawer>
</template>
script
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'SideMenu',
data() {
return {
drawer: false,
miniStatus: false,
switchToMini: 700
};
},
computed: {
miniState: function() {
return this.$q.screen.width < this.switchToMini;
}
},
methods: {
drawerClick(e) {
if (this.miniStatus) {
this.miniStatus = false;
e.stopPropagation();
}
}
}
});
</script>
I explained better here in this image
[1]: https://i.stack.imgur.com/PqYoc.png

Filter empty the array after click on custom event

I am trying to practice vue application using props but when I click on the the name, I tried to do console.log the problem but it hasn't helped me. Maybe I am new to filter option but I want to know what I am doing wrong?
App.vue
<template>
<div id="app">
<h1>All Friends</h1>
<AllFriends :Totalfriends="friends" />
<h1>Online Friends</h1>
<OnlineFriends :Totalfriends="friends" #toggleStatus="toggleStatus" />
</div>
</template>
<script>
import AllFriends from "./components/AllFriends"
import OnlineFriends from "./components/OnlineFriends"
export default {
name: 'App',
components: {
AllFriends,
OnlineFriends
},
data(){
return{
friends: [
{name:'Mario', online: true},
{name:'Luigi', online: false},
{name:'Todd', online: true},
{name:'Bowzer', online: false}
]
}
},
methods: {
toggleStatus(payload){
this.friends = this.friends.filter((friend)=>{
if(friend !== payload){
console.log(friend)
}
})
}
}
}
</script>
OnlineFriends.vue
<template>
<div>
Online Friends
<div v-for="(friends, index) in Totalfriends" :key="index">
<p #click="toggleStatus(friends.name)" v-if="friends.online">{{friends.name}}</p>
</div>
</div>
</template>
<script>
export default {
props:["Totalfriends"],
methods: {
toggleStatus(friend){
this.$emit("toggleStatus",{friend})
}
}
}
</script>
AllFriends.vue
<template>
<div>
All Friends
<div v-for="(friend, index) of Totalfriends" :key="index">
<p>{{friend.name}}</p>
</div>
</div>
</template>
<script>
export default {
name: "AllFriends",
props: ["Totalfriends"]
}
</script>

Slot contents are merged between Tabs in VueJS

I have this TabPanel where I am rendering multiple tabs. Each Panel inside TabPanel uses/extends Panel where I am using slots. However, during tab switch content of Tabs are getting merged.
<template>
<div class>
<div class>
<ul class>
<li
style="display: inline-block; padding:10px; margin: 10px; border: 1px solid #ccc"
v-for="(panel, index) in panels"
:key="index"
#click="selectTab(index)"
:ref="'panel' + index"
>{{ panel.title }}</li>
</ul>
</div>
<div class>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "TabPanel",
data() {
return {
panels: [],
selectedIndex: 0
};
},
methods: {
selectTab(panelIndex) {
this.selectedIndex = panelIndex;
this.panels.forEach((panel, index) => {
panel.isActive = index === panelIndex;
});
}
},
created() {
this.panels = this.$children;
},
mounted() {
this.selectTab(0);
}
};
</script>
Panel code
<template>
<div v-show="isActive">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Panel",
props: {
title: {
type: String,
required: true
},
panelId: {
type: String,
required: true
}
},
data() {
return {
isActive: true
};
}
};
</script>
Is there anyway to fix this issue. This is the CodeSandbox link to demonstrate this issue since its easier to visualize problem that way I think.
Dashboard.vue and RosePanel.vue changes isActive data but they are not doing nothing whit this.
One option is to set a v-show/v-if to each own <Panel>:
<Panel v-show="isActive" :title="title" :panelId="panelId">Content from Dashboard</Panel>
CodeSandbox: https://codesandbox.io/s/charming-hamilton-jz1og
The simplest way to fix the issue is to wrap all components in <Panel>-s:
<TabPanel>
<Panel title="Dashboard" panel-id="dashboard">
<Dashboard></Dashboard>
</Panel>
<Panel title="Test" panel-id="test">Content from Panel</Panel>
<Panel title="Rose Panel" panel-id="rose-panel">
<RosePanel></RosePanel>
</Panel>
</TabPanel>
https://codesandbox.io/s/eager-brown-6ethk?file=/src/App.vue
But this is probably not what you had in mind.
From what I see, you simply forgot to propagate the "is-active" property to the embedded panel:
<Panel :title="title" :panelId="panelId" :is-isActive="isActive">Content from Dashboard</Panel>
(e.x in the Dashboard Component)

Toggle modal dialog from parent component in VUE

So i want to toggle my modal dialog from parent component and i tried each step mentioned here Stack Overflow Question about same Topic , but still my Modal Dialog is not visible and it even has undefined value when i see from VUE console. and in Elements section the modal dialog div is not created.
MY MODAL DIALOG is not showing in elements section on webpage, but showing in Vue console with undefined prop value. But Click effect is working from Parent component. and modal is setting true when i click on div.
My Parent Code
<template>
<div class="collection">
<section class="section section-elements">
<div class="elements-outer" #click="openModal">
<CopyComponent v-bind:item="item"/>
</div>
</section>
<modal v-modal="modal"/>
</div>
</template>
<script>
import Modal from "../components/Modal.vue";
export default {
name: 'Collection',
components: {
Modal
},
data() {
return {
modal: false
}
},
methods: {
openModal() {
this.modal = !this.modal;
}
}
}
</script>
My Child Component
<template>
<div v-if="value" class="modal">
<div class="body">
body
</div>
<div class="btn_cancel" #click="modalToggle">
<i class="icon icon-cancel" />
</div>
</div>
</template>
<script>
export default {
name: "Modal",
props: ["value"],
methods: {
modalToggle() {
this.$emit("input", !this.value);
}
}
};
</script>
am i missing anything?
Please help, thanks.
Your prop is called value so you need to pass it from parent component, also assign event for toggle modal.
<modal :value="modal" #toggle="modalToggle"/>
And in your child:
<template>
<div v-if="value" class="modal">
<div class="body">
body
</div>
<div class="btn_cancel" #click="modalToggle">
<i class="icon icon-cancel" />
</div>
</div>
</template>
<script>
export default {
name: "Modal",
props: ["value"],
methods: {
modalToggle() {
this.$emit("toggle");
}
}
}
</script>
You misspelled v-model in <modal v-modal="modal"/>, it should be <modal v-model="modal"/>

Categories

Resources