Vue, error [vuex] do not mutate vuex store state outside mutation handlers - vue.js

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>

Related

How do I capture the value of the prop in the text field?

I have a prop and currently am able to get the data of the prop, Am trying a way to capture the item of the prop when saving the form.
Is there a way where i can take the value and pass if in a hidden text-area and bind the data to the vmodel?
Any help I appreciate.
<v-dialog v-model="dialog" persistent max-width="800">
<template v-slot:activator="{ on }">
<v-btn dark v-on="on" color="primary" round> Make payment </v-btn>
</template>
<v-card>
<v-card-title class="headline primary">
<span class="white--text">Add a new Doctor Payment Record {{ queueId }}</span>
<v-btn icon dark #click.native="dialog = false" absolute right>
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<users-search
:leave-selected="true"
idOnly
label="Select Doctor"
#results="setDoctor"
>
</users-search>
<div class="row px-3">
<v-autocomplete
class="px-3 col-sm-8"
v-model="expense.bank"
v-if="banks.data"
:items="banks.data"
outline
chips
label="Select bank"
item-text="name"
item-value="id"
>
</v-autocomplete>
<v-text-field
class="px-3 col-sm-8"
outline
flat
v-model="expense.amount"
type="number"
#input="expense.percentage()"
required
label="Amount *"
persistent-hint
/>
</div>
<v-text-field
class="px-3"
outline
flat
v-model="expense.total_paid"
required
label="amount paid"
persistent-hint
/>
<v-text-field
class="px-3"
outline
flat
:value="setQueue"
v-model="expense.queueId"
required
:label=queueId
persistent-hint
/>
<v-alert :value="true" type="error" v-if="errors.any()">
<div v-html="errors.display()"></div>
</v-alert>
<v-layout row wrap>
<v-flex xs12>
<v-btn
color="success"
:loading="saveLoader"
#click="recordExpense()"
>save</v-btn
>
</v-flex>
</v-layout>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
import NewUser from "#finance/libs/users/NewUser";
import {mapActions, mapGetters} from "vuex";
export default {
props: [
'queueId'
],
data: () => ({
dialog: false,
expense: new NewUser(),
saveLoader: false,
}),
computed: {
...mapGetters({
banks: "getBanks",
}),
balance: function () {
return parseFloat(10);
},
submitted() {
return this.expense.form.submitted;
},
contaminated() {
return this.expense.form.errorDetected;
},
errors() {
return this.expense.form.errors;
},
},
watch: {
submitted(v) {
if (v) {
this.saveLoader = false;
}
},
contaminated() {
this.saveLoader = false;
},
},
methods: {
...mapActions({
fetchBanks: "setBanks",
}),
setDoctor(user) {
this.expense.doctor_id = user.id;
},
setQueue(){
console.log(this.queueId);
this.expense.queueId = this.queueId;
},
async recordExpense() {
this.saveLoader = true;
let response = await this.expense.saveExpense();
this.saveLoader = false;
if (response) {
this.dialog = false;
this.$emit("expenseCreated");
}
},
},
mounted() {
this.fetchBanks();
}
};
</script>
The prop queueId i also want to store it along with the user information from the form.
Try this one, it should work:
<template>
<textarea v-model="hiddenValue" :style="{ display: 'none' }"></textarea>
</template>
<script>
export default {
props: [ 'queueId' ],
data() {
return {
hiddenValue: this.queueId
}
}
}
</script>
In case you will no need the prop to be modified, please bind the texarea value to the prop directly:
<textarea hidden v-model="queueId" :style="{ display: 'none' }></textarea>

Vue.js & Django - axios post response status 200 but called catch error

i'm learning Vue.js and Django.
I want to post user info.
axios response status 200 code, but catch function is called.
If the response is successful, the login 'success dialog' is called , but the login 'fail dialog' is also called.
this image is console log.
enter image description here
please help me!
here is my code.
<script>
// LoginDialog.vue
import axios from "axios";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
export default {
name: "LoginDialog",
components: {},
props: {
dialog: Boolean,
},
data() {
return {
showPassword: false,
dialogClose: {
success: false,
fail: false,
},
me: {},
};
},
setup() {},
created() {},
mounted() {},
unmounted() {},
computed: {
dialogOpen: {
get() {
return this.dialog;
},
set(val) {
this.$emit("close", val);
},
},
},
methods: {
save() {
console.log("save()...");
const postData = new FormData(document.getElementById("login-from"));
axios
.post("/api/login/", postData)
.then((res) => {
console.log("LOGIN POST RES", res);
// alert(`user ${res.data.username} login OK`);
this.dialogClose.success = true;
this.this.me = res.data;
})
.catch((err) => {
console.log("LOGIN POST ERR.RESPONSE", err.response);
this.dialogClose.fail = true;
// alert("login FAIL");
});
},
},
};
</script>
// LoginDialog.vue
<template>
<div>
<!-- 로그인 dialog -->
<v-dialog v-model="dialogOpen" max-width="500">
<v-card width="800" class="mx-auto mt-5">
<v-toolbar color="primary" dark flat>
<v-toolbar-title class="pb-0">
<h2>Login</h2>
</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-form id="login-from" ref="loginForm">
<v-text-field
label="Username"
name="username"
prepend-icon="mdi-account-circle"
></v-text-field>
<v-text-field
:type="showPassword ? 'text' : 'password'"
label="Password"
name="password"
prepend-icon="mdi-lock"
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
#click:append="showPassword = !showPassword"
autocomplete="on"
></v-text-field>
</v-form>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text
color="grey"
center
#click="(dialogOpen = false), $refs.loginForm.reset()"
>Cancel</v-btn
>
<v-btn color="success" center #click="dialogOpen = false"
>Register</v-btn
>
<v-btn color="info" center #click="save()">Login</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- login success 팝업창 -->
<v-dialog v-model="dialogClose.success" max-width="290">
<v-card>
<v-card-title class="text-h5"> Login Success! </v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
text
#click="dialogClose.success = false"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- login fail 팝업창 -->
<v-dialog v-model="dialogClose.fail" max-width="290">
<v-card>
<v-card-title class="text-h5"> Login Fail! </v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click="dialogClose.fail = false">
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
So the Axios call goes well, but after that, in the .then block an error is thrown and caught by the .catch block. This is also why the err.response is empty, because that is an error object of Axios. If you log the err instead of err.response you might see the actual error (as Akzhol Kadylbek suggested in his comment).
Looking at your code, I presume that error comes from this line:
this.this.me = res.data;
/// change to this.me = res.data;
The double this. looks like a typo.
Hope this helps.

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.

How to programatically close a Veutify dialog

Hello everyone i was searching on the vuetify documentation a function or something like that to close a form dialog just after getting the axios response with the status 200 ..
i don't if there's a way to get a dialog instance and use a close() method on it like the bootstrap modals
here's my code :
template code
<template>
<v-dialog justify-center max-width="500px">
<template v-slot:activator="{on}">
<v-icon
small
v-on="on"
>
mdi-pencil
</v-icon>
</template>
<v-card>
<form #submit.prevent="submit">
<v-card-text>
<v-text-field
v-model="name"
label="Name"
required
></v-text-field>
<v-text-field
v-model="email"
label="E-mail"
required
></v-text-field>
<v-text-field
v-model="password"
label="password"
required>
</v-text-field>
</v-card-text>
<v-card-actions>
<v-btn
color="blue darken-1"
text
>close</v-btn>
<v-btn
color="blue darke-1"
text
type="submit"
>apply</v-btn>
</v-card-actions>
</form>
</v-card>
</v-dialog>
</template>
and here's the script
<script>
export default {
data () {
return {
name: '',
email: '',
password: ''
}
},
methods: {
submit() {
let Data = new FormData()
Data.append('name', this.name)
Data.append('email', this.email)
Data.append('password', this.password)
axios.post('http://stage.test/admin/users', Data)
.then(Response => {
if (Response.status === 200) {
}
})
}
},
}
</script>
Try to bind dialog component to a data property called open as follows :
<template>
<v-dialog v-model="open" justify-center max-width="500px">
<template v-slot:activator="{on}">
<v-icon
small
v-on="on"
>
mdi-pencil
</v-icon>
.....
then in then callback assign false to this.open
<script>
export default {
data () {
return {
open:false,
name: '',
email: '',
password: ''
}
},
methods: {
submit() {
let Data = new FormData()
Data.append('name', this.name)
Data.append('email', this.email)
Data.append('password', this.password)
axios.post('http://stage.test/admin/users', Data)
.then(Response => {
if (Response.status === 200) {
this.open=false
}
})
}
},
}
</script>

Call to database after successful login is not being made

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)