Suspense not working with layouts in vue 3 - vue.js

I have a view router with meta, where the layouts of each page are indicated. The problem is that suspense is triggered only once after a reboot. When switching to the same layout via router-link, it does not cause suspense. If you go through different layouts suspense works as it should. I can't figure out what the problem might be.
App.vue:
<script setup>
import {RouterView, useRoute, useRouter} from 'vue-router';
import {computed} from "vue";
import NotifiactionBlock from "./views/./components/blocks/NotifiactionBlock.vue";
import LoadSpinner from "./views/components/elements/spinners/LoadSpinner.vue";
const router = useRouter();
const route = useRoute();
const layout = computed(() => {
const {meta} = useRoute();
return meta.layout ?? 'default-layout';
});
</script>
<template>
<div>
<component :is="layout">
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition name="page" mode="out-in">
<Suspense>
<component :is="Component"></component>
<template #fallback>
<load-spinner></load-spinner>
</template>
</Suspense>
</Transition>
</template>
</RouterView>
</component>
<NotifiactionBlock></NotifiactionBlock>
</div>
</template>
one of the pages (HomeView):
<template>
<div class="images-list">
<div class="header-block">
<h2>Recommended images for you</h2>
</div>
<images-list :images="store.images"></images-list>
</div>
</template>
<script async setup>
import ImagesList from "../components/blocks/ImagesList.vue";
import {useImageStore} from "../../stores/image";
import LoadSpinner from "../components/elements/spinners/LoadSpinner.vue";
const store = useImageStore();
await store.setRecommendedImages();
</script>
async method in HomeView from pinia:
async function setRecommendedImages() {
const response = await requestRecommendedImages();
images.value = response.data.data;
}
vue router:
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
linkActiveClass: 'active',
routes: [
{
path: '/',
name: 'home',
component: HomeView,
meta: {
layout: MainLayout,
}
},
{
path: '/images/:id',
name: 'image',
component: ImageView,
},
{
path: '/register',
name: 'register',
component: RegisterView,
meta: {
layout: AuthLayout,
}
},
{
path: '/login',
name: 'login',
component: LoginView,
meta: {
layout: AuthLayout,
}
},
{
path: '/liked',
name: 'liked',
component: LikedView,
meta: {
layout: MainLayout,
}
},
{
path: '/profile',
name: 'profile',
component: ProfileView,
meta: {
layout: DefaultLayout,
}
},
{
path: '/users/:id',
name: 'users',
component: UserView,
meta: {
layout: MainLayout,
}
},
{
path: '/top',
name: 'top',
component: TopView,
meta: {
layout: MainLayout,
}
}
]
})

Did you try to add timeout on Suspense ?
<RouterView v-slot="{ Component }">
<Suspense>
<component :is="layout">
<Suspense timeout="0">
<template #default>
<component :is="Component" />
</template>
<template #fallback>
<load-spinner></load-spinner>
</template>
</Suspense>
</component>
</Suspense>
</RouterView>

Related

Missing required prop: "slug" - Vue 3

Im build project on forntend vue and backend django. I stuck with problem what i can't resolv.
I trying to send data from ProjectsView.vue to ProjectDetailView.vue. Im trying to use props but i get error:
[Vue warn]: Missing required prop: "slug"
ProjectsView.vue don't send data to ProjectDetailView.vue and axios throw a error
GET http://127.0.0.1:8000/api/v1/projectdetail/undefined/ 500 (Internal Server Error)
I can't find problem in this code.
this is my ProjectsView:
<template>
<div class="container d-flex d-xl-flex justify-content-xl-center">
<div class="d-flex d-sm-flex d-md-flex d-lg-flex d-xl-flex justify-content-center flex-wrap justify-content-sm-center justify-content-md-center justify-content-lg-center justify-content-xl-center">
<div v-for="prof in projects" v-bind:key="prof.id">
<div class="card" style="width: 285px;height: 400px;margin: 5px;border-radius: 15px;">
<div class="card-body text-center">
<img class="img-fluid" :src="prof.avatar" style="width: 150px;border-width: 1px;border-radius: 100px;" />
<h4 class="card-title">
<router-link
:to="{ name: 'projectdetail', params: { slug: prof.slug } }"
>{{ prof.title }}
</router-link>
<fa v-if="prof.is_online" icon="circle" data-bs-toggle="tooltip" title="Online" style="color: rgb(0,197,67);font-size: 12px;padding: 0px;margin-top: 0px;" /></h4>
<h6 class="text-muted card-subtitle mb-2">{{ prof.about }}</h6>
<h6 class="text-muted card-subtitle mb-2">{{ prof.last_online_at }}</h6>
<div v-if="prof.tahs">
<div class="d-inline-block" v-for="tag in prof.tahs.split(',')" v-bind:key="tag">
<span class="badge rounded-pill bg-secondary" style="margin: 1px;">{{tag}}</span>
</div>
</div>
<p class="card-text"></p><a class="card-link" href="#">Link</a><a class="card-link" href="#">Link</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import { useToast } from "vue-toastification";
import Project from '#/views/DetailProjectView.vue'
export default {
name: 'Projects',
setup() {
// Get toast interface
const toast = useToast();
return { toast }
},
data() {
return {
projects: [],
errors: [],
}
},
mounted() {
this.getItemsProjects()
},
components: {
Project,
},
methods: {
async getItemsProjects() {
this.$store.commit('setIsLoading', true)
axios
.get('/api/v1/projects/')
.then(response => {
this.projects = response.data
console.log(this.projects)
})
.catch(error => {
console.log(error)
})
this.$store.commit('setIsLoading', false)
},
}
}
</script>
and this is my ProjectDetailView.vue
<template>
<div>working? {{slug}}
</div>
</template>
<script>
import axios from 'axios'
import { useToast } from "vue-toastification";
export default {
name: 'Project',
setup() {
// Get toast interface
const toast = useToast();
return { toast }
},
data() {
return {
project: [],
errors: [],
}
},
mounted() {
this.getItemsProjects()
},
props: {
slug: {
type: String,
required: true,
},
},
methods: {
async getItemsProjects() {
this.$store.commit('setIsLoading', true)
axios
.get(`/api/v1/projectdetail/${this.slug}`)
.then(response => {
this.project = response.data
console.log(this.project)
})
.catch(error => {
console.log(error)
})
this.$store.commit('setIsLoading', false)
},
}
}
</script>
and my router:
import { createRouter, createWebHistory } from 'vue-router'
import store from '../store'
import HomeView from '../views/HomeView.vue'
import Signup from '../views/SignupView.vue'
import Login from '../views/LoginView.vue'
import Dash from '../views/DashView.vue'
import Myacc from '../views/MyAccView.vue'
import Profile from '../views/ProfileView.vue'
import Projects from '../views/ProjectsView.vue'
import ProjectDetail from '../views/DetailProjectView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/signup',
name: 'signup',
component: Signup
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/dash',
name: 'dash',
component: Dash,
meta: {
requiredLogin: true
}
},
{
path: '/myacc',
name: 'myacc',
component: Myacc,
meta: {
requiredLogin: true
}
},
{
path: '/profile',
name: 'profile',
component: Profile,
meta: {
requiredLogin: true
}
},
{
path: '/projects',
name: 'projects',
component: Projects,
meta: {
requiredLogin: true
}
},
{
path: '/project/:slug',
name: 'projectdetail',
component: ProjectDetail,
meta: {
requiredLogin: true,
}
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiredLogin) && !store.state.isAuthenticated) {
next('/login')
} else {
next()
}
})
export default router
how to fix this bug? thank you for advice.. im new in vue
edit

vuejs3 Uncaught (in promise) RangeError: Maximum call stack size exceeded

hello i am a vuejs newbie, what i want to do actually looks simple but i couldn't figure it out.
when a member logs in
both
I want "Dashboard.vue" to be loaded as well as "home.vue" to be loaded, I've done it somehow, but
"http://localhost/home"
when i write
I get the following error.
how can i solve this?
Uncaught (in promise) RangeError: Maximum call stack size exceeded
at pushWithRedirect
app.js
require('./bootstrap');
import {createApp} from 'vue';
import App from './App.vue'
import AdminRouter from './Admin/router';
createApp(App)
.use(AdminRouter )
.use(store)
.mount("#app");
App.vue
<template>
<div class="min-h-screen bg-gray-100">
<main>
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component"/>
</transition>
</router-view>
</main>
</div>
</template>
router/index.js
import {createRouter, createWebHistory} from 'vue-router'
import routes from './routes.js'
import store from '../../store'
const router = createRouter({
history: createWebHistory(),
routes
})
router.beforeEach((to, from, next) => {
if (store.getters.user) {
if (to.matched.some(route => route.meta.guard === 'guest')) next({name: 'admin'})
else next();
} else {
if (to.matched.some(route => route.meta.guard === 'auth')) next({name: 'login'})
else next();
}
})
export default router;
routes.js
export default [
{
path: "",
component: () => import('../layout/Dashboard.vue'),
name: 'admin',
redirect: {name: 'home'},
meta: {guard: 'auth'},
children: [
{
path: "/",
component: () => import('../Views/Home.vue'),
name: "home",
meta: {guard: 'auth'}
},
{
path: "user",
component: () => import('../Views/User/UserList.vue'),
name: "user",
meta: {guard: 'auth'}
},
]
},
{
path: '/:pathMatch(.*)*',
redirect: '/home',
}
];

How to open this route in new window?

if (success) {
context.bookingData.city = context.city;
context.bookingData.departureFlightDate = context.departureFlightDate;
context.bookingData.arrivalFlightDate = context.arrivalFlightDate;
context.bookingData.idParkingService = context.idParkingService;
context.bookingData.Price = context.bookings.price.items.data.price;
//const booking = JSON.stringify(context.bookingData);
context.addBookingData(context.bookingData);
context.$router.push({
path: "parkplatz-buchen-schritt-2",
});
}
how to open this path in new window or tab at the same time I have to pass context data to the destination
To open the route in a new window
Home.vue
<template>
<button v-on:click="openInNewWindow()">Open in new window</button>
</template>
<script>
export default {
methods: {
openInNewWindow(){
let routeData = this.$router.resolve({
path: '/about',
query: {
city : 'test',
departureFlightDate : 'test',
arrivalFlightDate : 'test',
idParkingService : 'test',
price : 'test'
}
});
window.open(routeData.href, '_blank');
}
}
}
</script>
Router
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
{{this.$route.query.city}}
</div>
</template>
<script>
export default {
mounted(){
console.log(this.$route.query)
}
}
</script>

Quasar framework : Navigation guards with file boot doesn't?

i just make register app to test login , register and navigation guards but always i can open the link i will put the code to help me
router/routes.js
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{
path: '',
meta:{signIn:true},
component: () => import('pages/Index.vue')
},
{
path: 'login',
component: () => import('pages/auth.vue')
},
]
}
]
// Always leave this as last one
if (process.env.MODE !== 'ssr') {
routes.push({
path: '*',
component: () => import('pages/Error404.vue')
})
}
export default routes
this is file boot :boot/router-auth.js
// import something here
import {firebaseAuth} from "boot/firebase"
// "async" is optional
export default ({ router/* app, router, Vue, ... */ }) => {
// something to do
router.beforeEach((to,from,next) =>{
if(to.matched.some(route =>route.meta.signIn)){
if(firebaseAuth.currentUser)
{
next()
}else{
//next({ path: "/auth" })
//Router.push("/auth")
console.log("maldkds");
next({path:"/login"})
}
}
next()
//{ path: "/login" }
})
}
and this is my MainLayout.vue
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-toolbar>
<q-toolbar-title class="text-center">
{{title}}
</q-toolbar-title>
<div class="row">
<q-btn flat dense round v-if="!userDetails.userId" color="white" icon="person" no-caps label="Login" />
<q-btn flat dense round v-else color="white" icon="person" no-caps #click="logoutUser" >Logout <br/> {{userDetails.name}} </q-btn>
</div>
<div></div>
</q-toolbar>
</q-header>
<q-page-container>
<router-view />
</q-page-container>
</q-layout>
</template>
<script>
import {mapState,mapActions} from "vuex"
export default {
name: 'MainLayout',
data () {
return {
}
},
computed:{
...mapState("store",["userDetails"]),
title(){
if(this.$route.fullPath=="/"){
return "Register App"
} else if(this.$route.fullPath=="/login"){
return "Auth"
}
}
},
methods:{
...mapActions("store",["logoutUser"]),
}
}
</script>
<style>
.q-toolbar .q-btn{
line-height: 1.2;
}
</style>
Finally i have always this error:
NavigationDuplicated
_name: "NavigationDuplicated" name: "NavigationDuplicated" message: "Navigating to current location ("/login") is not allowed"
I resolved by creating const Router and exporting, after Route, use beforeEach with you validate.
import Vue from 'vue'
import VueRouter from 'vue-router'
import { isAuth, isAdmin, isUser } from "./auth";
import routes from './routes'
Vue.use(VueRouter)
const Router = new VueRouter({
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE,
scrollBehavior: () => ({ y: 0 }),
routes
})
Router.beforeEach((to,from, next) => {
to.matched.some( route =>{
if(route.meta.requiresAuth){
if(!isAuth()){
next({ path: '/' })
}
}
next()
})
})
export default Router
Put debugger as the first line of your router.beforeEach() and examine what is inside to.matched. It is possible that it is matching the route with path '' and it may redirect /login to /login, aka. redirect loop.

Vue Component Issue

I have this page I want to try out Vue Router with Vue Components. I cant figure out whats wrong. I am getting an error Uncaught TypeError: Cannot read property 'name' of undefined at this line const App = new Vue.extend({})
<body>
<div id="app">
<router-view></router-view>
</div>
<template id="foo"> <h1>This is homepage</h1> </template>
<template id="bar"> <h1>This is Bar page</h1> </template>
</body>
//Vue.js v1.0.28
<script src="{{ asset( 'js/vue.js' ) }}"></script>
// vue-router v0.7.13
<script src="{{ asset( 'js/vue-router.js' ) }}"></script>
<script>
const router = new VueRouter()
const App = new Vue.extend({})
router.map({
'/': {
component: {
template: '#foo'
}
},
'/bar': {
component: {
template: '#bar'
}
},
})
router.start(App, '#app')
</script>
</html>
What am I doing wrong?
EDIT:
Okay, I have managed to get this working.
const Foo = Vue.component('foo', { template: '#foo' });
const Bar = Vue.component('bar', { template: '#bar' });
Vue.use(VueRouter)
const router = new VueRouter()
router.map({
'/foo': {
component: Foo
},
'/bar': {
component: Bar
},
})
var App = Vue.extend({})
router.start(App, 'body')
What I need now is to extract those templates from index.blade.php into their own files like Foo.vue and Bar.vue. How do I do that?
I am using Webpack to compile assets.
You could try to use vue-router v.v2.2.1 and you can check this official example https://github.com/vuejs/vue-hackernews-2.0 and here router code:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import { createListView } from '../views/CreateListView'
import ItemView from '../views/ItemView.vue'
import UserView from '../views/UserView.vue'
export default new Router({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: [
{ path: '/top/:page(\\d+)?', component: createListView('top') },
{ path: '/new/:page(\\d+)?', component: createListView('new') },
{ path: '/show/:page(\\d+)?', component: createListView('show') },
{ path: '/ask/:page(\\d+)?', component: createListView('ask') },
{ path: '/job/:page(\\d+)?', component: createListView('job') },
{ path: '/item/:id(\\d+)', component: ItemView },
{ path: '/user/:id', component: UserView },
{ path: '/', redirect: '/top' }
]
})