Vue 3 + Vee-validate 4 - handleSubmit does not getting triggered - vuejs3

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>

Related

Firebase signInWithEmailAndPassword not working in vue.js

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.

VueJs TypeError: "_vm.saveEmployee is not a function" on form submit

I'm coding a Employee Database in Vuejs using Firebase as my backend. On my new employee page I keep getting a 3 errors when submitting the form to add a new employee.
"Property or method "saveEmployee" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property."
"[Vue warn]: Error in v-on handler: "TypeError: _vm.saveEmployee is not a function"
"TypeError: "_vm.saveEmployee is not a function"
This is how my newemployee.vue file looks like
<template>
<div id="new-employee">
<h3>New Employee</h3>
<div class="row">
<form #submit.prevent="saveEmployee" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="employee_id" required />
<label>Employee ID#</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="name" required />
<label>Name</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="dept" required />
<label>Department</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="position" required />
<label>Name</label>
</div>
</div>
<button type="submit" class="btn">Submit</button>
<router-link to="/" class="btn grey">Cancel</router-link>
</form>
</div>
</div>
</template>
<script>
import db from "./firebaseInit";
export default {
name: "new-employee",
data() {
return {
employee_id: null,
name: null,
dept: null,
position: null
};
},
methods: {
saveEmployee() {
db.collection("employees")
.add({
employee_id: this.employee_id,
name: this.name,
dept: this.dept,
position: this.position
})
.then(docRef => {
console.log("Client added: ", docRef.id);
this.$router.push("/");
})
.catch(error => {
console.error("Error adding employee: ", error);
});
}
}
};
</script>
The whole problem is on the saveEmployee function and I can not find a fix.

Gatsby: CSS by className only sometimes works [duplicate]

I'm almost new to react.
I'm trying to create a simple editing and creating mask.
Here is the code:
import React, { Component } from 'react';
import Company from './Company';
class CompanyList extends Component {
constructor(props) {
super(props);
this.state = {
search: '',
companies: props.companies
};
}
updateSearch(event) {
this.setState({ search: event.target.value.substr(0,20) })
}
addCompany(event) {
event.preventDefault();
let nummer = this.refs.nummer.value;
let bezeichnung = this.refs.bezeichnung.value;
let id = Math.floor((Math.random()*100) + 1);
$.ajax({
type: "POST",
context:this,
dataType: "json",
async: true,
url: "../data/post/json/companies",
data: ({
_token : window.Laravel.csrfToken,
nummer: nummer,
bezeichnung : bezeichnung,
}),
success: function (data) {
id = data.Nummer;
this.setState({
companies: this.state.companies.concat({id, nummer, bezeichnung})
})
this.refs.bezeichnung.value = '';
this.refs.nummer.value = '';
}
});
}
editCompany(event) {
alert('clicked');
event.preventDefault();
this.refs.bezeichnung.value = company.Bezeichnung;
this.refs.nummer.value = company.Nummer;
}
render() {
let filteredCompanies = this.state.companies.filter(
(company) => {
return company.bezeichnung.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
}
);
return (
<div>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">Suche</div>
<div className="col-xs-12 col-sm-12 col-md-9 col-lg-9">
<div className="form-group">
<input className="form-control" type="text" value={this.state.search} placeholder="Search" onChange={this.updateSearch.bind(this)} />
</div>
</div>
</div>
<form onSubmit={this.addCompany.bind(this)}>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">Neuen Eintrag erfassen</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<input className="form-control" type="text" ref="nummer" placeholder="Nummer" required />
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<input className="form-control" type="text" ref="bezeichnung" placeholder="Firmenname" required />
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<button type="submit" className="btn btn-default">Add new company</button>
</div>
</div>
</div>
</form>
<div className="row">
<div className="col-xs-10 col-sm-10 col-md-10 col-lg-10">
<ul>
{
filteredCompanies.map((company)=> {
return <Company company={company} key={company.id} onClick={this.editCompany.bind(this)} />
})
}
</ul>
</div>
</div>
</div>
);
}
}
export default CompanyList
The Company class looks like this:
import React, { Component } from 'react';
const Company = ({company}) =>
<li>
{company.Nummer} {company.Bezeichnung}
</li>
export default Company
My question now is, why is the onclick event not being fired, when I click on a Company.
In this part:
<ul>
{
filteredCompanies.map((company)=> {
return <Company company={company} key={company.id} onClick={this.editCompany.bind(this)} className="Company"/>
})
}
</ul>
The reason is pretty simple, when you onClick like
<Company company={company} key={company.id} onClick={this.editCompany.bind(this)} />
its not an event that is set on the component, rather a prop that is being passed to the Company component and can be accessed like props.company in the Company component,
what you need to do is to specify the onClick event and className in the Company component like
import React, { Component } from 'react';
const Company = ({company, onClick, className}) => (
<li onClick={onClick} className={className}>
{company.Nummer} {company.Bezeichnung}
</li>
)
export default Company
The function passed on as prop to the Company component can be passed on by any name like
<Company company={company} key={company.id} editCompany={this.editCompany.bind(this)} className="Company"/>
and used like
import React, { Component } from 'react';
const Company = ({company, editCompany}) => (
<li onClick={editCompany}>
{company.Nummer} {company.Bezeichnung}
</li>
)
you have to bind the event on you child component :
import React, { Component } from 'react';
const Company = ({ company, onClick }) =>
<li onClick={onClick}>
{company.Nummer} {company.Bezeichnung}
</li>
export default Company
or
const Company = ({ company, ...props }) =>
<li {...props}>
{company.Nummer} {company.Bezeichnung}
</li>
if you want that all props passed to your Compagny component (exept compagny) goes to your li tag. see ES6 spread operator and rest.

State variables becoming undefined when calling vuex action

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:

I want to be able to set a "name" for a user in Firebase when using .createUserWithEmailAndPassword

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);
}
);

Resources