Call to database after successful login is not being made - vue.js

I'm having difficulty in getting an axios call to my database being initiated after a user has logged into my SPA.
A route (gaplist/3) brings the user to a page (gaplist.vue) which
detects if the user is logged in or not.
If not logged in, a login form is presented.
Once the entered username/password combo is accepted, the user is "pushed" to the same page (gaplist/3)
Here, the logged in status is detected and - this is where it all falls down - a call to the database would return a bunch of records associated with the user and the parameter "3".
Unfortunately, the last step doesn't fully happen. The logged in status is detected, but the database call is not made. Only if I refresh the page is the call made and the results presented.
What concept am I not grasping here?
Thanks, Tom.
My code is as follows:
GapList.vue (route: gaplist/3)
<template>
<v-content>
<v-container fluid fill-height>
<v-layout justify-center>
<v-flex xs12 sm6>
<h1>Production and sale of produce</h1>
<v-card flat>
<div v-if="isIn">
<p v-for="(card, id) in cards">{{card.product}}</p>
<logout-button></logout-button>
</div>
<div v-else>
<gap-login :gapid=gapid></gap-login>
</div>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-content>
</template>
<script>
import GapLogin from '../components/gap/GapLogin';
import LogoutButton from '../components/gap/LogoutButton'
export default {
name: 'GapList',
components: {
GapLogin,
LogoutButton
},
data () {
return {
gapid: this.$route.params.id,
cards: [],
lang: this.$i18n.locale,
bNoRecords: false,
}
},
created(){
this.loadCrops(this.gapid,this.lang)
},
computed: {
isIn : function(){ return this.$store.getters.isLoggedIn},
},
methods: {
loadCrops(gapid,lang){
var vm = this;
if (this.isIn){
axios.get('/gapcroplist/' + gapid)
.then(function (resp) {
vm.cards = resp.data;
})
.catch(function (resp) {
vm.bNoRecords = true;
});
}
},
}
}
</script>
GapLogin.vue
<template>
<div class="formdiv">
<v-layout justify-center>
<h3>Login</h3>
<v-card flat>
<v-alert
v-if="loginError"
:value="true"
type="error"
transition="scale-transition"
dismissible
>
You didn't enter correct information
</v-alert>
<v-form class="login" #submit.prevent="login">
<v-text-field
v-model="form.email"
type="email"
label="Email"
required
autofocus
></v-text-field>
<v-text-field
v-model="form.password"
type="password"
label="Password"
required
></v-text-field>
<v-btn
type="submit"
>Login in </v-btn>
</v-form>
</v-card>
</v-layout>
</div>
</template>
<script>
export default {
name: "GapLogin",
props: ['gapid'],
data() {
return {
form: {
email: null,
password: null
},
loginError:false
}
},
methods: {
login: function () {
this.loginError = false
this.$store.dispatch('login', this.form)
.then(() =>
{this.$router.push({path: '/gaplist/' + this.gapid})
})
.catch(err => {
this.loginError = true
}
)
},
}
}
</script>

Updated answer:
created hook will not be called again. Using updated will result in an error as well, as you would trigger another update and have an endless loop.
Instead of pushing to same route I would suggest that you emit a completed event:
In your login method in then instead of $router.push:
this.$emit('completed')
And register the event on the gap-login-component:
<gap-login #completed="completed" :gapid=gapid></gap-login>
And add that method to the GapList.vue-file:
completed () {
this.loadCrops(this.gapid, this.lang)
}
You are using axios in a global context, but it doesn't exists, it seems.
Try using this.$axios:
this.$axios.get('/gapcroplist/' + gapid)

Related

Vuetify reset form after submitting

I am using a form inside dialog using vuetify.
Imported the component in page like this -
<template>
<div>
<topicForm :dataRow="dataRow" v-model="dialog" />
</div>
</template>
methods: {
openDialog(item = {}) {
this.dataRow = item;
this.dialog = true;
},
}
Dialog form code -->
<template>
<div>
<v-dialog v-model="value" max-width="500px" #click:outside="close">
<v-card outlined class="pt-5">
<v-form ref="form" class="px-3">
<v-card-text class="pt-5">
<v-row no-gutters>
<v-text-field
required
outlined
label=" Name"
v-model="data.name"
:rules="[rules.required]"
></v-text-field>
</v-row>
<v-row no-gutters>
<v-textarea
required
outlined
label=" Description"
v-model="data.description"
></v-textarea>
</v-row>
</v-card-text>
</v-form>
<v-divider> </v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
large
dark
outlined
color="success"
#click="save"
class="ma-3"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
props: [
"dataRow",
"value",
// ----------------------
],
methods: {
save() {
if (this.$refs.form.validate()) {
this.$root
.$confirm("Are you sure you want to save?")
.then((confirm) => {
if (confirm) {
this.ADD_TOPIC_DATA(this.data)
.then((data) => {
this.FETCH_TOPIC_DATA();
this.$refs.form.reset();
this.$refs.form.resetValidation();
this.close();
})
.catch((err) => {
console.log(err)
});
}
});
}
},
close() {
this.$emit("input", false);
},
}
watch: {
dataRow(val) {
this.data = { ...val };
},
},
Problem I am having is after adding a data, then if I try to add again by opening the dialog, the required field shows validation error, which is name here!
Image of that -->
Searched in stackoverflow. Found that should use this.$refs.form.reset(). Used that in save method without success. Also used this.$refs.form.resetValidation(), but don't work.
Any suggestion?
Thanks in advance.
The problem here is you're assigning new value to dataRow when opening the dialog which triggers validation inside the dialog. You could also use lazy-validation prop which allows you to only manually trigger the validation.

Unspecified variables although I've used them

I have a site that I initially want to create an interface for SignUp and login, I created a page for Login with all the data needed to be placed, but this error appeared to me how can I handle it?
C:\Users\Super\Desktop\tatbekat\my-app\src\pages\Auth\login.vue
60:8 error 'axios' is defined but never used no-unused-vars
81:13 error 'success' is defined but never used no-unused-vars
84:14 error 'error' is defined but never used no-unused-vars
This is the login page that contains the login form and a set of data that must be set in addition to a special login function.
login.vue:
<template>
<v-container>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-card>
<v-alert color="error" :value="error" icon="close"> </v-alert>
<v-card-text>
<v-container>
<form #click="onLogin">
<v-layout row>
<v-flex xs12>
<v-text-field
name="email"
label="Email"
id="email"
v-model="loginUser.email"
type="text"
color="#43A047"
required
>
{{ email }}
</v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<v-text-field
name="password"
label="Password"
id="password"
v-model="loginUser.password"
type="password"
color="#43A047"
required
>
{{ password }}
</v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<v-btn class="green darken-1 color">
Sign In
</v-btn>
</v-flex>
</v-layout>
</form>
</v-container>
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import { mapActions } from 'vuex'
import axios from "axios";
export default {
data() {
return {
loginUser: {
email: "",
password: "",
error: false
},
};
},
method: {
...mapActions([
'LOGIN'
]),
onSignIn(){
this.$store.dispatch('LOGIN',{
email:this.loginUser.email,
password:this.loginUser.password
})
.then(success=>{
this.$router.push("/")
})
.catch(error => {
this.error = true
})
}
},
};
</script>
<style scoped>
.color {
color: #fafafa;
}
</style>
And in this file, they wrote the necessary actions for the login and signup process.
user-store.js:
import axios from "axios";
export default {
state: {},
getters: {},
mutations: {},
actions: {
SIGNUP: (/*{ commit },*/ payload) => {
return new Promise((resolve, reject) => {
axios.post(`/signup` , payload)
.then(({/*data ,*/ status }) => {
if (status === 200){
resolve(true)
}
})
.catch(error =>{
reject(error)
})
});
},
LOGIN: (/*{ commit },*/ payload) => {
return new Promise((resolve, reject) => {
axios.post(`/signin` , payload)
.then(({/*data ,*/ status }) => {
if (status === 200){
resolve(true)
}
})
.catch(error =>{
reject(error)
})
});
},
},
};
All the errors are coming from login.vue file:.
axios: you have imported axios in login.vue but the actual axios calling is in the store file so the axios in login.vue is not used and you can delete the import.
success: in onSignIn method in login.vue you didn't use success in then call back so you can rewrite it like this:.
.then(() => {this.$router.push('/')}).
error: like the previous one you can rewrite catch block in login.vue like this:.
.catch(() => {this.error = true})

Vue, error [vuex] do not mutate vuex store state outside mutation handlers

Here is original code. Very simple, sign in form. We have Email field, password. It takes this parameters and on clicking submit button it checks the user and writes his user.uid into Vuex. But I'm getting error which is listed above in title. I did research and it looks like it's a common issue in Vuex, due to those fields at some point updating Vuex store 'on a fly' which is false in my case, cause it only updates Vuex store when you press a submit button. Anyhow decided corrected to be look like this and still have no luck
original code
<template>
<div class="form__inner">
<div class="overlay" #click="$store.commit('logReg/logIn')"></div>
<v-container
fill-height
fluid>
<v-row
align="center"
justify="center">
<v-col cols="2">
<v-card>
<v-card-title>
Log in
</v-card-title>
<v-card-text>
<v-text-field placeholder="Email"
v-model="logIn"/>
<v-text-field placeholder="Password"
v-model="password"/>
</v-card-text>
<v-card-actions>
<v-btn color="success"
class="mx-auto"
#click="signIn">Log me in</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
data(){
return {
logIn: '',
password: ''
}
},
methods: {
async signIn(){
let res = await this.$fireAuth.signInWithEmailAndPassword(this.logIn, this.password);
this.$store.commit('userState', res);
}
}
}
</script>
my vuex
export const state = () => ({
user: null
})
export const mutations = {
userState(state, authUser){
state.user = authUser;
}
}
my try to fix issue which still had no luck, gives same error
<template>
<div class="form__inner">
<div class="overlay" #click="$store.commit('logReg/logIn')"></div>
<v-container
fill-height
fluid>
<v-row
align="center"
justify="center">
<v-col cols="2">
<v-card>
<v-card-title>
Log in
</v-card-title>
<v-card-text>
<v-text-field placeholder="Email"
v-model="logIn"/>
<v-text-field placeholder="Password"
v-model="password"/>
</v-card-text>
<v-card-actions>
<v-btn color="success"
class="mx-auto"
#click="signIn">Log me in</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
data(){
return {
logIn: '',
password: ''
}
},
computed: {
logg: {
get(){
return this.logIn;
},
set(val){
this.logIn = val;
}
},
pass: {
get(){
return this.password;
},
set(val){
this.password = val;
}
}
},
methods: {
async signIn(){
let res = await this.$fireAuth.signInWithEmailAndPassword(this.logg, this.pass);
this.$store.commit('userState', res);
}
}
}
</script>
You have to use mutations like this:
<script>
import {mapMutations} from 'vuex';
export default {
data(){
return {
logIn: '',
password: ''
}
},
methods: {
...mapMutations({
userState: 'userState'
}),
async signIn(){
let res = await this.$fireAuth.signInWithEmailAndPassword(this.logIn,this.password);
this.userState(res);
}
}
}
</script>

Update data from local copy of Vuex store

I am implementing a user profile edit page that initially consists of the data loaded from the vuex store. Then the user can freely edit his data and finally store them in the store.
Since the user can also click the cancel button to revert back to his original state, I decided to create a 'local' view copy of the user data fetched from the store. This data will be held in the view and once the user presses save, they will be saved in the store.
The view looks as following:
<template class="user-profile">
<v-form>
<template v-if="profile.avatar">
<div class="text-center">
<v-avatar width="120" height="120">
<img
:src="profile.avatar"
:alt="profile.firstname"
>
</v-avatar>
</div>
</template>
<div class="text-center mt-4">
<v-btn
color="primary"
dark
#click.stop="showImageDialog=true"
>
Change Image
</v-btn>
</div>
<v-row>
<v-col>
<v-text-field
label="First name"
single-line
disabled
v-model="profile.firstname"
></v-text-field>
</v-col>
<v-col>
<v-text-field
label="Last name"
single-line
disabled
v-model="profile.lastname"
></v-text-field>
</v-col>
</v-row>
<v-text-field
label="Email"
single-line
v-model="profile.email"
></v-text-field>
<v-text-field
id="title"
label="Title"
single-line
v-model="profile.title"
></v-text-field>
<v-textarea
no-resize
clearable
label="Biography"
v-model="profile.bio"
></v-textarea>
<v-dialog
max-width="500"
v-model="showImageDialog"
>
<v-card>
<v-card-title>
Update your profile picture
</v-card-title>
<v-card-text>
<v-file-input #change="setImage" accept="image/*"></v-file-input>
<template v-if="userAvatarExists">
<vue-cropper
ref="cropper"
:aspect-ratio="16 / 9"
:src="profile.avatar"
/>
</template>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
text
#click="showImageDialog=false"
>
Cancel
</v-btn>
<v-btn
color="green darken-1"
text
#click="uploadImage"
>
Upload
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<div class="mt-8">
<v-btn #click="onUpdateUser">Update</v-btn>
</div>
</v-form>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import VueCropper from 'vue-cropperjs';
import 'cropperjs/dist/cropper.css';
export default {
components: { VueCropper},
mounted() {
this.profile = this.getUserProfile ? this.getUserProfile : {}
},
data() {
return {
profile: {},
avatar: null,
userAvatarExists: false,
showImageDialog: false,
}
},
watch: {
getUserProfile(newData){
this.profile = newData;
},
deep: true
},
computed: {
...mapGetters({
getUserProfile: 'user/me',
})
},
methods: {
...mapActions({
storeAvatar: 'user/storeAvatar',
updateUser: 'user/update'
}),
onUpdateUser() {
const data = {
id: this.profile.id,
email: this.profile.email,
title: this.profile.title,
bio: this.profile.bio,
avatar: this.profile.avatar,
}
this.updateUser(data)
},
uploadImage() {
this.$refs.cropper.getCroppedCanvas().toBlob((blob => {
this.storeAvatar(blob).then((filename => {
this.profile.avatar = filename.data
this.$refs.cropper.reset()
}));
this.showImageDialog = false
}));
},
setImage(file) {
this.userAvatarExists = true;
if (file.type.indexOf('image/') === -1) {
alert('Please select an image file');
return;
}
if (typeof FileReader === 'function') {
const reader = new FileReader();
reader.onload = (event) => {
this.$refs.cropper.replace(event.target.result);
};
reader.readAsDataURL(file);
} else {
alert('Sorry, FileReader API not supported');
}
}
}
}
</script>
Issues/Questions:
As you can see from the code, after the user changes his profile
picture, the image should be rendered based on the
v-if="profile.avatar". The issue is that after the
profile.avatar is set in the uploadImage function, the
template does not see this change and no image is rendered.
However if I change the code so that the profile.avatar becomes
just avatar (it is no longer within the profile object), the
template starts to see the changes and renders the image
correctly. Why so? Does it have something to do with making a
copy from the store in the watch function?
Is it in general a good approach to keep the profile just as a local
view state or should it rather be stored in the vuex store even if
it is just a temporary data?
As you can see in the mounted function, I am setting the profile
value based on the getUserProfile getter. This is because the
watch function does not seem to be called again when switching
routes. Is there any other way how to do this?
The issue is due to the reactivity of data properties
You have used profile as an object, default it doesn't have any properties like avatar or firstname, its just empty
In vue js, If you are declaring an object, whatever the key mention in the declaration is only the part of reactivity. Once the keys inside profile changes, it rerenders the template
But still you can add new properties to a data property object by using $set
lets say in data you have declared
profile: {}
if you want to set avatar as new reactive property in runtime use
this.$set(this.profile, key, value)
which is
this.$set(this.profile, avatar, imageData)
In your above code, the setIuploadImage function
uploadImage() {
var self = this;
self.$refs.cropper.getCroppedCanvas().toBlob((blob => {
self.storeAvatar(blob).then((filename => {
self.$set(self.profile, "avatar", filename.data)
self.$refs.cropper.reset()
}));
self.showImageDialog = false
}));
},
this won't work inside arrow function in vuejs, so just preserved the this inside another variable "self" and used inside arrow function
Also in mounted function, if this.getUserProfile returns empty object, then as per javascript empty object is always truthy and directly assigning object to profile doesn't make the object reactive
mounted() {
this.profile = this.getUserProfile ? this.getUserProfile : {}
},
above code can be written as
mounted() {
if (this.getUserProfile && Object.keys(this.getUserProfile).length) {
var self = this;
Object.keys(this.getUserProfile).map(key => {
self.$set(self.profile, key, self.getUserProfile[key])
});
} else {
this.profile = {};
}
}

Vue components data and methods disappear on one item when rendered with v-for as Vuetify's cards

I have Vue component that renders a list of Vuetify cards:
<restaurant-item
v-for="card in userRestaurantCards"
:key="card['.key']"
:card="card"
>
</restaurant-item>
The card displays info obtained from props, Vuex, as well as info defined in the restaurant-item card itself:
<v-card>
<v-img
class="white--text"
height="200px"
:src="photo"
>
<v-container fill-height fluid class="card-edit">
<v-layout fill-height>
<v-flex xs12 align-end flexbox>
<v-menu bottom right>
<v-btn slot="activator" dark icon>
<v-icon>more_vert</v-icon>
</v-btn>
<v-list>
<edit-restaurant-dialog :card="card" :previousComment="comment"></edit-restaurant-dialog>
<v-list-tile >
<v-list-tile-title>Delete</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</v-flex>
</v-layout>
</v-container>
</v-img>
<v-card-title>
<div>
<span class="grey--text">Friends rating: {{ card.rating }}</span><br>
<h3>{{ card.name }}</h3><br>
<span>{{ card.location }}</span>
</div>
</v-card-title>
<v-card-actions>
<v-btn flat color="purple">Comments</v-btn>
<v-spacer></v-spacer>
<v-btn icon #click="show = !show">
<v-icon>{{ show ? 'keyboard_arrow_down' : 'keyboard_arrow_up' }}</v-icon>
</v-btn>
</v-card-actions>
<v-slide-y-transition>
<v-card-text v-show="show">
<div> {{ comment.content }} </div>
</v-card-text>
</v-slide-y-transition>
</v-card>
The script is:
import { find, isEmpty } from 'lodash-es'
import { mapGetters } from 'vuex'
import EditRestaurantDialog from '#/components/dashboard/EditRestaurantDialog'
export default {
name: 'restaurant-item',
components: {
EditRestaurantDialog
},
props: {
card: Object
},
data() {
return {
show: false,
name: this.card.name,
location: this.card.location,
rating: this.card.rating,
link: this.card.link,
photo: this.getPhotoUrl()
}
},
computed: {
comment() {
// Grab the content of the comment that the current user wrote for the current restaurant
if (isEmpty(this.card.comments)) {
return { content: 'You have no opinions of this place so far' }
} else {
const userComment = find(this.card.comments, o => o.uid === this.currentUser)
return userComment
}
},
...mapGetters(['currentUser'])
},
methods: {
getPhotoUrl() {
const cardsDefault = find(this.card.photos, o => o.default).url
if (isEmpty(cardsDefault)) {
return 'https://via.placeholder.com/500x200.png?text=No+pics+here+...yet!'
} else {
return cardsDefault
}
}
}
}
Here is the kicker: when I have 2 objects in the data, the first card component renders correctly... while the second doesn't have any of the methods or data defined right there in the script.
Here's a link to a screenshot of the Vue Devtools inspecting the first card:
https://drive.google.com/file/d/1LL4GQEj0S_CJv55KRgJPHsCmvh8X3UWP/view?usp=sharing
Here's a link of the second card:
https://drive.google.com/open?id=13MdfmUIMHCB_xy3syeKz6-Bt9R2Yy4Xe
Notice how the second one has no Data except for the route?
Also, note that both components loaded props, vuex bindings and computed properties just as expected. Only the Data is empty on the second one...
I've been scratching my head for a while over this. Any ideas would be more than welcome.
I got it to work after I moved the method getPhotoUrl method to a computed property:
computed: {
comment() {
// Grab the content of the comment that the current user wrote for the current restaurant
if (isEmpty(this.card.comments)) {
return { content: 'You have no opinions of this place so far' }
} else {
const userComment = find(this.card.comments, o => o.uid === this.currentUser)
return userComment
}
},
photoUrl() {
const cardsDefault = find(this.card.photos, o => o.default)
if (isEmpty(cardsDefault)) {
return 'https://via.placeholder.com/500x200.png?text=No+pics+here+...yet!'
} else {
return cardsDefault.url
}
},
...mapGetters(['currentUser'])
}