How do I make dynamic routing work in Vue? - vue.js

I got a project and when I use :to, my dynamic routes dont work..I also can't seem to pass params. The buttons supposed to be used as dynamic routes are declared in the v-for loop. I am using Quasar CLI / Vue3.
It'd be really awesome if some of you had an insight as to why this does not work. I get no errors, simply, when I hover over the button it shows no url as it normally would in a browser.
The view displaying items:
<template>
<div class="projects">
<div class="text-h3 project-title">
Favourite Projects <q-icon name="star" color="yellow" />
</div>
</div>
<div class="project-container">
<div class="q-pa-md project-card">
<q-card class="my-card single-projects no-shadow no-border-radius">
<q-img
src="https://res.cloudinary.com/practicaldev/image/fetch/s--9yBkqrjS--/c_imagga_scale,f_auto,fl_progressive,h_500,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nphrgz8yfnjylrwfr0yl.png"
>
<div class="absolute-bottom">
<div class="text-h6">
Damn good project
<q-icon name="star" color="yellow" />
</div>
<div class="text-subtitle2">by Big Boi</div>
</div>
</q-img>
<q-card-actions class="project-btns">
<q-btn
unelevated
size="md"
class="open-btn no-border-radius dropdown-btn"
>Open</q-btn
>
<q-btn
flat
no-caps
unelevated
size="md"
class="delete-btn no-border-radius"
color="red"
#click="confirm = true"
>Delete</q-btn
>
<q-dialog v-model="confirm" persistent class="dialog no-shadow">
<q-card>
<q-card-section class="row items-center">
<span class="q-ml-sm"
>Are you sure you want to delete this project?</span
>
</q-card-section>
<q-card-actions align="center">
<q-btn
flat
no-caps
label="Cancel"
color="black"
class="no-border-radius"
v-close-popup
/>
<q-btn
flat
no-caps
label="Delete"
color="red"
class="no-border-radius"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog>
</q-card-actions>
</q-card>
</div>
</div>
<div class="projects">
<div class="text-h3 project-title">All Projects</div>
</div>
<div v-for="item in items" :key="item.project" class="project-container">
<div class="q-pa-md project-card">
<q-card class="my-card single-projects no-shadow no-border-radius">
<q-img
src="https://res.cloudinary.com/practicaldev/image/fetch/s--9yBkqrjS--/c_imagga_scale,f_auto,fl_progressive,h_500,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nphrgz8yfnjylrwfr0yl.png"
>
<div class="absolute-bottom">
<div class="text-h6">{{ item.project }}</div>
<div class="text-subtitle2">by {{ item.author }}</div>
</div>
</q-img>
<q-card-actions class="project-btns">
<q-btn
:to="{
name: 'project',
params: {
project: item.project,
},
}"
unelevated
size="md"
class="open-btn no-border-radius dropdown-btn"
>Open
</q-btn>
<q-btn
flat
no-caps
unelevated
size="md"
class="delete-btn no-border-radius"
color="red"
#click="confirm = true"
>Delete</q-btn
>
<q-dialog v-model="confirm" persistent class="dialog no-shadow">
<q-card>
<q-card-section class="row items-center">
<span class="q-ml-sm"
>Are you sure you want to delete this project?</span
>
</q-card-section>
<q-card-actions align="center">
<q-btn
flat
no-caps
label="Cancel"
color="black"
class="no-border-radius"
v-close-popup
/>
<q-btn
flat
no-caps
label="Delete"
color="red"
class="no-border-radius"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog>
</q-card-actions>
</q-card>
</div>
</div>
</template>
<script lang="ts">
import { ref } from 'vue';
export default {
name: 'myProjects',
setup() {
return {
confirm: ref(false),
items: [
{ project: 'good project', id: '302a49g8Aa43', author: 'Josh' },
{ project: 'okay fine', id: '65at9g847a11', author: 'Tray' },
{ project: 'let me see', id: '538s3fg4782f', author: 'Martin' },
],
};
},
};
</script>
This is also my router where I do the routing:
{
path: '/project/:id',
props: true,
component: () => import('layouts/SingleProLayout.vue'),
children: [{ path: '', component: () => import('pages/Project.vue') }],
},

Try wrapping the q-btn inside a router-link https://router.vuejs.org/api/

Related

Pass component as prop in object param

I have the following Card Grid set up in Vue3:
<template>
<div class="max-w-5xl mx-auto">
<div class="ml-20 pr-72">
<h2 class="text-4xl pb-8">{{ title }}</h2>
<p class="font-normal text-base pb-20">{{ description }}</p>
</div>
<div class="grid grid-cols-2 gap-5">
<div
v-for="card in cards"
:key="card.title"
class="rounded-xl py-6 px-5 bg-cover bg-center flex flex-col justify-between items-start h-60"
:style="{ backgroundImage: `url(${card.imageUrl})` }"
>
<div
class="rounded-full p-4 bg-gradient-to-r from-prospire-blue to-prospire-light-blue"
>
{{ card.icon }}
</div>
<p class="text-white font-light text-2xl">{{ card.title }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
title: string;
description: string;
cards: any[];
}>();
</script>
<style scoped></style>
Which i call using:
<CardGrid
:title="$t('aboutUs.cardGrid.title')"
:description="$t('aboutUs.cardGrid.description')"
:cards="[
{
title: $t('aboutUs.cardGrid.cards[0].title'),
imageUrl:
'https://images.pexels.com/photos/5595573/pexels-photo-5595573.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
icon: <BeakerIcon />,
},
]"
/>
This however gives me a BeakerIcon' refers to a value, but is being used as a type here. Did you mean 'typeof BeakerIcon'? error.. Is it possible to pass a 3rd party component (icon in this case), to a component in a way?
One option is to pass card.icon as a string, such as 'BeakerIcon' and do
<div class="rounded-full p-4 bg-gradient-to-r from-prospire-blue to-prospire-light-blue">
<component :is="card.icon" />
</div>
...granted the icon you pass has been registered as a component.
It also work without registering the component globally.
Here is an example on sfc.vuejs.org passing the imported components in the props.

vue2 + element ui - how to setting dynamic v-model

Let's make the string(categoryV3) an array with ','
and create array length.
and icon-add click add ..
but v-model not working.. and i don't know add .........
I am not good at speaking English.
Please give me your opinion.
<template>
<el-table
:data="tableData"
>
<el-table-column type="selection" width="55" align="center"> </el-table-column>
<el-table-column props="category" label="category" show-overflow-tooltip>
<template slot-scope="{row}">
<div v-for="(item, index) in generateArray(row.categoryV3)" :key="index">
<el-select class="filter-item select1" filterable v-model="item[index]" placeholder="-">
<el-option
v-for="item in options"
:key="item.value"
:label="`${item.value}. ${item.label}`"
:value="item.value"
/>
</el-select>
<span class="tmp-icon icon-add"><i class="el-icon-circle-plus-outline"></i></span>
<span class="tmp-icon icon-remove" v-show="generateArray(row.categoryV3).length > 1"
><i class="el-icon-remove-outline"></i
></span>
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [{
categoryV3:"option1,option2"
}, {
categoryV3:""
}],
options: [{
value: 'Option1',
label: 'Option1'
}, {
value: 'Option2',
label: 'Option2'
}]
};
},
methods: {
generateArray(val) {
return val.split(',');
},
}
};
</script>
data add 'categoryV3: []'
<div v-for="(item, index) in categoryV3[scope.$index]" :key="index">
<el-select class="filter-item select1" filterable v-model="categoryV3[scope.$index][index]" placeholder="-">
<el-option
v-for="item in relationCode"
:key="item.code_value"
:label="`${item.code_value}. ${item.code_name}`"
:value="item.code_value"
/>
</el-select>
<span class="tmp-icon icon-add" #click="addModel(categoryV3[scope.$index], '')"
><i class="el-icon-circle-plus-outline"></i
></span>
<span
class="tmp-icon icon-remove"
#click="removeModel(categoryV3[scope.$index], index)"
v-show="categoryV3[scope.$index].length > 1"
><i class="el-icon-remove-outline"></i
></span>
</div>

TinyMce breaking TailwindCss animation when loaded inside a slide-over element

I have a simple Vue3 application that is making heavy use of TailwindUI components. I'm trying to place a TinyMce editor inside of a slide over component and that works fine. The issue is the entry animation.
On first entry, it slides in like it's supposed to. However, if it is closed and reopened the entry animation is gone. The whole time the exit animation continues to work without issue. Is there a way I can do this and keep the animation intact?
Here is a CodeSandBox with the issue reproduced in it's simplest form.
Here is the relevant code:
App.vue
<template>
<button #click="open = true">Open Menu</button>
<SlideOver :open="open" #close="open = false" />
</template>
<script>
import { ref } from "vue";
import SlideOver from "./components/slide-over.vue";
export default {
name: "App",
components: {
SlideOver,
},
setup() {
const open = ref(false);
return { open };
},
};
</script>
slide-over.vue
<!-- This example requires Tailwind CSS v2.0+ -->
<template>
<TransitionRoot as="template" :show="open">
<Dialog
as="div"
static
class="fixed inset-0 overflow-hidden"
#close="$emit('close')"
:open="open"
>
<div class="absolute inset-0 overflow-hidden">
<DialogOverlay class="absolute inset-0" />
<div class="fixed inset-y-0 right-0 pl-10 max-w-full flex">
<TransitionChild
as="template"
enter="transform transition ease-in-out duration-500 sm:duration-700"
enter-from="translate-x-full"
enter-to="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leave-from="translate-x-0"
leave-to="translate-x-full"
>
<div class="w-screen max-w-md">
<div
class="h-full flex flex-col py-6 bg-white shadow-xl overflow-y-scroll"
>
<div class="px-4 sm:px-6">
<div class="flex items-start justify-between">
<DialogTitle class="text-lg font-medium text-gray-900">
Panel title
</DialogTitle>
<div class="ml-3 h-7 flex items-center">
<button
class="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
#click="$emit('close')"
>
<span class="sr-only">Close panel</span>
<XIcon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div class="mt-6 relative flex-1 px-4 sm:px-6">
<TinyMceEditor api-key="no-api-key" />
</div>
</div>
</div>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
<script>
import {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
} from "#headlessui/vue";
import { XIcon } from "#heroicons/vue/outline";
import TinyMceEditor from "#tinymce/tinymce-vue";
export default {
components: {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
XIcon,
TinyMceEditor,
},
props: {
open: {
type: Boolean,
default: false,
},
},
setup() {},
};
</script>
In my opinion, this is a problem with loading the TinyMce Editor (I don't know exactly what the problem is). I added a delay in loading the editor after opening the modal using watchEffect based on the props open with setTimeout in it and v-if on the TinyMceEditor tag. It may not be a perfect and aesthetic solution, but the animation works smoothly.
Here is a code in codesandbox.io.
And code here: slide-over.vue (App.vue stays the same)
<template>
<TransitionRoot as="template" :show="open">
<Dialog
as="div"
static
class="fixed inset-0 overflow-hidden"
#close="$emit('close')"
:open="open"
>
<div class="absolute inset-0 overflow-hidden">
<DialogOverlay class="absolute inset-0" />
<div class="fixed inset-y-0 right-0 pl-10 max-w-full flex">
<TransitionChild
as="template"
enter="transform transition ease-in-out duration-500 sm:duration-700"
enter-from="translate-x-full"
enter-to="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leave-from="translate-x-0"
leave-to="translate-x-full"
>
<div class="w-screen max-w-md">
<div
class="h-full flex flex-col py-6 bg-white shadow-xl overflow-y-scroll"
>
<div class="px-4 sm:px-6">
<div class="flex items-start justify-between">
<DialogTitle class="text-lg font-medium text-gray-900">
Panel title
</DialogTitle>
<div class="ml-3 h-7 flex items-center">
<button
class="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
#click="$emit('close')"
>
<span class="sr-only">Close panel</span>
<XIcon class="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div class="mt-6 relative flex-1 px-4 sm:px-6">
<TinyMceEditor v-if="loadEditor" api-key="no-api-key" />
</div>
</div>
</div>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
<script>
import { ref, watchEffect } from "vue";
import {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
} from "#headlessui/vue";
import { XIcon } from "#heroicons/vue/outline";
import TinyMceEditor from "#tinymce/tinymce-vue";
export default {
components: {
Dialog,
DialogOverlay,
DialogTitle,
TransitionChild,
TransitionRoot,
XIcon,
TinyMceEditor,
},
props: {
open: {
type: Boolean,
default: false,
},
},
setup(props) {
const loadEditor = ref(false);
watchEffect(() => {
if (props.open) {
setTimeout(function () {
loadEditor.value = true;
}, 10);
} else {
loadEditor.value = false;
}
});
return { loadEditor };
}
};
</script>

Looping a new <q-item> with <q-popoup-edit> nested doesn't work in quasar framework

I have been searching for solution for my problem, but still not resolved :')..
Anyway, Thanks so much for the help, Masters :)
So,
I have a button for add new task with this code inside the q-item-section , but when I clicked it, it is not adding new item as a expected..
<q-btn
#click="addMore"
class="text-weight-bolder"
label="Add"
size="14px"
color="white"
flat
icon-right="mdi-plus-circle-outline"
/>
By clicking above button, it will shows a new item (by using following code below), which is needed to be filled by users
<q-item v-for="task in todoOthers2" :key="task">
<q-item-section avatar>
<div class="cursor-pointer size-16px">
{{ task.time }}
<q-popup-edit v-model="time" class="bg-grey-2 text-white" :cover="false" :offset="[0, 10]" v-slot="scope">
<q-input filled v-model="todoOthers2.time" mask="time" :rules="['time']">
<template v-slot:append>
<q-icon name="access_time" class="cursor-pointer text-primary">
<q-popup-proxy transition-show="scale" transition-hide="scale">
<q-time v-model="time">
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat />
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</q-popup-edit>
</div>
</q-item-section>
<q-item-section no-wrap >
<div class="cursor-pointer text-weight-bolder" style="width: 100px">
{{task.activity}}
<q-popup-edit v-model="todoOthers2.activity" class="bg-grey-2 text-white" :cover="false" :offset="[0, 10]" v-slot="scope">
<q-input v-model="scope.value" dense autofocus counter #keyup.enter="scope.set">
<template v-slot:append>
<q-icon name="edit" />
</template>
</q-input>
</q-popup-edit>
</div>
</q-item-section>
<q-item-section top side>
<div class="text-grey-8 q-gutter-xs">
<q-btn size="12px" color="secondary" flat dense round icon="mdi-check-circle" />
<q-btn size="12px" color="negative" flat dense round icon="mdi-delete" />
</div>
</q-item-section>
</q-item>
Here is the setup()
setup() {
const todoOthers2 = ref({
activity:"Add Your Task Here",
time:"08:00"
function addMore(){
todoOthers2.value.push({
activity: "Add Your Task Here",
time:"08:00"
})
return {
todoOthers2,
addMore,
}
}

Accessing Vuex data within dynamic _id.vue page - Vue/Nuxt

I want to access my user details stored in the state when visiting a dynamic (_id.vue) page. I have set the id of the page like this after using the link to the page:
data() {
return {
selectedTutor: null,
id: this.$route.params.id,
}
},
But now I need to define the selectedTutor by searching the id within the data in the vuex state. I was trying to do something like this:
created() {
this.selectedTutor = this.$store.state.tutors.find((tutor) => tutor.id === this.id)
},
But everything stays undefined. So when id equals to the id within the object, that object needs to be set as selectedTutor so I can access all the necessary data to be displayed on the page.
Here you can see the Vuex state
EDIT
_id.vue page
<template>
<div>
<section>
<div>
<h3>
{{ id }}
</h3>
</div>
</section>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'TutorPage',
/* eslint-disable vue/require-prop-types */
layout: 'app',
props: ['id'],
middleware: 'auth',
data() {
return {
selectedTutor: null,
}
},
computed: {
...mapState(['tutors']),
},
created() {
this.selectedTutor = this.$store.state.tutors.find(
(tutor) => tutor.id === this.id
)
},
}
</script>
MainResult Page
<base-grid>
<ul id="tutors" class="grid grid-cols-2 gap-6">
<tutor-item
v-for="tutor in tutors"
:id="tutor.id"
:key="tutor.id"
:name="tutor.attributes.name"
:rate="student.hourlyRate"
:subject="student.subject"
:description="student.biography"
:profile-image="student.imageUrl"
:image-alt="student.imageAlt"
:age="student.age"
:rating="student.rating"
:total-reviews="student.reviewCount"
class="overflow-hidden bg-white border rounded-lg shadow-md"
>
</tutor-item>
</ul>
</base-grid>
Tutor Item (the resultcard)
<template>
<div>
<li>
<div class="flex">
<div class="w-2/3">
<img
class="flex-shrink-0 object-cover w-full h-64 mx-auto bg-gray-200"
:src="profileImage"
:alt="imageAlt"
/>
</div>
<div class="p-6">
<div
class="text-xs font-semibold leading-snug tracking-wide text-gray-500 uppercase"
>
{{ subject }} • {{ age }} jaar
</div>
<NuxtLink :to="'/tutors/' + id">
<h4 class="text-lg font-semibold leading-5 tracking-wide">
{{ name }}
</h4>
</NuxtLink>
<div class="mt-2">
{{ rate }}€
<span class="text-sm text-gray-600">per uur</span>
</div>
<div class="mt-2">
<span class="font-semibold text-light-blue-800"
>{{ rating }}/5 sterren</span
>
<span class="text-sm text-gray-600 truncate">
(na {{ totalReviews }} reviews)
</span>
</div>
<div class="mt-2">{{ description }}</div>
<div>
<div class="mt-4 text-sm font-semibold text-gray-600">
<span>MA</span>
<span
class="inline-block leading-7 text-center text-gray-100 bg-yellow-400 bg-opacity-50 w-7 h-7 rounded-xl"
>DI</span
>
<span>WO</span>
<span
class="inline-block leading-7 text-center text-gray-100 bg-yellow-400 bg-opacity-50 w-7 h-7 rounded-xl"
>DO</span
>
<span
class="inline-block leading-7 text-center text-gray-100 bg-yellow-400 bg-opacity-50 w-7 h-7 rounded-xl"
>VR</span
>
<span>ZA</span>
<span>ZO</span>
</div>
</div>
</div>
</div>
</li>
</div>
</template>
<script>
export default {
/* eslint-disable vue/require-prop-types */
name: 'TutorItem',
props: [
'id',
'firstName',
'lastName',
'name',
'rate',
'subject',
'age',
'rating',
'totalReviews',
'description',
'profileImage',
'imageAlt',
],
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
},
tutorsDetailsLink() {
return this.$route.path + '/' + this.id
},
},
}
</script>
<style></style>
EDIT
Whoops, what a mistake. It was returning the id as a string but I needed a number. This is why it returned undefined. It is solved now! Thanks