Vuex state on page refresh and multiple tabs - vue.js

In my app i use firebase API for users authentication
I save the login status as a boolean value in my vuex state
When the user logs in I set the login status to true and using this I hide the login button on the top menu and display the log out button and vice versa when the user logs out.
So i use vuex-persisted state to save the state for page refreshes
The dafault storage in vuex-persisted state is local storage
Instead of saving the state of store on locaal storage i want it to be saved in cookies...so i followed the same apprach as described in the vuex-persisted state documentationn
the problems I am facing are:
when i use the default storage i.e local storage it works but when i use cookies the state is not getting saved in the cookie and persisted state does not work
when i open the app on 2 different tabs and the user logs out in one tab the state is synced in both tabs but log out button is still shown in the other tab
my store
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import * as Cookies from 'js-cookie'
import authStore from './modules/auth'
import statusStore from './modules/allStatus'
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
authStore,
statusStore
},
plugins: [
createPersistedState({
getState: (key) => Cookies.getJSON(key),
setState: (key, state) => Cookies.set(key, state, { expires: 3, secure: true })
})
]
});

The author of vuex-persistedstate here.
You've indeed try to set your cookies on a "secure connection". Try to set secure to false should do the trick. Otherwise open an issue on the repository.

I had a similar issue and thought that persisted state cookies were not working. I changed "secure: true" to "secure: false" and it started working as described in the documentation. If you testing the app in a non SSL enabled environment like a localhost nodejs server, try the "secure: false" option.

With bootstrap and vue js that works for me!
<div id="app">
<b-tabs content-class="mt-3" v-model="myIndex" #input="change()">
<b-tab title="Tab 1">
</b-tab>
<b-tab title="Tab 2">
</b-tab>
<b-tab title="Tab 3">
</b-tab>
</b-tabs>
</div>
<script>
let lecture = new Vue({
el: '#app',
data() {
return {
myIndex: 0, // Current tab
}
},
mounted() {
// Get the previous tab from the url
this.myIndex = parseInt(window.location.hash.replace('#',''), 10);
},
methods: {
change () {
// Save the current tab in url
window.location.hash = this.myIndex;
}
}
});
</script>

Related

Vuejs. Update data() on update

I'm trying to check if there is an authenticated user in order to render the sidebar and the header.
If it's not authenticated the sidebar and header don't get rendered in order to display just the Login or Register. But my this.auth data is not getting updated until I reload the page. I understand that probably there is a feature on vuejs that will update this variable more often? ( My sidebar contains the router-links to load the rest of my components but this.auth does not get updated when I click on a router-link )
EDITED: Using a computed property solved it.
<template>
<v-app>
<header-component v-if="auth" />
<sidebar-component v-if="auth" />
<router-view></router-view>
</v-app>
</template>
<script>
import Sidebar from "./components/Sidebar"
import Header from "./components/Header"
export default{
name: 'App',
components: {
'sidebar-component': Sidebar,
'header-component': Header,
},
computed: {
auth ()
{
return (!localStorage.getItem("auth")) ? false : true
}
}
}
</script>
You can delete your auth declare in the data, and use the computed like this:
computed {auth (){return (!localStorage.getItem("auth")) ? false : true}}

How transfer a selected option to another page in vue application?

On the first page of my Vue application, I have a drop-down menu that contains a list of mailboxes.
I would like to save the value/text of the selection and use it as a parameter or variable on the Inbox page that I routed to.
Here is the drop-down code using v-autocomplete:
<v-autocomplete dense
filled
label="Choose Mailbox"
v-model="mailboxes"
:items="mailboxes"
item-text='mailbox'
item-value='mailbox'>
</v-autocomplete>
Here is the button as v-btn that routes to the Inbox page.
<v-btn rounded color="primary"
#click="$router.push('Inbox')">
Load Mailbox</v-btn>
How do I save the selected mailbox value to use on the routed-to Inbox page?
I suggest you to get started with Vuex :)
It's a library that share a reactive data object across the whole app.
Here is what it could look like for you:
// /store/index.js
export state: () => {
mailbox: '',
}
export mutation: () => {
SET_MAILBOX(state, mailbox) {
state.mailbox = mailbox
}
}
// your-page.vue
<template>
<v-autocomplete
v-model="mailboxes"
dense
filled
label="Choose Mailbox"
:items="mailboxes"
item-text='mailbox'
item-value='mailbox'>
</v-autocomplete>
</template>
<script>
export default {
computed: {
mailboxes: {
get() {
this.$store.state.mailbox // Get the value from the Vuex store
},
set(newMailbox) {
this.$store.commit('SET_MAILBOX', newMailbox) // Update the Vuex store
},
}
}
}
</script>
Passing the selected value as a route parameter:
$router.push({
name: "Inbox",
params: { selectedValue: YOUR_VALUE }
});
In the Inbox page, you can access it through:
$route.params.selectedValue
Other easy solution is to use browser local storage.
Vue Client-Side Storage

Nuxt and Vuetify. navigation drawer doe

I've been trying to solve this mystery for a week now.
I'm trying to set up a sidebar with Vuetify in my Nuxt/Vue website where people click on the hamburger menu and the sidebar opens up. To do this I set up the hamburger menu to run a toggle method.
<v-app-bar-nav-icon #click="toggleSidebar"></v-app-bar-nav-icon>
......
<script>
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations({
toggleSidebar: 'appSidebar/toggleSidebar'
})
}
}
</script>
The method then update vuex state
export const state = () => ({
sidebarOpen: false
})
export const mutations = {
toggleSidebar(state) {
state.sidebarOpen = !state.sidebarOpen;
},
}
This works fine. The bar opens and close when I click on the hamburger menu. However, for some reason, which I'm glad it does, the sidebar closes when I click outside the sidebar (if anyone could explain how this works too i'd be appreciated). When this happens, the state doesn't update and the next time i want to open i need to click on the menu twice to get it back to close then open again.
This is how i set up the sidebar
<v-app>
<v-navigation-drawer app temporary clipped v-model="status" color="blue lighten-3" dark>
<script>
export default {
computed: {
status (){
return this.$store.state.appSidebar.sidebarOpen
}
}
}
</script>
Thank you for your help!
The drawer closes when you click outside because of the temporary prop. According to your code, your state changes only on clicking the hamburger button. But internally vuetify uses the temporary property. You can either do without your vuex code or without the temporary prop.

How can I add vuetify dialog into existing application?

I created a vue dialog app/component using vue cli. It consist of a sample button to be clicked on to imitate how the dialog (What I need) will be loaded when a link on the existing application is clicked. I have a couple of issues.
When using v-app it adds the application wrapper I dont need seeing as its only the dialog I want. It creates a huge whitespace not needed. If I remove it, it errors [Vuetify] Unable to locate target [data-app] and the dialog wont load when <div #click='getInformation('USA')'></div> in the existing application is used.
Tried removing v-app and just using template but continues to error. Seems I need to still specify v-app in some way. Lost here
An example on how Im trying to pull it off but not working in App.vue
<template>
<div v-resize="onResize">
<v-dialog>
<v-card>
{{ information }}
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data() {
return {
isMobile: false,
information: []
};
},
methods: {
onResize() {
if (window.innerWidth < 425) this.isMobile = true;
else this.isMobile = false;
},
getInformatiom(country) {
axios
.get(`${api}/${country}/info`, {
headers: {
Authorization: `token`
}
})
.then(response => {
this.information = response.data.info;
});
}
}
};
main.js
import Vue from "vue";
import App from "./App.vue";
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
Vue.use(Vuetify);
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
Dialog component is ready to go, just having so much trouble getting it to show when its being called from the existing application. Just a note, the existing application does not use Vue, its only classic asp, Im only updating the dialog on the page to look/work better using vue/vuetify. Any help would be GREATLY APPRECIATED
You NEED the v-app element with vuetify.
Try this to only use the app when showing the dialog. Then use CSS to customise the v-app.
<v-app v-if='this.information && this.information.length'>
<v-dialog>...</v-dialog>
</v-app>
I would use the max-width prop of v-dialog, make it dynamic by adding :max-width and then have that bound to a computed property which subscribes to your screen size. I would not try to control it from an external div. See here for full list of sizing options
https://vuetifyjs.com/en/components/dialogs

Nuxt.js Role Based Rendering on Server

I have two roles in my database. First one is Admin and second one is normal User. I have a page that shows users table to both Admin and normal User. However, for Admin I want to show a button that creates new user with email and password. I do not want to show this button to normal User. I want it to be done on server and I do not want to set isAdmin parameter to vuex states. Because if I set it to vuex state then I can easily change the data using vue devtools... Is there any solution to render conditionally. Not like this:
<template> <div>
<b-table striped hover :items="getUserList"></b-table>
<b-button v-if="isAdmin" variant="primary" size="lg" to="/add_user">+</b-button> </div> </template>
<script> import { mapActions, mapGetters } from "vuex"; import axios from "axios";
export default { middleware: 'authorize-user', methods: {
async getUserList() {
try {
let res = await axios.post("/api/users");
if (res && res.data) {
return res.data.users ? res.data.users : [];
}
} catch (e) {
console.log("user fetch failed");
}
} }, computed: {
...mapGetters(['isAdmin']) } } </script>
Any js code on client can be altered and accessed. So your concern isn`t a valid one. You always should check permission on server. So in that case even if they change isAdmin on client and make button appear - it still wont do anything on click.