Problems with the work of preloader vue.js - vue.js

I'm trying to make showing preloader when i go from one component to another. I use this preloader. I create file loader.js and write there:
import Vue from 'vue';
import Loading from 'vue-loading-overlay';
import 'vue-loading-overlay/dist/vue-loading.css';
Vue.use(Loading);
let loader = Vue.$loading.show({
loader: 'dots',
color: '#5D00FF',
zIndex: 999,
});
function loaderStart() {
loader;
}
function loaderEnd() {
loader.hide();
}
export default {loaderStart, loaderEnd}
loader,js i import to the index.js and there i write when i want to call loader start but it does not starting(withoun if in beforeResolve preloader is working). Here is index.js:
import Vue from 'vue'
import Router from 'vue-router'
import Authorization from '#/components/Authorization'
import Main from '#/components/Main'
import loader from './loader'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: 'Main',
component: Main,
},
{
path: '/authorization',
name: 'Authorization',
component: Authorization
}
]
})
router.beforeResolve((to, from, next) => {
if(to.path) {
loader.loaderStart()
}
next()
});
router.afterEach((to, from) => {
loader.loaderEnd()
});
export default router;
Please, help me find the problem

Your current loader will appear just once because you called show method once as well. You need to invoke show method every loaderStart call and store the loader:
let loader = null;
function loaderStart() {
// it would be better to extract these values as constants
loader = Vue.$loading.show({
loader: 'dots',
color: '#5D00FF',
zIndex: 999,
});
}
function loaderEnd() {
loader.hide();
}
Probably you have some async components since you added loader to routing logic, so you should use the beforeEach hook instead of the beforeResolve one.
router.beforeEach((to, from, next) => {
loader.loaderStart()
next()
});
router.afterEach((to, from) => {
loader.loaderEnd()
});
Loader API docs (show method)
Vue-router guards
Vue-router navigation flow

Related

Routes.js call function from composable

I am working on my routes.js and I want to check if user has access to module before redirecting them by calling the function hasAccess() from a composable but it's giving me this warning:
[Vue warn]: inject() can only be used inside setup() or functional components.
routes.js
import { createWebHistory, createRouter } from "vue-router";
import useFN from "./composables/FN"
const routes = [
// define routes here
]
const router = createRouter({
history: createWebHistory(),
routes,
})
const { hasAccess } = useFN() // use function from composable
router.beforeEach(async (to, from, next) => {
console.log(await hasAccess()); // call function from composable
next()
}
export default router;
How can I call a function from composable if I don't have setup()?
Usually I don't have any issues using composables I create for my self as they are usually very basic, but according to Vue 3 - inject() can only be used inside setup() or functional components
composable your composable appears to be using provide/inject which is intended within a setup() method
I will like to propose another approach if viable.
You could call a method(action), getter inside your store instead, if you are using one.
I often use that perform checks before navigating to a route for example if a user account is suspended or the authenticated user does not have the intended privilege
import { createRouter, createWebHistory } from 'vue-router'
import store from '#/store'
...
const account = {
'ACTIVE': 1,
'RESTRICTED': 2,
'DECLINED': 0
}
const routes = [
{
path: '/some-path',
component: () => import('../components/...'),
props: route => ({ reference: route.query.reference }),
meta: { requiresAuth: true },
beforeEnter: async (to, from) => {
if (store.getters['Auth/authUser'].account_status == account.RESTRICTED) {
return false
}
return true
},
},
...
]
```

Vue: Can't access Pinia Store in beforeEnter vue-router

I am using Vue 3 including the Composition API and additionally Pinia as State Management.
In the options API there is a method beforeRouteEnter, which is built into the component itself. Unfortunately this method does not exist in the composition API. Here the code, which would have been in the beforeRouteEnter method, is written directly into the setup method. However, this means that the component is loaded and displayed first, then the code is executed and, if the check fails, the component is redirected to an error page, for example.
My idea was to make my check directly in the route configuration in the beforeEnter method of a route. However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js.
Console Log
Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
Router.js
import { useProcessStore } from "#/store/process";
const routes: Array<RouteRecordRaw> = [
{
path: "/processes/:id",
name: "ProcessView",
component: loadView("ProcessView", "processes/"),
beforeEnter: () => {
const processStore = useProcessStore();
console.log(processStore);
},
children: [
{
path: "steer",
name: "ProcessSteer",
component: loadView("ProcessSteer", "processes/")
},
{
path: "approve/:code",
name: "ProcessApprove",
component: loadView("ProcessApprove", "processes/")
}
]
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
main.js
import { createApp } from "vue";
import "#/assets/bundle-bootstrap.css";
import App from "#/App.vue";
import { createPinia } from "pinia";
import router from "#/router";
import SvgIcon from "#/components/SvgIcon.vue";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.use(router);
app.component("SvgIcon", SvgIcon);
router.isReady().then(() => {
app.mount("#app");
});
However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js
Before what? Pinia instance is created with const pinia = createPinia(); after the router module is imported - while it is imported, all side-effects including the call to createRouter() are executed. Once the router is created it begins it's initial navigation (on client - on server you need to trigger it with router.push()) - if you happen to be at URL matching the route with guard that is using Pinia store, the useProcessStore() happens before Pinia is created...
Using a store outside of a component
You have two options:
either you make sure that any useXXXStore() call happens after Pinia is created (createPinia()) and installed (app.use(pinia))
or you pass the Pinia instance into any useXXXStore() outside of component...
// store.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;
// router.js
import pinia from "#/store.js";
import { useProcessStore } from "#/store/process";
const routes: Array<RouteRecordRaw> = [
{
path: "/processes/:id",
name: "ProcessView",
component: loadView("ProcessView", "processes/"),
beforeEnter: () => {
const processStore = useProcessStore(pinia ); // <-- passing Pinia instance directly
console.log(processStore);
},
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
// main.js
import { createApp } from "vue";
import App from "#/App.vue";
import store from "#/store.js";
import router from "#/router";
const app = createApp(App);
app.use(store);
app.use(router);
router.isReady().then(() => {
app.mount("#app");
});
Hope this would be helpful.
Vue provide support for some functions in which we need store(outside of the components).
To fix this problem I just called the useStore() function inside the function provided by Vue(beforeEach) and it worked.
Reference : https://pinia.vuejs.org/core-concepts/outside-component-usage.html
Example :
import { useAuthStore } from "#/stores/auth";
.
.
.
.
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
router.beforeEach(async (to, from) => {
const authStore = useAuthStore();
// use authStore Here
});
I have same problem to access the store in "beforeEach" method for managing authorization.
I use this method in main.js, not in router.js. in router.js store is not accessible.
create pinia instance in piniCreate.js
//piniaCreate.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;
after that create my store in mainStore.js
import { defineStore } from 'pinia'
export const mainStore = defineStore('counter', {
state: () => {
return {
user: {
isAuthenticated: isAuthen,
}
}
},
actions: {
login(result) {
//...
this.user.isAuthenticated = true;
} ,
logOff() {
this.user.isAuthenticated = false;
}
}
});
Then I used beforeEach method in the main.js
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from "#/stores/piniaCreate";
import { mainStore } from '#/stores/mainStore';
import router from './router'
const app = createApp(App)
.use(pinia)
.use(router)
const store1 = mainStore();
router.beforeEach((from) => {
if (from.meta.requiresAuth && !store1.user.isAuthenticated) {
router.push({ name: 'login', query: { redirect: from.path } });
}
})
app.mount('#app');
You can pass the method in the second parameter of definestore:
store.js
export const useAppStore = defineStore('app', () => {
const state = reactive({
appName: 'App',
appLogo: ''
})
return {
...toRefs(state)
}
})
router.js
router.beforeEach((to, from, next) => {
const apppStore = useAppStore()
next()
})
I have resolved this by adding lazy loading
const routes = [
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]

How to add router with query param to router list?

I want to add a route with query params.
If the url is blog, then navigate to index page.
If the url includes the author query param, replace a component on the page with the BlogAuthorPage component.
router: {
extendsRoutes(routes, resolve) {
routes.push({
name: 'author-page-detail',
path: '/blog?author=*',
component: resolve(__dirname, 'pages/blog/author-page.vue')
})
}
}
This should not be done in nuxt.config.js's router key but rather in your blog.vue page directly with a component router guard.
The code below should be enough to check if the route does have a author query params and redirect to the blog/author-page page.
<script>
export default {
beforeRouteEnter(to, from, next) {
next((vm) => {
if (vm.$route.query?.author) next({ name: 'blog-author-page' })
else next()
})
},
}
</script>
I use "#nuxtjs/router": "^1.6.1",
nuxt.config.js
/*
** #nuxtjs/router module config
*/
routerModule: {
keepDefaultRouter: true,
parsePages: true
}
router.js
import Vue from 'vue'
import Router from 'vue-router'
import BlogIndexPage from '~/pages/blog/index'
import BlogAuthorPage from '~/pages/blog/author-page';
Vue.use(Router);
export function createRouter(ssrContext, createDefaultRouter, routerOptions, config) {
const options = routerOptions ? routerOptions : createDefaultRouter(ssrContext, config).options
return new Router({
...options,
routes: [
...options.routes,
{
path: '/blog',
component: ssrContext.req.url.includes('/blog?author') ? BlogAuthorPage : BlogIndexPage
}
]
})
}

Vuejs helper with route

I have a function that is used all over the app.
I would like to export this function to a module and import where is needed.
function inside component:
navigate: debounce(function() {
this.$router.push({
path: '/cars',
query: this.params
})
}, 200)
How can I export this function to a module and use on components ?
You can add the function into a mixin (https://v2.vuejs.org/v2/guide/mixins.html)
funcs.js:
export default
{
methods:
{
navigate()
{
debounce(() =>
{
this.$router.push({
path: '/cars',
query: this.params
});
}, 200);
}
}
}
component.vue:
import funcs from './funcs.js'
export default
{
...
mixins: [funcs],
...
}
Considering you mention this to be used often across your app, you can add a new method to your Vue router instance.
const router = new VueRouter({
routes
})
// Create and export a pass-through API so you can use it just like router.push
export const debouncedPush = debounce(router.push, 200);
// Add debouncedPush method on the router
router.debouncedPush = debouncedPush
const app = new Vue({
router
}).$mount('#app')
And then, in your component code, you can use it like
this.$router.debouncedPush({path: '/cars', query: this.params})
Or, you can import just the method like:
import { debouncedPush } from './path/to/file'
debouncedPush({path: '/cars'})

Lazy loading on vuex modules

I’m trying to use lazy loading with my vuex modules like this article : https://alexjoverm.github.io/2017/07/16/Lazy-load-in-Vue-using-Webpack-s-code-splitting/
Here is my old store\index.js :
import Vue from 'vue';
import Vuex from 'vuex';
import app from './modules/app';
import search from './modules/search';
import identity from './modules/identity';
import profil from './modules/profil';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
app,
search,
identity,
profil,
},
});
I tried to do this :
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store();
import('./modules/app').then((appModule) => {
store.registerModule('app', appModule);
});
import('./modules/search').then((searchModule) => {
store.registerModule('search', searchModule);
});
import('./modules/identity').then((identityModule) => {
store.registerModule('identity', identityModule);
});
import('./modules/profil').then((profilModule) => {
store.registerModule('profil', profilModule);
});
export default store;
But now I have a lot of error like “TypeError: _vm.consultList is undefined", consultList is a mapState variable, I also have same errors on my mapActions
Did I’ve done something wrong ?
All of those modules will be registered when app is loaded any because you most likely add the store to your initial vue instance. How I dynamically loading my vuex module is via the router:
{
path: "/orders/active",
name: "active-orders",
component: ActiveOrders,
props: true,
beforeEnter: (to, from, next) => {
importOrdersState().then(() => {
next();
});
}
},
Then also inside my router file I added:
const importOrdersState = () =>
import("#/store/orders").then(({ orders }) => {
if (!store.state.orders) store.registerModule("orders", orders);
else return;
});