Use Vue Router In Script - vue.js

I have a script that will redirect a user to the login screen when they get a response code of 401 - which means their session has expired within the API.
import axios from 'axios';
axios.interceptors.response.use(function (response) {
return response;
}, function(error) {
if(error.response.status === 401) {
localStorage.clear();
window.location = '/';
return Promise.reject(error);
}
return Promise.reject(error)
})
I wish to use vue router instead of window.location to redirect to the login page.
I have tried adding these lines of code to the script:
import Vue from 'vue';
Vue.$router.push({ name: 'login' })
I get an error.
How would one go about using vue router in this instance?

Make sure you already installed vue router. If not yet, this is how to install
npm install vue-router // npm
or vue router cdn
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
When used with a module system, you must explicitly install the router via Vue.use(). Do this one
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
then redirect to other page like this
this.$router.push({ name: 'nome-of-location'})

You can try this:
this.$router.push({ name: 'nome-of-location'})

This problem is is applicable to other frameworks like React and Angular and should be solved similarly there.
Router instance is unavailable outside component hierarchy, it cannot be accessed when Axios interceptor is defined in module scope.
It's also unwise to modify global Axios instance because it can be used by third-party libraries and cause unexpected side effects for them, this also makes clean-up more complicated in tests.
Local Axios instance can be defined in Vue application, also allows to define specific options like base URL:
Object.defineProperty(Vue.prototype, 'axios', {
get() {
return this.$root._axiosInstance;
}
});
Vue.mixin({
created() {
if (this.$root === this) {
let axiosInstance = axios.create({/*...*/});
axiosInstance.interceptors.response.use(
response => response,
error => {
...
this.$router.push(...);
...
}
);
this._axiosInstance = axiosInstance;
}
}
});
And is accessed as this.axios inside components.

Related

Vue 3 Vue-router 4 Composition API - Calling router.push outside of component not working

In my Vue 3 project, i use the composition API of Vue and i have axios defined as a plugin in plugins/axios.js like this:
import axios from "axios";
import { useRouter } from "vue-router";
import { useErrorStore } from "../stores/error";
axios.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const errorStore = useErrorStore();
errorStore.state.errors.push(error.response.data.detail);
if (parseInt(error.response.status) == 422) {
const router = useRouter();
router.push({
name: "login",
});
}
return Promise.reject(error);
}
);
export default axios;
ANTICIPATED BEHAVIOR:
When the API call returns a 422 status, meaning the access token is invalid/ expired, the user shall be redirected to the login page.
PROBLEM:
router.push does not work, i am not redirected when the error occurs. And i am not getting any error message on the console either.
On the other hand, the errorStore as defined and accessed in the error case works just fine.
QUESTION:
How can i redirect the user to another route from within the custom axios plugin file?
Alternatively, is there a better/ more common way to achieve this?
Thanks for your help!

How can I use router in store for Quasar

I'm trying to redirect to dashboard after login.
I found this in app.js file that
// make router instance available in store store.$router = router
So I codede that in my login method of auth.js file like below.
store.$router.push({ name: 'dashboard' })
But there is nothing to happen.
How can I use router in vuex store file?
Router is not available in the store directly. You might have to use plugin which makes the router available to the store.
Here is the Vuex-Router plugin which helps to access router to the store.
Are you trying to call router.push in an vuex action?
If so i am doing the same in my quasar v2 project
store/auth/actions.js
import { api } from 'boot/axios'
export function login({ dispatch, commit }, data) {
return api.post('/user/login', data).then(res => {
commit('setToken', res.data.token)
this.$router.push({ name: 'dashboard' }) // <-- What you are looking for?
}).catch(err => {
let msg = err.response.data || 'Error occurred'
return Promise.reject(msg)
})
}

BeforeRouteEnter not working in production with script setup

I used the beforeRouteEnter hook in vue-router to load data from two different endpoints using axios. I used promise.all() to load the data and then passed it to the component using next(). It seems to be working in development but when it is hosted on vercel the data isn't rendered on the component.
import axios from "axios"
import NProgress from "nprogress"
export default {
name: "DetailView",
beforeRouteEnter(to, from, next) {
NProgress.start()
const getDetails = axios.get(`api/grades/${ to.params.id }/`)
const getResults =
axios.get(`api/results/`, {
params: {
'grade-id': to.params.id,
}
})
Promise.all([getDetails, getResults])
.then(([details, results]) => {
next((vm) => {
vm.details = details.data
vm.results = results.data
})
})
.finally(NProgress.done())
},
}
I used a <script setup>...</script> for the setup function with the
import { ref } from "vue"
const details = ref({})
const grades = ref({})
I'm relatively new to javascript too and still trying to understand promises and async/await very well. Thank you
Finally found a solution to the problem. Components using <script setup> in vue are closed by default, he public instance of the component, which is retrieved via template refs or $parent chains, will not expose any of the bindings declared inside <script setup>. From the vue docs.
I had to explicitly expose the properties used in the beforeRouteEnter navigation guard using the defineExpose compiler macro
defineExpose(
{
details,
results
}
)

Client side only code in store on Nuxt.js

I'm trying to migrate my Vue app to nuxt.js and running into the issue with a store module where I import a cryptography node library. This library accesses a "window" object that naturally only exists on the client side.
The very fact of this module being in the store directory crashes the app. AFAIK I can import it as a plugin in nuxt.config.js but how do I attach it to the store after that?
Thanks!
Actually, I'm not sure anymore if the error is caused by SSR, since I see it in the browser. Just thought it was the case because of the "window not defined" thing. I've also tried importing crypto-pro on a page - it gives the same error. Naturally, in a regular vue project it works fine.
Here's my store/index.js
import crypto from "../plugins/crypto";
export const plugins = [crypto];
export const state = () => ({
});
Here's the crypto.js module. The
import Vue from 'vue';
import {createHashSignature, getHashedData, getUserCertificates, isValidSystemSetup, verifySignature } from 'crypto-pro';
import FileReader from '#tanker/file-reader';
export default {
namespaced: true,
state: {some state},
mutations: {some mutations},
actions: {some actions}
}
Here's what I get when I try opening the site
ReferenceError
window is not defined
node_modules\crypto-pro\lib\crypto-pro.js
define("cryptoPro", [], factory);
else if(typeof exports === 'object')
exports["cryptoPro"] = factory();
else
root["cryptoPro"] = factory();
})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
crypto-pro module itself is an API for working with a browser plugin that connects to the crypto provider on the user's PC.
You can create a plugin like this:
export default function ({ store }) {
if (process.client) {
//do something on client side
}
}
Destructure store out of the context

Vue.js / Mixins - Is there a way to get the global mixin-object outside of the vue component?

I am new with Vue.js
I am using Vue.js 2.4.4.
I have created the global mixin in my app.js file:
...
import router from './router'
...(some imports and plugins definitions)
Vue.use(VeeValidate);
Vue.component(VuePassword);
...
Vue.mixin({
data: function(){
return {
get auth(){
return Auth;
}
}
}
});
const app = new Vue({
el: '#root',
template: `<app></app>`,
components: { App },
router
});
This mixin imports some Auth object with validation methods e.t.c which needed to be in every component.
All of my components can check this mixin and it's working fine.
But I need to check the auth state after every route request, and I want to use my currently existing mixin, so I am trying to make something like this in my router.js file:
import Vue from 'vue'
import VueRouter from 'vue-router'
...
Vue.use(VueRouter);
const router = new VueRouter({
routes:[
...
]
});
router.beforeEach((to, from, next) => {
if(to.meta.requiresAuth) {
if(...call to mixin method) {
next();
} else {
next('/');
}
} else {
next();
}
});
export default router
Question:
Is there a way to get the global mixin object and change it's inner values or can you please give some small advise or example what is the right solution to this kind of tasks?
Or should I use the plugins instead of mixins?
I would rather create a seperate file for auth and not make it a mixin. Then using Vue.use() which will set auth on the vue object.
A sample of what the files might look like:
auth.js
export default function(Vue) {
Vue.auth = {
// do your auth logic
}
}
Then in your main js file
main.js
import Auth from './auth.js'
Vue.use(Auth);
Then you should be able to use Vue.auth
Another option would be keep using the mixin and pass the value to a store (like vuex) or create your own if your project is small...