I have a Vue application that is connected to Firebase and is using Vue router. I have a Login page and UserProfile page, when user enters their credentials, I want to redirect them to the UserPage using router, like this:
submit() {
firebase
.auth()
.signInWithEmailAndPassword(this.form.email, this.form.password)
.then(data => {
console.log("data", data);
this.$router.push({ name: "profile" }).catch(err => {
console.log("err");
});
})
.catch(err => {
this.error = err.message;
});
}
The weird thing is, that when I try to login, first attempt displays 'data' in the console and then the 'err" (which is undefined) and it doesnt redirect, but still logs in to user account. When I click submit button again, then it shows 'data' again but no 'err' and redirects to the UserProfile page. I cant figure out why its happening, any help would be appreciated!
The form and button code:
<form action="#" #submit.prevent="submit">
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">Email</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control"
name="email" value required autofocus v-model="form.email"/>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control"
name="password" required v-model="form.password"/>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">Login</button>
</div>
</div>
</form>
plus submit method:
methods: {
submit() {
firebase
.auth()
.signInWithEmailAndPassword(this.form.email, this.form.password)
.then(data => {
console.log("data", data);
this.$router.push({ name: "profile" }).catch(err => {
console.log("err", err);
});
})
.catch(err => {
this.error = err.message;
});
}
}
Router config (index.js):
Vue.use(Router);
const router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "*",
redirect: "/games"
},
{
path: "/games",
name: "games",
component: Games
},
{
path: "/games/:game",
name: "game",
component: Game
},
{
path: "/profile",
name: "profile",
component: Profile,
meta: {
requiresAuth: true
}
},
{
path: "/login",
name: "login",
component: Login
},
{
path: "/register",
name: "Register",
component: Register
}
]
});
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.user.loggedIn) {
next({ name: "login" });
} else {
next(); // go to wherever I'm going
}
} else {
next(); // does not require auth, make sure to always call next()!
}
});
export default router;
There are probably some side effects due to the action value. You actually don't need to submit your form, just to call the Firebase signInWithEmailAndPassword() method.
I would change your code as follows:
template:
<form>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">Email</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control"
name="email" value required autofocus v-model="form.email"/>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control"
name="password" required v-model="form.password"/>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="button" #click="login" class="btn btn-primary">Login</button>
</div>
</div>
</form>
script:
methods: {
login() {
firebase
.auth()
.signInWithEmailAndPassword(this.form.email, this.form.password)
.then(data => {
console.log("data", data);
this.$router.push({ name: "profile" })
});
})
.catch(err => {
this.error = err.message;
});
}
}
Related
I am new to Vue and learning Vue using composition API. Now I am stuck on the validation and form submitting process. I am using VeeValidate for validation and form submission. I am handleSubmit function for form submission, but this is not getting triggered at all.
I have tried creating the form as per the documentation but how I messed up. Any help would be appreciable
<template>
<div class="modal-dialog modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add Layout </h4>
<a class="modal-close" #click="isModalOpen = !isModalOpen"><i class="fa fa-times"></i></a>
</div>
<form autocomplete="off" #submit="onSubmit">
<div class="modal-body">
<div class="row">
<div class="col-md-12 col-lg-12">
<div class="mb-3">
<label class="form-label">Layout Name:<span class="text-danger">*</span></label>
<div class="form-group">
<input type="text" name="layoutName" v-model="layoutName" class="form-control"
maxlength="50" placeholder="Enter Layout Name"
:class="{ 'is-invalid': layoutNameError }" />
<div class="invalid-feedback">{{ layoutNameError }}</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
<div class="mb-3">
<label class="form-label">Module:<span class="text-danger">*</span></label>
<div class="form-group">
<Dropdown name="moduleId" v-model="moduleId" :options="moduleList"
optionLabel="text" placeholder="Select Module" class="widthAll"
#change="onChangeModule($event.value)" :editable="true" :filter="true"
:showClear="true" />
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
<div class="mb-3">
<label class="form-label">Sub Module:<span class="text-danger">*</span></label>
<div class="form-group">
<Dropdown name="subModuleId" v-model="subModuleId" :options="subModuleList"
optionLabel="text" placeholder="Select Sub Module" class="widthAll"
:editable="true" :filter="true" :showClear="true" />
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
<div class="mb-3">
<label class="form-label">Device Type:<span class="text-danger">*</span></label>
<div class="form-group">
<Dropdown name="deviceTypeId" v-model="deviceTypeId" :options="deviceTypeList"
optionLabel="text" placeholder="Select Device Type" class="widthAll"
:editable="true" :filter="true" />
</div>
</div>
</div>
<div class="col-md-6 col-lg-6 col-sm-12 col-xs-12">
<div class="mb-3">
<label class="form-label">Layout Type:<span class="text-danger">*</span></label>
<div class="form-group">
<Dropdown name="layoutTypeId" v-model="layoutTypeId" :options="layoutTypeList"
optionLabel="text" placeholder="Select Layout Type" class="widthAll"
:editable="true" :filter="true" />
</div>
</div>
</div>
<div class="col-md-12 col-lg-12">
<div class="mb-3">
<label class="form-label">Description:</label>
<div class="form-group">
<input type="text" name="description" v-model="description" class="form-control"
maxlength="50" placeholder="Enter description"
:class="{ 'is-invalid': descriptionError }" />
<div class="invalid-feedback">{{ descriptionError }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="col-12 mt-3">
<button type="submit" class="btn btn-success me-2">Submit</button>
Cancel
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import * as Yup from 'yup';
import Dropdown from 'primevue/dropdown';
import InputMask from '#/views/shared/inputmask/inputmasktemplate.vue';
//Services
import CommonService from '#/service/crmservice/commonService';
import ManageLayoutService from '#/service/crmservice/manageLayoutService';
import { useForm, useField } from 'vee-validate';
export default {
name: 'ManageLayoutAddView',
components: {
InputMask,
Dropdown
},
props: {
isModalOpen: Yup.boolean
},
setup(props) {
console.log(props)
const commonService = ref(new CommonService());
const manageLayoutService = ref(new ManageLayoutService());
const layoutTypeKey = ref('layoutType');
const deviceTypeKey = ref('deviceType');
const moduleList = ref([]);
const subModuleList = ref([]);
const layoutTypeList = ref([]);
const deviceTypeList = ref([]);
const companyId = ref(null);
const userId = ref(null);
const layoutObj = ref({
layoutName: null,
moduleId: null,
subModuleId: null,
deviceTypeId: null,
layoutTypeId: null,
description: null,
});
const schema = Yup.object().shape({
layoutName: Yup.string().required('Layout Name is required'),
moduleId: Yup.string().required('Module is required'),
subModuleId: Yup.string().required('Sub Module is required'),
deviceTypeId: Yup.string().required('Device Type is required'),
layoutTypeId: Yup.string().required('Layout Type is required'),
description: Yup.string()
});
const { handleSubmit, errors, meta } = useForm({
validationSchema: schema,
});
const { value: layoutName, errorMessage: layoutNameError } = useField("layoutName");
const { value: moduleId, errorMessage: moduleIdError } = useField("moduleId");
const { value: subModuleId, errorMessage: subModuleIdError } = useField("subModuleId");
const { value: deviceTypeId, errorMessage: deviceTypeIdError } = useField("deviceTypeId");
const { value: layoutTypeId, errorMessage: layoutTypeIdError } = useField("layoutTypeId");
const { value: description, errorMessage: descriptionError } = useField("description");
const getMasterItemList = (masterKey) => {
// $loader.show();
commonService.value.getMasterItemList(masterKey, "asd", userId.value, companyId.value)
.then(response => {
masterKey == "layoutType" ? layoutTypeList.value = response.data : deviceTypeList.value = response.data;
// $loader.hide();
});
}
const getModuleList = () => {
// $loader.show();
commonService.value.getModuleList(userId.value, companyId.value)
.then(response => {
moduleList.value = response.data;
// $loader.hide();
})
}
const getSubModuleList = (moduleId) => {
// $loader.show();
commonService.value.getSubModuleList(moduleId, userId.value, companyId.value)
.then(response => {
subModuleList.value = response.data;
// $loader.hide();
})
}
const onChangeModule = (event) => {
getSubModuleList(event.value);
}
const getUserInfo = () => {
userId.value = localStorage.getItem("userId");
companyId.value = localStorage.getItem("companyId");
}
const onSubmit = handleSubmit((values) => {
debugger;
console.log(JSON.stringify(values, null, 2));
});
onMounted(() => {
getUserInfo();
getModuleList();
getMasterItemList(layoutTypeKey.value);
getMasterItemList(deviceTypeKey.value);
})
return {
layoutObj,
deviceTypeList,
layoutTypeList,
moduleList,
subModuleList,
errors,
meta,
layoutName, layoutNameError,
moduleId, moduleIdError,
subModuleId, subModuleIdError,
deviceTypeId, deviceTypeIdError,
layoutTypeId, layoutTypeIdError,
description, descriptionError,
getMasterItemList,
getModuleList,
onChangeModule,
onSubmit
}
}
}
</script>
I'm trying to use validation option in a form. The app is developing under Vue3.
I have installed npm install #vuelidate/core #vuelidate/validator into project folder. In a file main.js I have been trying to add Vuelidate as following:
import { createApp } from 'vue'
import Vuelidate from 'vuelidate'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import 'materialize-css/dist/js/materialize.min'
createApp(App).use(store).use(router).use(Vuelidate).mount('#app')
Next I am working on Login.vue file as following
<template>
<form class="card auth-card" #submit.prevent="onSubmit">
<div class="card-content">
<span class="card-title">Example</span>
<div class="input-field">
<input
id="email"
type="text"
v-model.trim="email"
:class="{invalid: ($v.email.$dirty && !$v.email.required) || ($v.email.$dirty && !$v.email.email)}"
>
<label for="email">Email</label>
<small class="helper-text invalid"
v-if="$v.email.$dirty && !$v.email.required"
>Could not be empty</small>
<small class="helper-text invalid"
v-else-if="$v.email.$dirty && !$v.email.email"
>Incorrect form</small>
</div>
</div>
<div class="card-action">
<div>
<button
class="btn waves-effect waves-light auth-submit"
type="submit">
Enter-login
<i class="material-icons right">send</i>
</button>
</div>
</div>
</form>
</template>
<script>
import { email, required, minLength } from '#vuelidate/validators'
import useVuelidate from '#vuelidate/core'
export default {
name: 'login',
setup () {
return { v$: useVuelidate() }
},
data: () => ({
email: '',
password: ''
}),
validations: {
email: { email, required },
password: { required, minLength: minLength(6) }
},
methods: {
onSubmit () {
if (this.$v.$invalid) {
this.$v.$touch()
return
}
this.$router.push('/')
}
}
}
</script>
Then I try to run all that with npm run serve but with no success. Chrome DeveloperTools inform me about "Uncaught (in promise) TypeError: Cannot read property 'super' of undefined".
What did I do wrong? Is it possible to use Vue3 and Vuelidate together?
Lidia
Step 1: Import useVuelidate inside component
import useVuelidate from "#vuelidate/core";
import { email, required, minLength } from "#vuelidate/validators";
Step 2: Initalize useVuelidate inside component
setup() {
return { v$: useVuelidate() };
},
Step 3: Initalize your modal data
data() {
return {
email: '',
password: ''
};
},
Step 4: Add validations rule
validations() {
return {
email: { email, required },
password: { required, minLength: minLength(6) }
};
},
Step 5: Add form submit method
methods: {
onSubmit: function() {
this.v$.$touch();
if (this.v$.$error) return;
alert('Form is valid')
}
}
Step 6: HTML template design will be like,
<form class="card auth-card" #submit.prevent="onSubmit">
<div class="card-content">
<span class="card-title">Example</span>
<div class="input-field">
<label for="email">Email <span class="required">*</span></label>
<input id="email" type="text" v-model.trim="email">
<small class="error" v-for="(error, index) of v$.email.$errors" :key="index">
{{ capitalizeFirstLetter(error.$property) }} {{error.$message}}
</small>
</div>
<div class="input-field">
<label for="email">Password <span class="required">*</span></label>
<input id="email" type="text" v-model.trim="password">
<small class="error" v-for="(error, index) of v$.password.$errors" :key="index">
{{ capitalizeFirstLetter(error.$property) }} {{error.$message}}
</small>
</div>
</div>
<div class="card-action">
<div>
<button class="btn waves-effect waves-light auth-submit" type="submit"> Enter-login<i class="material-icons right">send</i>
</button>
</div>
</div>
</form>
</template>
DEMO
I'm trying to get the sign in part working on my webapp but it's not working properly.
Whenever I press the login button the page either refreshes and the url gets updated with the credentials and stays at the same page OR the router gets pushed and goes to the 'homepage' without logging the user in.
I also followed this guide for reference: https://blog.logrocket.com/vue-firebase-authentication/
What's weird is that the sign up part is working just fine.
SignIn.vue
<div class="card-body">
<form>
<!-- email -->
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
<input id="email" type="email" class="form-control" name="email" placeholder="e-mail" value required autofocus v-model="form.email" />
</div>
<!-- password -->
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
<input id="password" type="password" class="form-control" name="password" placeholder="password" required v-model="form.password" />
</div>
<!-- error -->
<div v-if="error" class="alert alert-danger animated shake">{{ error }}</div>
<br />
<!-- login -->
<div class="form-group d-flex justify-content-between">
<div class="row align-items-center remember"><input type="checkbox" v-model="form.rememberMe" />Remember Me</div>
<input type="submit" #click="submit" value="Login" class="btn float-right login_btn" />
</div>
</form>
</div>
Script in SignIn.vue
<script>
import firebase from 'firebase';
export default {
data() {
return {
form: {
email: '',
password: '',
rememberMe: false
},
error: null
};
},
methods: {
submit() {
firebase
.auth()
.signInWithEmailAndPassword(this.form.email, this.form.password)
.catch(err => {
this.error = err.message;
})
.then(data => {
this.$router.push({ name: 'home' });
});
}
}
};
</script>
Store.js
import Vue from 'vue';
import Vuex from 'vuex';
import profile from './modules/profile';
import authenticate from './modules/authenticate';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
profile,
authenticate
}
});
Authenticate.js in store
const state = {
user: {
loggedIn: false,
data: null
}
};
const getters = {
user(state) {
return state.user;
}
};
const mutations = {
SET_LOGGED_IN(state, value) {
state.user.loggedIn = value;
},
SET_USER(state, data) {
state.user.data = data;
}
};
const actions = {
fetchUser({ commit }, user) {
commit('SET_LOGGED_IN', user !== null);
if (user) {
commit('SET_USER', {
displayName: user.displayName,
email: user.email
});
} else {
commit('SET_USER', null);
}
}
};
export default {
state,
mutations,
actions,
getters
};
It is probably because you assign the submit type to your button, your form is submitted before the Firebase method is triggered.
You should change the button code from
<input type="submit" #click="submit" value="Login" class="btn float-right login_btn" />
to
<input type="button" #click="submit" value="Login" class="btn float-right login_btn" />
See the W3 specification for more detail on button types.
Error Message:Error: Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field articleName)
I am trying to add the variables from my form to my Firebase Cloud Firestore database. I use vuex for global state management. When I pass the variables from my CreateSale component to sale->state they get defined. But when I call the action createSale when submitting the form, the state variables are undefined. Everything should be setup properly. I registered Vuex aswell as configured firebase.
sale.js (vuex module)
createSale(state, getters) {
console.log(state.articleName) //undefined
db.collection('clothes').add({
articleName: state.articleName,
description: state.description,
brand: state.brand,
price: state.price,
imagesRefs: getters.fileRefs
}).then(docRef => {
for (var i = 0; i < state.files.length; i++) {
}
this.$router.push('/')
}).catch(error => alert(error.message))
}
CreateSale.vue
template:
<div class="container" id="createSale">
<form #submit.prevent="createSale" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="articleName" v-on:change="setArticleName(articleName)" required>
<label>Article Name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="description" v-on:change="setDescription(description)" required>
<label>Description</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="brand" v-on:change="setBrand(brand)" required>
<label>Brand</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input style="padding-top: 16px;" type="number" v-model="price" v-on:change="setPrice(price)" required>
<label>Price</label>
</div>
</div>
<UploadFile />
<button type="submit" class="btn btn-success">Submit</button>
<router-link to="/" class="btn grey">Cancel</router-link>
</form>
script:
export default {
name: 'createSale',
components: {
UploadFile
},
data() {
return {
articleName: '',
description: '',
brand: '',
price: ''
}
},
methods: {
...mapMutations('sale', [
'setArticleName',
'setDescription',
'setBrand',
'setPrice'
]),
...mapActions('sale', {
createSale: 'createSale',
console: 'console'
})
}
Your action need to be
createSale({state, getters}) {
console.log(state.articleName) //undefined
db.collection('clothes').add({
articleName: state.articleName,
description: state.description,
brand: state.brand,
price: state.price,
imagesRefs: getters.fileRefs
}).then(docRef => {
for (var i = 0; i < state.files.length; i++) {
}
this.$router.push('/')
}).catch(error => alert(error.message))
}
The first parameter which is passed to action is context object, you need to get state and getters from this context object.
Example from vuex website:
VueJS, with Vuex and Firebase allow me to to register a user in my app easily but can I, AT THE SAME TIME, create one or more database refs for that specific user?
I can set and get the user.email from the users list, but I'm stuck when it comes to create more fields in the actual database.
Can you help me out?
This is my sign-up component code:
<template>
<section class="section">
<h1>Sign-up</h1>
<h2>Create an Account</h2>
</div>
<div class="card-content">
<form v-on:submit.prevent>
<div class="field">
<label class="label">Name</label>
<div class="control">
<input class="input" type="text" placeholder="Your Display Name" v-model="name">
</div>
</div>
<div class="field">
<label class="label">Email</label>
<div class="control">
<input class="input" type="email" placeholder="joe#bloggs.com" v-model="email">
</div>
</div>
<div class="field">
<label class="label">Password</label>
<div class="control">
<input class="input" type="password" v-model="password">
</div>
</div>
<button type="submit" class="button is-primary" v-on:click="signUp">Sign-up</button>
</form>
<p>Already registered? <router-link to="/Sign-In">Log in</router-link></p>
</section>
</template>
<script>
import Firebase from "firebase";
export default {
data: function() {
return {
name: "",
email: "",
password: ""
};
},
methods: {
signUp: function() {
Firebase.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(
user => {
this.$router.replace('dashboard');
},
error => {
alert(error.message);
}
);
}
}
};
</script>
I am assuming I could create another method but I really want to keep it as simple as possible.
You need to make an additional API call to set the displayName on the user:
firebase.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(
user => {
return user.updateProfile({displayName: this.displayName});
},
error => {
alert(error.message);
}
);