view not updated after the url change - vue.js

I'm trying to access users of different users using my search input where I can type the name of the user and then click on any of the result and navigate to their page. The first user page is displayed but when I search for another user and click on the user name then the route gets updated in the address bar but the view in the window still displays the earlier user. what am I doing wrong in my code?
<template>
<div>
<custom-component :users="users" #input="onClick"></custom-component>
</div>
</template>
<script>
export default {
methods: {
onClick(id) {
this.$router.push(`/user/${id}`);
},
},
watch: {
$route(to, from) {
if (to.params.id !== from.params.id) {
this.$router.push(to.fullPath);
}
},
},
};

You need to change watch and fetch the users list again. No need to push the router again in watch.
watch: {
$route(to, from) {
// react to route changes...
let self = this;
self.getUsers(); // For example
}
},

Related

Nuxt / Vue redirect if vuex property is undefind not working

I got a nuxt app running which has an account page. This account page uses mapState computed properties for the user. User data is used in the account page template as well as its child components via props.
Whenever I start the app by going to myurl/account I get "can not read property x of undefined". Its obvious to me, as there is no logged in user when I go right away to /account.
I tried to push the routes back to the /login page within the created() hook of the account page, but its not working. I still get the same error.
How to deal with users trying to access a page before a property used by the template is set? The created hook logs only server side, not in the dev tools of chrome. Shouldnt this.$router.push("login") work?
ACCOUNT PAGE
<template>
<v-container fluid class="px-0 py-0 mt-12">
<v-row>
<accountheader :user="user" :company="company" />
</v-row>
</v-container>
</template>
<script>
import { mapState } from "vuex";
export default {
transitions: "page",
computed: {
...mapState("user", {
company: (state) => state.company,
user: (state) => state.user,
}),
},
head: {
title: "ACCOUNT",
meta: [
{
hid: "description",
name: "description",
content: "account page",
},
],
},
created() {
if (this.user === undefined) {
this.$router.push("/login");
}
},
};
</script>
<style></style>
I managed to get arround this myself by implementing a middleware in the page file itself like so. In case anyone runs into the same issue.
Solution
middleware({ store, redirect }) { // If the user is not authenticated if (store.state.user.user === undefined) { return redirect("/login"); } },

Data's getters able to watch non-reactive localStorage, but not for computed?

I have a section that shows a Login button when there a user is not authenticated(as shown in localStorage), otherwise username and a 'logout botton' will be shown.
The code works when I put the authenticated inside the data block. However, if I put it in the computed field, it always shows login
#template
<router-link v-if="!authenticated" to="login">Log In</router-link>
<template v-else>
Logged in as {{username}}
<button #click="logout">Log out</button>
</template>
#script
data: function() {
return {
// get authenticated() { //this works
// return localStorage.getItem('authenticated');
// },
}
},
computed: function() {
return {
authenticated: function() { //this does not work
return localStorage.getItem('authenticated');
}
}
},
Vue isn't able to observe changes to local storage items, and so the authenticated computed property will forever return the same value because Vue caches the values of computed properties.
In this scenario you would typically use a method:
methods: {
authenticated() {
return localStorage.getItem('authenticated')
}
}
although your getter would work too.

How to open vuetify dialog after user logs in to application

In my application I want to show a modal to introduce the user in my application, so it will appear only in the first time he logs in. What I am doing is storing isNewUser in the global state and using it to know if it should render the modal or not using the same process described in this answer. (I'm not using event bus)
Here is my parent component:
<template>
<Intro :value="isNewUser" #input="finishTutorial" />
</template>
mounted() {
const store = this.$store;
this.isNewUser = store.state.auth.user.isNewUser;
},
When the user logs in and this component is rendered I saw the dialog being rendered and closing. If I hit f5 it reloads the page and dialog is showed correctly.
If I do the bellow modification it works, but I don't want to solve the problem this way since it won't work for all cases, it will depend on the speed of the user computer/internet.
mounted() {
setTimeout(() => {
const store = this.$store;
this.isNewUser = store.state.auth.user.isNewUser;
}, 2000);
},
I've tried using v-if as well
<template>
<Intro v-if="isNewUser" :value="true" #input="finishTutorial" />
</template>
<script>
export default {
components: {
Intro,
},
data() {
return {
isNewUser: false,
};
},
mounted() {
const store = this.$store;
this.isNewUser = store.state.auth.user.isNewUser;
},
methods: {
async finishTutorial() {
this.$store.dispatch('auth/finishTutorial');
this.isNewUser = false;
},
},
};
</script>
You can use a computed property to do so:
computed: {
isNewUser() {
return this.$store.state.auth.user.isNewUser;
}
}
and in the template you would do like so:
<template>
<Intro :value="isNewUser" #input="finishTutorial" />
</template>

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.

Vue 2 triggering method on child component

I will be happy if i can either trigger and event or call a method within the vue-button component when the user object gets updated. I need to make sure that this only happens on this specific vue-button and not on any other ones on the page.
The button controls a state object that styles the button depending on response from the server. Will display red if an error response is returned or green if a successful response is returned. In ether case the button then is also disabled so users can't spam click it. I need to have the ability to reset the button from the parent when the user is updated.
I do not believe that a global event bus is the solution because i need granular control over which components respond to the event and this button is used in a lot of places.
<template>
<div class="row">
<div class="col-12">
<basic-input-field :resource="user" :set-resource="setUserProperty" property="name" label="Name"></basic-input-field>
<basic-input-field :resource="user" :set-resource="setUserProperty" property="email" label="Email"></basic-input-field>
<basic-input-field :resource="user" :set-resource="setUserProperty" property="password" label="Password"></basic-input-field>
<vue-button :on-click="updateUser" label="Save"></vue-button>
</div>
</div>
</template>
<script>
import axios from 'axios';
import basicInputField from '../general/forms/basic-input-field.vue';
import vueButton from '../general/buttons/vue-button.vue';
export default {
name: 'user',
data() {
return {
};
},
mixins: [],
components: {
basicInputField,
vueButton
},
computed: {
user() {
return this.$store.state.user;
},
},
mounted() {
this.$httpGet('user', {id: 5});
},
watch: {
'user': function (newUser) {
// I want to trigger an event inside the component vue-button
// I do not want to trigger then event in every vue-button component on the page just this vue-button
// I need to call a resetStatus method within the vue-button component when the user changes
// This would have worked in vue 1 with an event 'resetStatus' that would call the method in the vue-button component
this.$broadcast('resetStatus');
}
},
methods: {
setUserProperty(property, value) {
this.$store.commit('UPDATE_MODULE_RESOURCE', {module: 'user', resource: property, value: value});
},
updateUser() {
return this.$httpPut('user', {id: this.user.id}, this.user);
}
},
};
</script>