Vue Router - How to lock user in a specific page - javascript

I am in trouble...
After registration in my application the user need's to create a workspace, I need to oblige the workspace creation otherwise the API will not work, but I am unable to lock it on the workspace creation page.
When I try to check if the user already has a workspace on the first load of the application vuex is still empty because the axios request has not yet been finalized...
How can I be sure that the vue router will wait for axios to receive the api data?
router.js
import Vue from 'vue'
import Router from 'vue-router'
import store from './../store'
import auth from './modules/auth'
import api from './modules/api'
import common from './modules/common'
import projects from './modules/projects'
import wabas from './modules/wabas'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
...auth,
...api,
...common,
...projects,
...wabas,
]
})
router.beforeEach((to, from, next) => {
store.dispatch('auth/setToken').then(() => {
// will return an empty array
console.log(store.getters['workspaces/workspaces'])
if (!store.state.workspaces.workspaces.length && to.name !== 'welcome_a' && to.name !== 'welcome_b' && to.name !== 'welcome_c') {
next({
name: 'welcome_a'
})
} else {
if (to.meta.visitor || to.name === 'welcome_a' || to.name === 'welcome_b' || to.name === 'welcome_c') {
next({
name: 'home'
})
return
}
next()
}
}).catch(() => {
if (to.meta.auth) {
next({
name: 'login'
})
return
}
next()
})
})
export default router
router/modules/common.js
const routes = [
{
path: '/',
name: 'home',
component: () => import('../../pages/Home'),
meta: {
auth: true
}
},
{
path: '/bem-vindo',
name: 'welcome_a',
component: () => import('../../pages/WelcomeA'),
meta: {
auth: true
}
},
{
path: '/finalizar-cadastro',
name: 'welcome_b',
component: () => import('../../pages/WelcomeB'),
meta: {
auth: true
}
},
{
path: '/area-de-trabalho',
name: 'welcome_c',
component: () => import('../../pages/WelcomeC'),
meta: {
auth: true
}
}
]
export default routes
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import localforage from "localforage";
localforage.config({
driver: localforage.LOCALSTORAGE,
storeName: 'positus'
})
Vue.config.productionTip = false
window._ = require('lodash')
import components from './components'
components.forEach(component => {
Vue.component(component.name, component);
});
import helpersMixin from './support/mixins/helpers'
Vue.mixin(helpersMixin)
import notifications from './support/notifications'
Vue.use(notifications)
import bus from './support/bus'
Vue.use(bus)
import VueClipboard from 'vue-clipboard2'
Vue.use(VueClipboard)
import http from './support/http'
Vue.use(http)
store.dispatch('auth/setToken').then(() => {
store.dispatch('auth/fetchSystemData').catch(() => {
store.dispatch('auth/clearAuth')
})
}).catch(() => {
store.dispatch('auth/clearAuth')
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
store/modules/auth.js
import Vue from 'vue'
import authApi from '../../api/auth'
import {setHttpToken} from '../../support/http'
import axios from 'axios'
import localforage from 'localforage'
import router from '../../router'
const state = {
user: {}
}
const getters = {
user(state) {
return state.user
}
}
const actions = {
fetchData({commit}) {
return axios.all([
authApi().user.get()
]).then(axios.spread((user) => {
commit('SET_USER_DATA', user.data.data)
})).catch(error => console.error(error))
},
fetchSystemData({dispatch}) {
return Promise.all([
dispatch('fetchData'),
dispatch('workspaces/fetchData', null, {root: true})
]).finally(() => {
dispatch('app/setProcessing', false, {root: true})
})
},
authenticateUser({commit, dispatch}, data) {
dispatch('app/setProcessing', true, {root: true})
return authApi().login(data)
.then(({data}) => {
dispatch('setToken', data).then(() => {
dispatch('fetchSystemData').then(() => {
router.push({
name: 'home'
})
}).catch(() => {
Vue.$n('Ocorreu um erro ao receber os dados da sua conta, tente novamente mais tarde.', 'error')
})
})
}).catch(() => {
dispatch('app/setProcessing', false, {root: true})
Vue.$n('Algum erro ocorreu na tentativa de acessar sua conta.', 'error')
})
},
setToken({dispatch}, token) {
if (_.isEmpty(token)) {
return dispatch('checkTokenExists').then((token) => {
setHttpToken(token)
})
}
dispatch('setTokenLocalStorage', token)
setHttpToken(token)
return token
},
setTokenLocalStorage({commit}, token) {
if (_.isEmpty(token)) {
localforage.removeItem('token', token)
return
}
localforage.setItem('token', token)
},
checkTokenExists() {
return localforage.getItem('token').then((token) => {
if (_.isEmpty(token)) {
return Promise.reject('NO_STORAGE_TOKEN')
}
return Promise.resolve(token)
})
},
clearAuth({dispatch}) {
Promise.all([
dispatch('setTokenLocalStorage', null)
]).finally(() => {
setHttpToken(null)
router.push({
name: 'login'
})
})
},
updateActiveWorkspace({commit}, data) {
commit('UPDATE_ACTIVE_WORKSPACE', data)
}
}
const mutations = {
SET_USER_DATA(state, user) {
state.user = user
},
UPDATE_ACTIVE_WORKSPACE(state, data) {
state.user.active_workspace = data
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
store/modules/workspaces.js
import workspacesApi from "../../api/workspaces"
import axios from "axios"
const state = {
workspaces: []
}
const getters = {
workspace(state) {
return (id) => {
return _.find(state.workspaces, (workspace) => {
return workspace.id === id
})
}
},
workspaces(state) {
return state.workspaces
}
}
const actions = {
fetchData({commit}) {
return axios.all([
workspacesApi().get()
]).then(axios.spread((workspaces) => {
commit('SET_WORKSPACES', workspaces.data.data)
})).catch(error => console.error(error))
},
setWorkspaces({commit}, data) {
commit('SET_WORKSPACES', data)
},
setWorkspace({commit, state}, data) {
let index = _.findIndex(state.workspaces, (space) => {
return space.id === data.id
})
if (index >= 0) {
commit('UPDATE_WORKSPACE', {
index: index,
data: data
})
} else {
commit('SET_WORKSPACE', data)
}
}
}
const mutations = {
SET_WORKSPACES(state, bool) {
state.workspaces = bool
},
SET_WORKSPACE(state, data) {
state.workspaces.push(data)
},
UPDATE_WORKSPACE(state, data) {
state.workspaces.splice(data.index, 1, data.data);
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

from the title of this question, you can add a hook in vue component that will run when before the route is changed
beforeRouteLeave (to, from, next) {
// called when the route that renders this component is about to
// be navigated away from.
// has access to `this` component instance.
}
if you want user to move to the next route on which he clicked, you will call next() inside this hook and he will be navigated to the next page.
if you want to lock him and not navigate, pass false in this next() method like next(false)

Why don't you just create a middleware server side that redirects the user to the workspace creation page if the user didn't have one yet? Something like that in laravel
class RedirectIfWorkspaceNotCreated
{
public function handle($request, Closure $next, $guard = null)
{
$user = Auth::guard($guard)->user();
if ($user && !checkForUserWorkspace) {
return redirect('/your/workspace/creation/page');
}
return $next($request);
}
}

Related

Doesn't see data from store after reboot

in App.vue I get user data (if he is authorized)
onMounted( async () => {
await init();
});
added the initialization function to the useAuthComposables file
import { authService } from '#/shared/services/v1/auth.service';
import { useAuthStore } from '#/store/auth';
export default function () {
const authStore = useAuthStore();
const init = async () => {
const [error, data] = await authService.getAuthUser();
if (error) {
console.log(error);
} else {
authStore.setUser(data);
authStore.setToken(true);
}
};
return {
init
};
}
and the store itself useAuthStore
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('authStore', {
state: () => ({
user: null,
isLoggedIn: false
}),
actions: {
setUser(data: any) {
this.user = data;
},
setToken(isAuth: boolean) {
this.isLoggedIn = isAuth;
}
},
getters: {
getUser(state: any) {
return state.user;
}
}
});
when I reload the page (f5) in the route does not see the side
router.beforeEach(async (to, from, next): Promise<any> => {
const authStore = useAuthStore();
console.log('beforeEach',authStore.getUser);
if (!authStore.isLoggedIn) {
next({ name: 'Login' });
}
else {
next();
}
});
but when I just follow the links, it displays a stor

Vue TypeError: Cannot read property 'loggedIn' of undefined

Based from the error, the problem is that the getters didn't retrieve any data. I tried to change the import for store since it was setup as a default by Quasar, but it only made things worse and I tried to debug those errors for hours too. Also tried changing the default export of store and router but it's pointless since they also have built in setup from Quasar too.
Login.vue
<template>
<div>
<q-page>
<h5>Login Page</h5>
<q-input name="user" v-model="username" filled label="Username" />
<q-input name="pass" v-model="password" filled label="Password" />
<q-btn #click="login" label="Login" />
<li v-if="!loggedIn">
<router-link to="/register">Register</router-link>
</li>
</q-page>
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
name: "login",
data() {
return {
username: "",
password: ""
};
},
computed: {
loggedIn() {
return this.$store.getters.loggedIn;
}
},
methods: {
login() {
this.$store
.dispatch("LOGIN", {
username: this.username,
password: this.password
})
.then(response => {
this.$router.push("/addsub");
})
.catch(error => {
console.log(error);
});
}
}
};
</script>
<style>
</style>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import { getField, updateField } from 'vuex-map-fields'
import axios from 'axios'
// import example from './module-example'
Vue.use(Vuex)
/*
* If not building with SSR mode, you can
* directly export the Store instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Store instance.
*/
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
// example
},
state: {
token: localStorage.getItem('access_token') || null,
subData: {
subjectName: ''
},
testItems: {
testId: '',
subjectName: '',
existingTest: '',
question: '',
answer: '',
studentAnswer: ''
}
},
mutations: {
sendData: state => {
const config = {
headers: { 'Content-Type': 'application/json' }
}
axios
.post(
'http://localhost/MyComposer/?submitId=0',
{
subjectName: state.subData.subjectName
},
config
)
.then(res => {
console.log(res)
alert(res.data)
})
.catch(error => {
console.log(error.response)
})
},
//FOR TEACHER
submitTestData: state => {
const config = {
headers: { 'Content-Type': 'application/json' }
}
axios
.post(
'http://localhost/MyComposer/?submitId=1',
{
subjectName: state.testItems.subjectName,
question: state.testItems.question,
answer: state.testItems.answer
},
config
)
.then(res => {
console.log(res)
alert(res.data)
})
.catch(error => {
console.log(error.response)
})
},
//FOR STUDENT
submitData: state => {
const config = {
headers: { 'Content-Type': 'application/json' }
}
axios
.post(
'http://localhost/MyComposer/?submitId=2',
{
existingTest: state.testItems.existingTest,
studentAnswer: state.testItems.studentAnswer
},
config
)
.then(res => {
console.log(res)
alert(res.data)
})
.catch(error => {
console.log(error.response)
})
},
updateField,
logMeIn (state, token) {
state.token = token
},
destroyToken (state) {
state.token = null
}
},
getters: {
getField,
loggedIn (state) {
return state.token != null
}
},
actions: {
LOGIN (context, payload) {
return new Promise((resolve, reject) => {
axios
.post('http://localhost/MyComposer/?submitId=3', payload)
.then(response => {
console.log(payload.username)
console.log(payload.password)
console.log(response.data)
console.log(response.data.username)
console.log(response.data.password)
for (var i = 0; i < response.data.length; i++) {
if (
response.data[i].Username === payload.username &&
response.data[i].Password === payload.password
) {
alert('Logged In Successfully!')
console.log(payload)
console.log(response.data)
resolve(response)
const token = response.data[i].UserId
console.log(token)
localStorage.setItem('access_token', token)
context.commit('logMeIn', token)
}
}
})
.catch(error => {
reject('Incorrect Username or password. Please try again!')
console.log(error)
})
})
},
destroyToken (context) {
axios.defaults.headers.common['Authorization'] =
'Bearer ' + context.state.token
if (context.getters.loggedIn) {
return new Promise((resolve, reject) => {
axios
.post('http://localhost/MyComposer/')
.then(response => {
for (var i = 0; i < response.data.length; i++) {
if (
response.data[i].Username === payload.username &&
response.data[i].Password === payload.password
) {
alert('Logged In Successfully!')
console.log(payload)
console.log(response.data)
resolve(response)
console.log(token)
localStorage.removeItem('access_token')
context.commit('destroyToken')
}
}
})
.catch(error => {
localStorage.removeItem('data')
context.commit('destroyToken')
console.log(error)
})
})
}
}
},
// enable strict mode (adds overhead!)
// for dev mode only
strict: process.env.DEV
})
return Store
}
Router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
// import { store } from '../store/index'
import { store } from 'quasar/wrappers'
Vue.use(VueRouter)
/*
* If not building with SSR mode, you can
* directly export the Router instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Router instance.
*/
export default function (/* { store, ssrContext } */) {
const router = new VueRouter({
scrollBehavior: () => ({ x: 0, y: 0 }),
routes,
store,
// Leave these as they are and change in quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (store.getters.loggedIn) {
next({
path: '/'
// query: { redirect: to.fullPath }
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (store.getters.loggedIn) {
next({
path: '/addsub'
// query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
})
return router
}
routes.js
// import VueRouter from 'vue-router'
// const router = new VueRouter({
const routes = [
{
path: '/',
component: () => import('layouts/MyLayout.vue'),
children: [
{
path: '',
component: () => import('pages/Index.vue')
},
{
path: '/subjectntestlist',
component: () => import('pages/SubTestList.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/addsub',
component: () => import('pages/FormData.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/logout',
component: () => import('pages/Logout.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/register',
component: () => import('pages/Register.vue'),
meta: {
requiresVisitor: true
}
},
{
path: '/newtest',
component: () => import('pages/AddTest.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/testlist',
component: () => import('pages/TestList.vue'),
meta: {
requiresAuth: true
}
}
]
}
]
// })
// Always leave this as last one
if (process.env.MODE !== 'ssr') {
routes.push({
path: '*',
component: () => import('pages/Error404.vue')
})
}
export default routes
I finally got it working. If you're going to work in Quasar, just un-comment out something they made for you, a.k.a. reserved words. I solved my problem by doing so
instead of creating a new Vue component just to call out the store. Also, you don't have to remove the export defaults at all because they are actually very helpful with easier connections with other components.
export default function ({ store, ssrContext } /* { store, ssrContext } */) { <-- This was commented out before
const router = new VueRouter({
scrollBehavior: () => ({ x: 0, y: 0 }),
routes,
// Leave these as they are and change in quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.loggedIn) {
next({
path: '/'
// query: { redirect: to.fullPath }
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (store.getters.loggedIn) {
next({
path: '/addsub'
// query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
})
return router
}

vuex unknown action type while trying to post data to Django backend

I am trying to send my vuejs front end data using Vuex and Vue-axios to the backend. I have create a vuex store and vue-axios services But I get an error saying [vuex] unknown action type: addGeneral when I try to pass the data.
This is my vuex folder structure:
-store
-modules
-app
-mutations.js
-state.js
-general.js
-index.js
-actions.js
-getters.js
-index.js
-mutations.js
-state.js
This is module/general.js :
import { ApiService } from '#/services/api.service'
import { FETCH_GENERAL,
ADD_GENERAL
} from '../actions'
import { FETCH_START,
FETCH_END,
SET_GENERAL,
SET_ERROR,
} from '../mutations'
const state = {
general: [],
errors: {},
loading: false
};
const getters = {
general (state) {
return state.general;
},
isLoading (state) {
return state.loading;
}
};
const actions = {
[FETCH_GENERAL] (context, payload) {
context.commit(FETCH_START);
return ApiService
.get('general')
.then(({data}) => {
context.commit(FETCH_END);
context.commit(SET_GENERAL, data.general.results);
})
.catch(({response}) => {
context.commit(SET_ERROR, response.data.errors)
})
},
[ADD_GENERAL] (context, payload) {
context.commit(FETCH_START);
return ApiService
.postGeneral(`general`, '',payload)
.then(({data}) => {
context.commit(FETCH_END);
context.commit(SET_GENERAL, data.general.results);
})
.catch(({response}) => {
context.commit(SET_ERROR, response.data.errors)
})
}
};
const mutations = {
[FETCH_START] (state) {
state.loading = true
},
[FETCH_END] (state) {
state.loading = false
},
[SET_GENERAL] (state, pgeneral) { // can pass in payload
state.components = pgeneral;
state.errors = {}
},
[SET_ERROR] (state, errors) {
state.errors = errors
}
};
export default {
state,
getters,
actions,
mutations
}
This is module/index.js :
const requireModule = require.context('.', true, /\.js$/)
const modules = {}
requireModule.keys().forEach(fileName => {
if (fileName === './index.js') return
// Replace ./ and .js
const path = fileName.replace(/(\.\/|\.js)/g, '')
const [moduleName, imported] = path.split('/')
if (!modules[moduleName]) {
modules[moduleName] = {
namespaced: true
}
}
modules[moduleName][imported] = requireModule(fileName).default
})
export default modules
This is store/actions.js :
export const FETCH_GENERAL = "fetchGeneral";
export const ADD_GENERAL = "addGeneral";
This is store/index.js :
import Vue from 'vue'
import Vuex from 'vuex'
// Store functionality
import actions from './actions'
import getters from './getters'
import modules from './modules'
import mutations from './mutations'
import state from './state'
Vue.use(Vuex)
// Create a new store
const store = new Vuex.Store({
actions,
getters,
modules,
mutations,
state
})
export default store
This is store/mutations.js :
export const FETCH_START = "loadingOn";
export const FETCH_END = "loadingOff";
export const SET_ERROR = "setError";
// related to general
export const SET_GENERAL = 'setGeneral';
This is my vue-axios folder structure:
-services
-api.services.js
-config.js
This is services/api.serviecs.js :
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import { API_URL } from './config'
import Cookies from 'js-cookie'
let CSRF_TOKEN = Cookies.get('csrftoken');
export const ApiService = {
init () {
Vue.use(VueAxios, axios)
Vue.axios.defaults.baseURL = API_URL
},
get (resource, slug='') {
return Vue.axios
.get(`${resource}\${slug}`,)
.catch((error) => {
throw new Error(`ApiService ${error}`)
})
},
postGeneral (resource, slug='', obj) {
return axios
.post(`${API_URL}\\${resource}\\${slug}`,{
systemName: obj.systemName,
regionOfDeployment: obj.regionOfDeployment,
operatingMode: obj.operatingMode,
solution: obj.solution,
baselineMode: obj.baselineMode,
baselineDetails: obj.baselineDetails,
projectDuration: obj.projectDuration,
},
{
headers: {
'X-CSRFToken': CSRF_TOKEN,
'Content-Type': 'application/json',
}
})
.catch((error) => {
throw new Error (`ApiService ${error}`)
})
},
}
export default ApiService
This is config.js:
export default {}
export const API_URL = 'http://127.0.0.1:8000/api';
and finally this is my vuejs component:
...
<v-btn class="mt-5 mr-2 font-weight-light" color="blue"
#click="addGeneral" >
...
methods: {
addGeneral() {
let obj = {
systemName: '',
regionOfDeployment: '',
operatingMode: '',
solution: '',
baselineMode: '',
baselineDetails: '',
projectDuration: ''
};
this.postGeneral(obj)
},
postGeneral(obj) {
this.$store.dispatch(ADD_GENERAL, obj)
}
}
Why do I get the error and what's the best way to solve it?
You're using namespaced: true, so you need to pass module name in dispatch
postGeneral(obj) {
this.$store.dispatch('general/' + ADD_GENERAL, obj)
}

How to access this variable from my store / state in my vue router?

I have setup my routes.js file to import my state from my store. This is working as when I console.log(state) it outputs my store to console successfully:
I then define my route as below:
routes.js
import { state } from './store/store';
// import { mapState, mapGetters } from "vuex";
console.log(state)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard,
},
{
path: '/project/:id',
name: 'project',
component: Project,
props: true,
meta: {
requiresAuth: true,
},
children: [
{
path: 'module/:module',
name: 'module',
component: Tasks,
props: true,
children: [
{
path: 'task/:url',
name: 'task',
component: () => import(`./components/ProductServiceAnalysis/${$state.taskURL}.vue`),
props: true,
I am getting the error:
app.js:59653 [vue-router] Failed to resolve async component default: ReferenceError: state is not defined in relation to the second last line where I try to access the state.taskURL variable.
Why is this erroring? And how can I access my taskURL variable in my store from my Router?
If I am approaching this incorrectly then please offer suggestions.
This is my store.js:
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios'
Vue.use(Vuex);
axios.defaults.baseURL = 'http://buildmybusiness.test/api'
Vue.config.devtools = true;
export const state = {
token: localStorage.getItem('access_token') || null,
requiredTask: 'This is my current task',
currentModule: '1',
currentModuleName: 'Product & Service Analysis',
currentTask: '1',
modules:[],
tasks:[],
taskName:[],
actions:[],
userId: localStorage.getItem('loggedin_user') || null,
userName: localStorage.getItem('loggedin_username') || null,
projects:[],
currentProjectId: '',
currentProjectName: '',
taskURL: 'define-product-service'
}
export const store = new Vuex.Store({
state,
mutations: {
SET_MODULES: (state, payload) => {
state.modules = payload;
},
SET_TASKS: (state, tasks) => {
state.tasks = tasks;
},
SET_MODULE_TITLE: (state, moduleTitle) => {
state.currentModuleName = moduleTitle
},
SET_ACTIONS: (state, payload) => {
state.actions = payload;
},
RETRIEVE_TOKEN: (state, token) => {
state.token = token;
},
DESTROY_TOKEN: (state) => {
state.token = null;
},
SET_USERID: (state, userid) => {
state.userId = userid;
},
DESTROY_USERID: (state) => {
state.userId = null;
},
SET_USERNAME: (state, username) => {
state.userName = username;
},
DESTROY_USERNAME: (state) => {
state.userName = '';
},
SET_PROJECTS: (state, projects) => {
state.projects = projects;
},
DESTROY_PROJECTS: (state) => {
state.projects = [];
},
SET_PROJECT_ID: (state, projectId) => {
state.currentProjectId = projectId;
},
SET_PROJECT_NAME: (state, projectName) => {
state.currentProjectName = projectName;
},
SET_ACTION_URL: (state, taskURL) => {
state.taskURL = taskURL;
},
},
getters: {
loggedIn(state){
return state.token !== null;
},
SelectedTaskURL(state) {
return state.taskURL;
}
},
actions: {
setActionsURL(context, taskURL){
context.commit("SET_ACTION_URL", taskURL);
},
setProject(context, projectDetails){
const projectId = projectDetails.projectId;
const projectName = projectDetails.projectName;
context.commit("SET_PROJECT_ID", projectId);
context.commit("SET_PROJECT_NAME", projectName);
},
fetchProjects(context) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token;
return axios.get('/project').then(response => {
const projectNames = response.data.map(project => project);
context.commit("SET_PROJECTS", projectNames);
});
},
getUserDetails(context) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token;
return axios.get('/user').then(response => {
const userid = response.data.id
localStorage.setItem('loggedin_user', userid)
context.commit("SET_USERID", userid);
const username = response.data.name
localStorage.setItem('loggedin_username', username)
context.commit("SET_USERNAME", username);
});
},
register(context, data) {
return new Promise ((resolve, reject) => {
axios.post('/register', {
name: data.name,
email: data.email,
password: data.password,
})
.then(response => {
resolve(response)
})
.catch(error => {
reject(error);
})
})
},
destroyToken(context){
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token
if (context.getters.loggedIn){
return new Promise ((resolve, reject) => {
axios.post('/logout')
.then(response => {
localStorage.removeItem('access_token')
context.commit("DESTROY_TOKEN")
context.commit("DESTROY_USERID")
context.commit("DESTROY_USERNAME")
context.commit("DESTROY_PROJECTS")
resolve(response)
})
.catch(error => {
localStorage.removeItem('access_token')
context.commit("DESTROY_TOKEN")
context.commit("DESTROY_USERID")
context.commit("DESTROY_USERNAME")
context.commit("DESTROY_PROJECTS")
reject(error);
})
})
}
},
loadModules(context) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token
return axios.get('/modules').then(response => {
context.commit("SET_MODULES", response.data);
});
},
getTasks(context, moduleDetails){
var moduleTitle = moduleDetails.moduleName;
var moduleTitle = (moduleTitle === undefined) ? moduleTitle = 'Product & Service Analysis' : moduleTitle;
//console.log(moduleTitle);
var moduleId = moduleDetails.moduleId;
var moduleId = (moduleId === undefined) ? moduleId = 1 : moduleId;
return axios.get(`project/${context.state.currentProjectId}/module/${moduleId}`)
.then(response => {
context.commit("SET_TASKS", response.data);
context.commit("SET_MODULE_TITLE", moduleTitle);
});
},
loadTasks(context, tasks){
},
loadActions(context){
},
retrieveToken(context, credentials){
return new Promise ((resolve, reject) => {
axios.post('/login', {
username: credentials.username,
password: credentials.password,
})
.then(response => {
const token = response.data.access_token
localStorage.setItem('access_token', token)
context.commit("RETRIEVE_TOKEN", token)
resolve(response)
})
.catch(error => {
console.log(error);
reject(error);
})
})
},
}
});
my app.js
// main.js
require('./bootstrap');
import Vue from 'vue';
import App from './App.vue';
import VueRouter from 'vue-router';
import VueAxios from 'vue-axios';
import axios from 'axios';
import routes from './routes';
import BootstrapVue from 'bootstrap-vue'
import { store } from './store/store';
import Vuex from 'vuex'
Vue.config.productionTip = false;
Vue.use(VueRouter);
Vue.use(VueAxios, axios);
Vue.use(BootstrapVue);
Vue.use(Vuex);
const router = new VueRouter({
store,
routes,
mode: 'history'
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!store.getters.loggedIn) {
next({
name: 'login',
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (store.getters.loggedIn) {
next({
name: 'dashboard',
})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
})
new Vue({
store: store,
router,
render: h => h(App)
}).$mount('#app');
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-vue/dist/bootstrap-vue.css';
If reference to #shazyriver:
I have done as you suggested. I have put a console.log(./components/ProductServiceAnalysis/${state.taskURL}.vue); before const routes = [ ... which correctly accesses the taskURL property and prints it to console. However, it still fails with a 'state undefined' when it tries to access the same property from within the route itself - even though it works when accessed outside the const routes = [:
See console log for detail
First of all, your configuration and all your imports are correct.
It's very interesting issue...I have researched it and can conclude that there is a kind of bug in transpilation process of webpack+babel. Let me explain:
If you check your bundle you can see that there is strange untranspiled concat expression in your dynamic import line, something like this: ("./".concat(state.taskURL,".vue")) - but state should be wrapped with webpack helpers but it wasn't...Looks like module resolving was skipped for import statement string interpolation.
The simplest solution is just assign imported module to some variable and use that variable in the import statement(I recommend to use fully configured store instead of state):
import { store } from './store/store';
let storeVar = store;
//...
//...below
component: () => import(`./components/ProductServiceAnalysis/${storeVar.state.taskURL}.vue`),
In this case module will be processed correctly by webpack.
P.S. I had created clean project with just webpack and tried to play with dynamic imports and they was resolved successfully...So I suppose that issue in another transpilation layer, maybe babel.
P.P.S. If my explanation is not enough clear please feel free to ask in comments.
You just need to import it like:
import store from './store/store.js'
Then you can use it like:
store.commit('increaseCounter')

Can't get currentUser on load

When trying to check if a user is signed in via firebase.auth().currentUser like this:
if (firebase.auth().currentUser === null) {
console.log('User not signed in');
}
Whenever I refresh the page, or navigate around the above returns null (even though I have just logged in).
The weird thing is, that if I log
console.log(firebase.auth().currentUser) // This returns null
console.log(firebase.auth()) // Here I can inspect the object and currentUser exists...!
I don't really know what's going on here. I'm using React and Redux, but it shouldn't really matter I'd say.
Is there a small delay where the firebase initialises and you can't access the currentUser? If so, how can I see it in the log output of firebase.auth()?
This is a commonly asked question.
https://firebase.google.com/docs/auth/web/manage-users
You need to add an observer to onAuthStateChanged to detect the initial state and all subsequent state changes,
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
A simple way is to add a pending state.
Here is a react example using hooks:
// useAuth.ts
import { useState, useEffect } from 'react'
import { auth } from 'firebase'
export function useAuth() {
const [authState, setAuthState] = useState({
isSignedIn: false,
pending: true,
user: null,
})
useEffect(() => {
const unregisterAuthObserver = auth().onAuthStateChanged(user =>
setAuthState({ user, pending: false, isSignedIn: !!user })
)
return () => unregisterAuthObserver()
}, [])
return { auth, ...authState }
}
// SignIn.tsx
import React from 'react'
import { StyledFirebaseAuth } from 'react-firebaseui'
import { useAuth } from '../hooks'
export default function SignIn() {
const { pending, isSignedIn, user, auth } = useAuth()
const uiConfig = {
signInFlow: 'popup',
signInOptions: [
auth.GoogleAuthProvider.PROVIDER_ID,
auth.FacebookAuthProvider.PROVIDER_ID,
],
}
if (pending) {
return <h1>waiting...</h1>
}
if (!isSignedIn) {
return (
<div>
<h1>My App</h1>
<p>Please sign-in:</p>
<StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={auth()} />
</div>
)
}
return (
<div>
<h1>My App</h1>
<p>Welcome {user.displayName}! You are now signed-in!</p>
<a onClick={() => auth().signOut()}>Sign-out</a>
</div>
)
}
The best way to always have access to currentUser is to use vuex and vuex-persistedstate
//Configure firebase
firebase.initializeApp(firebaseConfig);
//When ever the user authentication state changes write the user to vuex.
firebase.auth().onAuthStateChanged((user) =>{
if(user){
store.dispatch('setUser', user);
}else{
store.dispatch('setUser', null);
}
});
The only issue above is that if the user presses refresh on the browser the vuex state will be thrown away and you have to wait for onAuthStateChange to fire again, hence why you get null when you try to access currentUser.
The secret to the above code working all the time is to use vuex-persisted state.
In your store.js file
import Vue from 'vue'
import Vuex from 'vuex'
import firebase from 'firebase/app'
Vue.use(Vuex)
import createPersistedState from "vuex-persistedstate";
export default new Vuex.Store({
plugins: [createPersistedState()],
state: {
user: null
},
getters:{
getUser: state => {
return state.user;
}
},
mutations: {
setUser(state, user){
state.user = user;
}
},
actions: {
setUser(context, user){
context.commit('setUser', user);
},
signIn(){
let provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function (result) {
})
},
signOut(){
firebase.auth().signOut();
}
}
})
You can now protect routes in your router as in the code example below.
import Vue from 'vue'
import Router from 'vue-router'
import Home from '#/components/Home'
import Search from '#/components/Search/Search'
import CreateFishingSite from '#/components/FishingSites/CreateFishingSite'
Vue.use(Router);
import store from './store'
import firebase from 'firebase'
let router = new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/search/:type',
name: 'Search',
component: Search
},
{
path: '/fishingsite/create',
name: 'CreateFishingSite',
component: CreateFishingSite,
meta: {
requiresAuth: true
}
}
]
})
router.beforeEach(async (to, from, next)=>{
let currentUser = store.state.user;
console.log(currentUser);
let requriesAuth = to.matched.some(record => record.meta.requiresAuth);
if(requriesAuth && !currentUser){
await store.dispatch('signIn');
next('/')
}else{
next()
}
})
If you are looking for a copy and paste Auth route for react with firebase:
const AuthRoute = ({ component: Component, ...rest }) => {
const [authenticated, setAuthenticated] = useState(false)
const [loadingAuth, setLoadingAuth] = useState(true)
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setAuthenticated(true)
} else {
setAuthenticated(false)
}
setLoadingAuth(false)
})
}, [])
return loadingAuth ? 'loading...' : (
<Route
{...rest}
render={props =>
authenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/user/login' }} />
)}
/>
)
}
Promise-wise, there are three options:
UPDATE: 11/26/22
For Firebase 9+, you could do:
Note: (this.auth) is the Auth object and depends on your framework.
const user1 = await firstValueFrom(authState(this.afa));
const user2 = await firstValueFrom(
new Observable(observer => onAuthStateChanged(this.afa, observer))
);
const user3 = this.afa.currentUser;
// best option
const user1 = await new Promise((resolve: any, reject: any) =>
firebase.auth().onAuthStateChanged((user: any) =>
resolve(user), (e: any) => reject(e)));
console.log(user1);
// sometimes does not display correctly when logging out
const user2 = await firebase.auth().authState.pipe(first()).toPromise();
console.log(user2);
// technically has a 3rd state of 'unknown' before login state is checked
const user3 = await firebase.auth().currentUser;
console.log(user3);
// On component load.
componentDidMount = () => this.getAuthStatus();
// Get firebase auth status.
getAuthStatus = () => {
firebase.auth().onAuthStateChanged((resp) => {
// Pass response to a call back func to update state
this.updateUserState(resp);
});
}
// update state
updateUserState = (resp) => {
this.setState({
user: resp
})
}
// Now you can validate anywhere within the component status of a user
if (this.state.user) { /*logged in*/}
Best approach for this is to use a promise and only instantiate the router after the response, something along the lines of:
store.dispatch('userModule/checkAuth').then(() => {
// whatever code you use to first initialise your router, add it in here, for example
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
})
inside the checkAuth action is where you would have your promise, like so:
checkAuth ({ commit }) {
return new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged(async (_user) => {
if (_user) {
commit('setUser', _user)
} else {
commit('setUser', null)
}
console.log('current user in checkAuth action:', _user)
resolve(true)
})
})
h/t to aaron k saunders - the source of this solution for me.
If you'd like the user to access to a certain page only if he is authenticated and to redirect to the home page if he is not, the following codes might help:
in React:
make a component with the following code:
import { onAuthStateChanged } from "#firebase/auth";
import { Route, Redirect } from "react-router-dom";
import { auth } from "../firebase/config";
import { useState, useEffect } from "react";
const GuardedRoute = ({ component, path }) => {
const [authenticated, setAuthenticated] = useState(false);
const [authCompleted, setAuthCompleted] = useState(false);
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
setAuthenticated(true);
} else {
setAuthenticated(false);
}
setAuthCompleted(true);
});
}, []);
return authCompleted ? (
authenticated ? (
<Route path={path} component={component} />
) : (
<Redirect to="/" />
)
) : (
""
);
};
export default GuardedRoute;
and in app.js use:
import RouterPage from "./pages/RouterPage";
<GuardedRoute path="/router-page" component={RouterPage} />
in Vue:
at the router file use:
const guardSuccess = (to, from, next) => {
let gUser = auth.currentUser
if (gUser) {
next()
} else {
next({ name: "Home" })
}
}
and in the routes of the page you want to restrict access to add:
{
path: "/router-page",
name: "routerPage",
component: () => import("../views/routerPage.vue"),
beforeEnter: guardSuccess
}
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
var user = firebase.auth().currentUser;
if(user != null){
var io=user.uid;
window.alert("success "+io);
}
} else {
// No user is signed in.
Window.reload();
}
});
first check if user exist then get it id by
firebase.auth().currentUser.uid

Categories