Make comment specific to post - javascript

I rendered all of the posts using v-for, but now I want to add a comment section to each. I don't know how to make a connection between a comment and a post. As you can see in the code, the comment section renders for all post cards, but how can it be specific to each post? (I am not sure of the logic and how I can implement it).
<template>
<div v-for="item in userList" :key="item.uid">
<div class="row q-py-xs">
<q-card flat class="bg-grey-3 full-width user-card">
<q-card-section>
<div class="text-body1 q-ma-xs" id="postDtls">
{{ item.postDetails }}
</div>
<div class="row q-gutter-sm">
<q-chip square>
<q-avatar icon="home" color="amber-10" text-color="white" />
{{ item.postAddress }}
</q-chip>
</div>
</q-card-section>
<q-card-section> </q-card-section>
<q-card-section>
<q-input
outlined
id="cmInput"
v-model="cmOffer"
class="bg-grey-1"
color="amber-10"
label-slot
clearable
>
<template v-slot:label>
<span class="text-weight-bold text-deep-orange">write</span>
your comment
</template>
<template v-slot:append>
<q-btn
round
flat
icon="move_to_inbox"
v-on:click="submitComment()"
/>
</template>
</q-input>
<q-btn push dense class="full-width q-my-xs" color="amber-10" label="send message directly" />
</q-card-section>
</q-card>
</div>
</div>
</template>
<script>
import { ref } from "vue";
import { db } from "src/boot/firebase";
import { collection, query, where, getDocs } from "firebase/firestore";
import { doc, setDoc } from "firebase/firestore";
export default {
setup() {
return {
userList: ref([]),
cmList: ref([]),
idList: ref([]),
cmOffer: ref("")
};
},
async created() {
const querySnapshot = await getDocs(collection(db, "post"));
const postCm = await getDocs(collection(db, "post"));
querySnapshot.forEach(doc => {
let docData = doc.data();
this.userList.unshift(docData);
});
postCm.forEach(doc => {
let cmData = doc.data();
this.cmList.unshift(cmData);
});
},
};
</script>

You can create array of comments for every post, and then update them accordingly. Please take look at following snippet:
const { ref } = Vue
const app = Vue.createApp({
setup () {
const userList = ref([{uid: 1, postDetails: 'post 1', postAddress: '1', comments: ['comment 1', 'comment 2']}, {uid: 2, postDetails: 'post 2', postAddress: '2', comments: []}])
const cmOffer = ref([])
const submitComment = (id, i) => {
userList.value = userList.value.map(user => {
if(user.uid === id) {
user.comments.push(cmOffer.value[i])
}
return {...user}
})
cmOffer.value = []
}
return { userList, cmOffer, submitComment }
}
})
app.use(Quasar)
app.mount('#q-app')
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/quasar#2.4.13/dist/quasar.prod.css" rel="stylesheet" type="text/css">
<div id="q-app">
{{ userList }}
<div v-for="(item, idx) in userList" :key="item.uid">
<div class="row q-py-xs">
<q-card flat class="bg-grey-3 full-width user-card">
<q-card-section>
<div class="text-body1 q-ma-xs" id="postDtls">
{{ item.postDetails }}
</div>
<div class="row q-gutter-sm">
<q-chip square>
<q-avatar icon="home" color="amber-10" text-color="white" />
{{ item.postAddress }}
</q-chip>
</div>
</q-card-section>
<q-card-section>
<div class="text-body1 q-ma-xs" v-for="(comment, i) in item.comments" :key="i" >
{{ comment }}
</div>
</q-card-section>
<q-card-section>
<q-input outlined id="cmInput" v-model="cmOffer[idx]" class="bg-grey-1" color="amber-10" label-slot clearable>
<template v-slot:label>
<span class="text-weight-bold text-deep-orange">write</span>
your comment
</template>
<template v-slot:append>
<q-btn round flat icon="move_to_inbox" #click="submitComment(item.uid, idx)" />
</template>
</q-input>
<q-btn push dense class="full-width q-my-xs" color="amber-10" label="send message directly" #click="submitComment(item.uid, idx)" />
</q-card-section>
</q-card>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar#2.4.13/dist/quasar.umd.prod.js"></script>

Related

Managing state for multiple instances in Vue 3 with Pinia and Element Plus

I am encountering a problem with multiple default objects sharing the same state, and I am unable to separate the binding. I need to manage the state for each instance independently.
The parent and child components pass data to each other, and both communicate directly with the store.
The issue is that when clicking on the 'Add' button in the parent component creates a new row in the child component and adds a default objects (el-input and el-select) to an array, then all the elements are changed to the same state and are not independent.
I am using Vue 3, Pinia for store management, and Element Plus for UI components.
Parent -
<template>
<div>
<!-- first table-->
<el-row>
<div class="first-control-buttons">
<div>
<label class="first-label">First</label>
</div>
<div class="actions-control">
<div class="left-actions-control">
<el-radio-group v-model="store.firstRadio" class="ml-4">
<el-radio label="or" size="small">OR</el-radio>
<el-radio label="and" size="small">AND</el-radio>
</el-radio-group>
</div>
<div class="vertical-divider"></div>
<div class="right-actions-control">
<el-button :icon="Delete" circle #click="removeAllClick('firstCards')"/>
<el-button :icon="Plus" circle #click="store.addCard('firstCards')" />
</div>
</div>
</div>
</el-row>
<childTable :cards="store.firstCards" typeOfCard="firstCards"
#removeCard="store.removeCard"></childTable>
<!-- second table-->
<el-row>
<div class="first-control-buttons">
<div>
<label class="include-exclude-label">Second</label>
</div>
<div class="actions-control">
<div class="left-actions-control">
<el-radio-group v-model="store.secondRadio" class="ml-4">
<el-radio label="or" size="small">OR</el-radio>
<el-radio label="and" size="small">AND</el-radio>
</el-radio-group>
</div>
<div class="vertical-divider"></div>
<div class="right-actions-control">
<el-button :icon="Delete" circle #click="removeAllClick('secondCards')"/>
<el-button :icon="Plus" circle #click="store.addCard('secondCards')"/>
</div>
</div>
</div>
</el-row>
<childTable :cards="store.secondCards"
typeOfCard="secondCards" #removeCard="store.removeCard">
</childTable>
</div>
</template>
<script lang="ts" setup>
import {ref, computed, watch, onMounted} from 'vue'
import { Plus, Delete } from '#element-plus/icons-vue'
import childTable from './childTable.ce.vue';
import {useStore} from "../../../stores/useStore";
const store = useStore();
function removeAllClick(typeOfCards: string) {
if (store.firstCards && typeOfCards === "firstCards") {
ElMessageBox.confirm(`Are you sure you wish to empty?`,'Confirmation', {
}).then(() => {
store.firstCards = []
}).catch(() => {
console.log("cancel button was pressed")
})
} else if (store.secondCards && typeOfCards === "secondCards") {
ElMessageBox.confirm(`Are you sure you wish to empty`,'Confirmation', {
}).then(() => {
store.secondCards = []
}).catch(() => {
console.log("cancel button was pressed")
})
}
}
</script>
Child -
<template>
<el-row>
<div class="internal-form-details">
<div class="top-inputs-internal">
<table style="width: 100%">
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Action</th>
<th>Rule</th>
</tr>
</thead>
<tbody>
<tr v-for="(card, index) in cards" :key="index">
<td>
<el-select
v-model="store.typeOptionValue"
class="m-2 select-box">
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</td>
<td>
<SharedSelect
placeholder="Select"
:selectAllOption="true"
:typeOption="store.typeOptionValue">
</SharedSelect>
</td>
<td>
<el-select
v-model="store.freqAndVeloValue"
class="m-2 select-box velocity">
<el-option
v-for="item in freqAndVeloOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</td>
<td>
<div class="internal-rule-column">
<!-- if Freq -->
<div
v-if="store.freqAndVeloValue === 'freq'"
class="internal-frequency-inputs">
<el-input
v-model="store.freqValue"
#input="validateNumberInput"
type="number"
min="0"
class="frequency-value"/>
<span>or more times</span>
<el-select
v-model="store.freqTimeValue"
class="m-2 time-select">
<el-option
v-for="item in freqTimeOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
<el-select
v-if="store.freqTimeValue === 'inTheLast'"
v-model="store.freqTimePeriodValue"
class="m-2 time-period-select">
<el-option
v-for="item in freqTimePeriodOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</div>
<!-- if Vel -->
<div v-else class="internal-velocity-inputs">
<span>spanning</span>
<el-input
v-model="store.veloValue"
#input="validateNumberInput"
type="number"
min="0"
class="velocity-value"
/>
<span>days</span>
<el-select
v-model="store.veloIncreasedAndDecreasedValue"
class="m-2 select-box increased"
:popper-append-to-body="false"
>
<el-option
v-for="item in veloTimeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<span>by</span>
<el-input
v-model="store.veloNumberValue"
#input="validateNumberInput"
type="number"
min="0"
class="velocity-number-value"
/>
<span>% or more</span>
</div>
<div class="remove-button-row">
<el-button :icon="Delete" circle #click="removeCards(index, typeOfCard)"/>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</el-row>
</template>
<script lang="ts" setup>
import {onMounted, ref, onUnmounted, watch, computed} from "vue";
import {useStore} from "#/stores/useStore";
import SharedSelect from "../../shared/SharedSelect.ce.vue";
const store = useStore();
const defaults = defineProps({
typeOfCard: {
type: String,
default: "",
},
cards: {
type: Array,
default: [],
},
});
onMounted(() => {
const foundObject = typeOptions.find((option) => option.value === store.typeOptionValue).url;
store.fetchTypeOptionValues(foundObject);
});
watch(() => store.typeOptionValue, () => {
const foundObject = typeOptions.find((option) => option.value === store.typeOptionValue).url;
store.fetchTypeOptionValues(foundObject);
})
const emit = defineEmits(["removeCard"]);
const removeCards = (index, typeOfCard) => {
emit("removeCard", {index, typeOfCard});
};
</script>
Store -
import { defineStore } from "pinia";
export const useStore = defineStore({
id: "Yehu",
state: () => ({
firstRadio: "or",
secondRadio: "or",
input: "",
cards: [],
firstCards: [],
secondCards: [],
drawer: false,
dataTable: [],
freqValue: 1,
veloValue: 7,
typeOptionValue: "becImpre",
firstSecondValue: "first",
freqAndVeloValue: "freq",
veloNumberValue: 1,
freqTimeValue: "atAnyTime",
freqTimePeriodValue: "minutes",
veloIncreasedAndDecreasedValue: "increased",
}),
getters: {},
actions: {
addCard(typeOfCard?: string) {
const cardDefaultValues = {
firstSecondValue: "include",
typeOptionValue: "becImpre",
freqAndVeloValue: "freq",
freqValue: 1,
freqTimeValue: "atAnyTime",
veloValue: 7,
veloIncreasedAndDecreasedValue: "increased",
veloNumberValue: 1,
};
if (typeOfCard === "firstCards") {
this.firstCards.push({
...cardDefaultValues,
key: this.firstCards.length + 1,
});
} else if (typeOfCard === "secondCards") {
this.secondCards.push({
...cardDefaultValues,
key: this.secondCards.length + 1,
});
} else {
this.cards.push({
...cardDefaultValues,
key: this.cards.length + 1,
});
}
},
removeCard(props: object) {
if (this.firstCards && props.typeOfCard === "firstCards") {
this.firstCards.splice(props.index, 1);
} else if (this.secondCards && props.typeOfCard === "secondCards") {
this.secondCards.splice(props.index, 1);
} else {
this.cards.splice(props.index, 1);
}
},
},
});
After verifying the things, I found that I needed to use 'card' inside my component because I am iterating through an array that I am passing from the parent component.

How to filter checkbox by query api in vuejs?

I'm making a website that has a filter function before pouring data out... Currently, I'm doing the pretty stupid way of dumping all the data out and putting it in an object and then filtering. I want when I click on it, it will call the api to return according to the params that I pass, but the initial default is that the checkbox is in the all box, it still returns all. Here is my code part....
Template:
<template>
<div class="job">
<div class="container recruit flex-wrap">
<div class="job-filter">
<h3>
Ngành nghề
<img
id="icon-filter"
#click="showfilter"
:style="{ display: display.btn_show_filter }"
src="../assets/recruit/angle-down-svgrepo-com.svg"
alt=""
/>
<img
id="icon-close-filter"
:style="{ display: display.btn_close_filter }"
#click="closefilter"
src="../assets/recruit/close-svgrepo-com.svg"
alt=""
/>
</h3>
//Handle Filter
<div
class="radio-group"
id="group-filter"
:style="{ display: display.group_filter }"
>
<div
class="radio-check"
v-for="(check, index) in checks"
#click="selectFilter(check)"
:key="index"
>
<input
type="radio"
:id="index"
name="fav_language"
:value="check"
v-model="selected"
/>
<label :for="check">{{ check }}</label
><br />
</div>
</div>
</div>
//End Handle filter
<div class="search">
<div class="search-top flex-wrap">
<h5>{{ totalJobs }} {{ localised("countRecruit") }}</h5>
<div class="search-input">
<input
type="search"
placeholder="Nhập tên vị trí công việc"
v-model="search"
/>
<button #click="searchJob">
<img src="../assets/recruit/search.svg" alt="" />
</button>
</div>
</div>
<div
id="jobs"
class="job-item"
v-for="(item, index) in jobs"
:key="index"
:per-page="perPage"
:current-page="currentPage"
>
<router-link
tag="a"
:to="{ name: 'Detail', params: { id: item.id } }"
>
<h3 class="mleft-27">{{ item.Name }}</h3>
</router-link>
<div class="job-info flex-wrap">
<div class="job-info-left pleft-27 flex-wrap">
<div>
<img src="../assets/recruit/years.svg" alt="" />
<b>{{ item.Exprerience }}</b>
</div>
<div>
<img src="../assets/recruit/luong.svg" alt="" />
<b>{{ item.Salary }}</b>
</div>
<div>
<img src="../assets/recruit/diadiem.svg" alt="" />
<b>{{ item.Headequarters }}</b>
</div>
</div>
<div>
<h6>{{ momentTime(item.updatedAt) }}</h6>
</div>
</div>
<div class="info-job flex-wrap">
<div class="list-info-job">
<li>{{ item.Content }}</li>
</div>
<router-link
tag="a"
:to="{ name: 'Detail', params: { id: item.id } }"
>
<button class="btn-detail">Xem chi tiết</button>
</router-link>
</div>
</div>
<h3 class="not-found" v-show="showNoData">Not Found</h3>
</div>
</div>
</div>
</template>
And here is the js logic I'm dealing with:
<script>
import "../assets/style.css";
import "../assets/job.css";
import job from "../locales/lang";
import request from "#/utils/request";
import moment from "moment";
export default {
name: "jobs",
components: {},
data() {
return {
totalJobs: null,
tickTime: "",
currentPage: 1,
showPag: true,
showNoData: false,
perPage: 4,
search: "",
page: "",
noData: [],
checks: [
"All",
"Developer",
"Tester",
"Designer",
"Support",
"Marketing",
"Other",
],
jobinfos: [],
showJobs: [],
selected: "All",
};
},
computed: {
},
watch: {
// selected(newVal) {
// if (newVal === "All") {
// return this.jobinfos;
// } else {
// return this.jobinfos.filter((i) => i.Genres === newVal);
// }
// },
},
async mounted() {
//API
this.getJobs();
// event enter
var self = this;
window.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
self.searchJob();
}
});
},
methods: {
//Call api job information by locale
async getJobs() {
await request
.get(`jobs?_locale=${this.$route.params.locale}`)
.then((response) => {
this.jobinfos = response.data;
this.showJobs = response.data;
this.totalJobs = response.data.length;
})
.catch((e) => {});
},
//Filter item jobs
selectFilter(item) {
this.selected = item;
if (this.selected == "All") {
this.showJobs = this.jobinfos;
} else {
this.showJobs = this.jobinfos.filter((i) => i.Genres === this.selected);
if (this.showJobs.length > 0) {
this.showPag = true;
this.showNoData = false;
} else {
this.showPag = false;
this.showNoData = true;
}
}
},
// method search by filter
},
};
</script>
As for the backend, I'm using 3rd party cms, it probably already provides enough query params for me, please give me some pseudocode or a way so that when I click on the checkbox, it will call the api to return the records according to the check box ^^ thanks

Having trouble using database data in vue app?

Hi I've been trying to get multiple profiles from a database and use v-for to try and display them all but whenever I try it doesn't work on and I can't figure out how to get it to use the data which should be getting imported.
Below is what the javascript file looks like.
import { projectFirestore } from "../Firebase/Config";
import { ref } from "vue"
const getPremium = () => {
const Premium = ref([])
const error = ref(null)
const load = async () => {
try{
const res = await projectFirestore.collection('Premium').get()
Premium.value = res.docs.map(doc => {
console.log(doc.data())
return {...doc.data(), id: doc.id}
})
}
catch (err){
error.value = err.message
console.log(error.value)
}
}
return { Premium, error, load}
}
export default getPremium
And heres the vue where I am trying to use v-for to create the profiles.
<script>
import getPremium from "../Composables/getPremium.js";
const {Premium, error, load} = getPremium();
load();
</script>
<template>
<div v-for =" Premium in getPremiums" :key="Premium.id" >
<div class= "hover:scale-105 transition ease-in-out duration-300 bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-900 text-neutral-400 font-bold rounded-xl">
<br>
<p>{{ Premium.Name }}</p>
<img src="../assets/Sample-pic.png" class="object-contain ml-6 w-60 h-80 transition ease-in-out duration-300">
<div class="grid grid-cols-2 grid-rows-fit text-left ml-6">
<p>Location:</p>
<p>{{ Premium.Location }}</p>
<p>Rates:</p>
<p>{{ Premium.Rates }} /hr</p>
<p>Phone:</p>
<p>{{ Premium.Phone }}</p>
</div><br>
</div>
</div>
</template>
I'm not sure what I need to do from here to get it to work properly, I'm new to using databases and any help would be greatly appreciated, Thankyou
Besides, you are trying to iterate through function, you iterate it using in operator. In operator iterates object properties, no arrays. And created variables call using first small character not big one like you have in code "Premium".
<script>
import getPremium from "../Composables/getPremium.js";
const { premiumList, error, load } = getPremiumList(); // Name variables, functions with first small letter not big one. Interfaces, Clases with first big letter. Add "List" or "s" in end of name so everyone knows it is array.
load();
</script>
<template>
<main>
<div v-for="item of premiumList" :key="item.id">
<div
class="hover:scale-105 transition ease-in-out duration-300 bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-900 text-neutral-400 font-bold rounded-xl"
>
<br />
<p>{{ item.Name }}</p>
<img
src="../assets/Sample-pic.png"
class="object-contain ml-6 w-60 h-80 transition ease-in-out duration-300"
/>
<div class="grid grid-cols-2 grid-rows-fit text-left ml-6">
<p>Location:</p>
<p>{{ item.Location }}</p>
<p>Rates:</p>
<p>{{ item.Rates }} /hr</p>
<p>Phone:</p>
<p>{{ item.Phone }}</p>
</div>
<br />
</div>
</div>
</main>
</template>
You are destructuring Premium from getPremium(), so that is what you should use in your v-for
<script>
import getPremium from "../Composables/getPremium.js";
const { Premium, error, load } = getPremium();
load();
</script>
<template>
<main>
<div v-for="item in Premium" :key="item.id">
<div
class="hover:scale-105 transition ease-in-out duration-300 bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-900 text-neutral-400 font-bold rounded-xl"
>
<br />
<p>{{ item.Name }}</p>
<img
src="../assets/Sample-pic.png"
class="object-contain ml-6 w-60 h-80 transition ease-in-out duration-300"
/>
<div class="grid grid-cols-2 grid-rows-fit text-left ml-6">
<p>Location:</p>
<p>{{ item.Location }}</p>
<p>Rates:</p>
<p>{{ item.Rates }} /hr</p>
<p>Phone:</p>
<p>{{ item.Phone }}</p>
</div>
<br />
</div>
</div>
</main>
</template>

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,
})

How to use dynamic route matching to create a route with the object.id given in a vuex state?

As mentioned above I'm trying to generate a route that is based on the property : assetId of the objects asset stored in a vuex state.
${asset.assetId} does not work and I do not find a suitable solution. How to define the route properly?
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
selectionVariants: [
'All',
'Portfolio'
]
}
},
computed: {
...mapState(['portfolio'])
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div class="container">
<div id="intro">
<h1>Portfolio</h1>
<p>Here your actual deposits are shown.</p>
</div>
<b-form-group label="Selection Filter" label-for="table-style-variant" label-cols-lg="2">
<b-form-select
v-model="tableVariant"
:options="selectionVariants"
id="table-style-variant"
>
<template #first>
<option value="">Choose a selection</option>
</template>
</b-form-select>
</b-form-group>
<b-table striped hover :items="portfolio">
<template #cell(assetName)="asset">
<router-link :to="`/market/${asset.assetId}`">
{{ asset.value }}
</router-link>
</template>
</b-table>
</div>
</template>
The slot data is not the asset, there is other information inside of it:
<template #cell(assetName)="slotData">
<router-link :to="`/market/${slotData.item.assetId}`">
{{ slotData.value }}
</router-link>
</template>
slotData.item is the asset object

Categories

Resources