VeeValidate with Yup: input type="number" value is converted to string on submit - vue.js

I use VeeValidate and Yup for form validation and don't know why my input field with type="number" is converted to string on submit.
When I input 78 and submit the form the output of the console.log in the onSubmit(values) function is the following:
values: {
prioritaet: "78"
}
Am I doing something wrong here or is this the normal behavior of VeeValidate and Yup? I would like to have a number instead of a string after submit.
My code looks like this:
<template>
<div class="container m-3">
<div class="row bg-primary align-items-center py-2">
<div class="col">
<h3 class="text-light mb-0">Format {{ this.action }}</h3>
</div>
<div class="col-auto">
<h5 class="text-light mb-0">{{ this.formatTitle }}</h5>
</div>
</div>
<Form #submit="onSubmit" :validation-schema="formatSchema" v-slot="{ errors }" ref="formatForm">
<div class="row mt-4">
<div class="col">
<h5>Formatdaten</h5>
</div>
</div>
<div class="row mb-2">
<div class="col-4">
<label for="prioritaet-input">Priorität: </label>
</div>
<div class="col">
<Field type="number" name="prioritaet" id="prioritaet-input" class="w-100" />
</div>
</div>
<div class="row justify-content-end">
<div class="col-auto me-auto">
<button class="btn btn-outline-primary">Änderungen übernehmen</button>
</div>
<div class="col-auto">
<button class="btn btn-outline-primary">Abbrechen</button>
</div>
<div class="col-auto">
<button type="sumbit" class="btn btn-outline-primary">Speichern</button>
</div>
</div>
<div class="row mt-4">
<template v-if="Object.keys(errors).length">
<span class="text-danger" v-for="(message, field) in errors" :key="field">{{ message }}</span>
</template>
</div>
</Form>
</div>
</template>
<script>
import { Form, Field } from "vee-validate";
import deLocale from "../assets/yup-localization.js";
import * as Yup from "yup";
import { markRaw } from "vue";
import { mapActions, mapState } from "vuex";
Yup.setLocale(deLocale);
export default {
name: "FormatBearbeitungsSchirm",
props: ["material_id"],
data() {
let action = "neu";
let formatTitle = "Format neu";
let formatSchema = markRaw(Yup.object().shape({
prioritaet: Yup.number().min(1).max(100).integer().label("Priorität"),
}));
return { formatSchema, action, formatTitle };
},
created() {
},
components: {
Form,
Field,
},
methods: {
onSubmit(values) {
console.log("values", values);
},
},
};
</script>

It looks like there is currently no support for specifying the .number modifier on the internal field model value of <Field>, so the emitted form values would always contain a string for number-type fields.
One workaround is to convert the value in the template, updating <Form>'s values slot prop in <Field>'s update:modelValue event:
<Form #submit="onSubmit" v-slot="{ values }">
<Field 👆
type="number"
name="prioritaet" 👇
#update:modelValue="values.prioritaet = Number(values.prioritaet)"
/>
<button>Submit</button>
</Form>
demo
Another simple workaround is to convert the property inside onSubmit before using it:
export default {
onSubmit(values) {
values.prioritaet = Number(values.prioritaet)
// use values here...
}
}

You must use the .number modifier.
You can read about it here
If you want user input to be automatically typecast as a Number, you can add the number modifier to your v-model managed inputs:
const app = new Vue({
el: "#app",
data: () => ({
mynumber1: undefined,
mynumber2: undefined
}),
methods: {
submit() {
console.log(typeof this.mynumber1, this.mynumber1)
console.log(typeof this.mynumber2, this.mynumber2)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
<div id="app">
<form>
<!-- number modifier -->
<input type="number" v-model.number="mynumber1" placeholder="Type here" />
<!-- no modifier -->
<input type="number" v-model="mynumber2" placeholder="Type here" />
<input type="button" #click="submit" value="submit" />
</form>
</div>

Related

How do I solve form validation error in my code?

I am very noob in vuejs. I am trying to create vuejs form with validation. But in browser its showing nothing. In console there showing Process is not define.
This is the console error image :
<script>
import { required, email, minLength } from 'vuelidate/lib/validators'
export function validName(name) {
let validNamePattern = new RegExp("^[a-zA-Z]+(?:[-'\\s][a-zA-Z]+)*$");
if (validNamePattern.test(name)){
return true;
}
return false;
}
export default {
setup () {
return { v$: useVuelidate() }
},
data() {
return {
form: {
firstName: '',
lastName: '',
email: '',
password: '',
confirmPassword: '',
}
}
},
validations() {
return {
form: {
firstName: {
required, name_validation: {
$validator: validName,
$message: 'Invalid Name. Valid name only contain letters, dashes (-) and spaces'
}
},
lastName: {
required, name_validation: {
$validator: validName,
$message: 'Invalid Name. Valid name only contain letters, dashes (-) and spaces'
}
},
email: { required, email },
password: { required, min: minLength(6) },
confirmPassword: { required }
},
}
},
}
</script>
<template>
<form #submit.prevent="onSubmit">
<!-- First and Last Name Row -->
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for=""> First Name:</label><input class="form-control" placeholder="Enter first name" type="text" v-model="v$.form.firstName.$model">
<div class="pre-icon os-icon os-icon-user-male-circle"></div>
<!-- Error Message -->
<div class="input-errors" v-for="(error, index) of v$.form.firstName.$errors" :key="index">
<div class="error-msg">{{ error.$message }}</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="">Last Name:</label><input class="form-control" placeholder="Enter last name" type="text" v-model="v$.form.lastName.$model">
<!-- Error Message -->
<div class="input-errors" v-for="(error, index) of v$.form.lastName.$errors" :key="index">
<div class="error-msg">{{ error.$message }}</div>
</div>
</div>
</div>
</div>
<!-- Email Row -->
<div class="form-group">
<label for=""> Email address</label><input class="form-control" placeholder="Enter email" type="email" v-model="v$.form.email.$model">
<div class="pre-icon os-icon os-icon-email-2-at2"></div>
<!-- Error Message -->
<div class="input-errors" v-for="(error, index) of v$.form.email.$errors" :key="index">
<div class="error-msg">{{ error.$message }}</div>
</div>
</div>
<!-- Password and Confirm Password Row -->
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for=""> Password</label><input class="form-control" placeholder="Password" type="password" v-model="v$.form.password.$model">
<div class="pre-icon os-icon os-icon-fingerprint"></div>
<!-- Error Message -->
<div class="input-errors" v-for="(error, index) of v$.form.password.$errors" :key="index">
<div class="error-msg">{{ error.$message }}</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="">Confirm Password</label><input #input="checkPassword()" class="form-control" placeholder="Confirm Password" type="password" v-model="v$.form.confirmPassword.$model">
<!-- Error Message -->
<div class="input-errors" v-for="(error, index) of v$.form.confirmPassword.$errors" :key="index">
<div class="error-msg">{{ error.$message }}</div>
</div>
</div>
</div>
</div>
<!-- Submit Button -->
<div class="buttons-w">
<button class="btn btn-primary" :disabled="v$.form.$invalid" style="margin-left:120px">Signup</button>
</div>
</form>
</template>
I am trying to make form and validation also. But when I am running code it's showing nothing and in console telling process is not Define.
I saw that you are using useVuelidate(), but you didn't import it:
import { required, email, minLength } from 'vuelidate/lib/validators'
export function validName(name) {
...
setup () {
return { v$: useVuelidate() }
},
...
You need to add this import as well:
import useVuelidate from '#vuelidate/core'
The way you imported vuelidate is different from what Vuelidate documentation says. try to substitute the line below:
import { required, email, minLength } from 'vuelidate/lib/validators'
With this one:
import { required, email, minLength } from '#vuelidate/validators'
import useVuelidate from '#vuelidate/core'
This will probably removes the error. I recommend to read documentation with more details.

how to $emit in vuejs

VueJS emit doesn't work, I'm trying to change the value of a boolean, but it doesn't want to emit the change
here is the first component:
<template>
<div id="reg">
<div id="modal">
<edu></edu>
</div>
<div :plus="plus" v-if="plus" #open="plus = !plus">
abc
</div>
</div>
</template>
the script:
<script>
import edu from './education/edu.vue'
export default {
components: {
edu
},
data() {
return {
plus: false
}
}
}
</script>
the second component with the emit:
<template>
<div id="container">
<h2>Education</h2>
<form action="">
<input type="text" class="input" placeholder="preparatory/bachelor/engineering..">
<input type="text" class="input" placeholder="University..">
<input type="text" class="input" placeholder="Where?..">
<h6>Start date</h6>
<input type="date" class="input" value="2017-09-15" min="2017-09-15" max="2021-09-15">
<div class="end-date">
<h6>End date</h6>
<div class="flexend">
<h6>present</h6><input type="checkbox" name="" id="checkbox" #click="checked">
</div>
</div>
<input type="date" class="input" v-if="show" value="2017-09-15" min="2017-09-15">
<div class="flex-end">
<button class="btn-plus" #click="$emit('open')"><i class="fas fa-plus"></i></button>
</div>
<div class="flex-end">
<button class="btn">next <i class="fas fa-arrow-right"></i></button>
</div>
</form>
</div>
</template>
the script:
<script>
export default {
props:{
plus: Boolean
},
data(){
return{
show: true,
}
},
I thought this is working, but it seems like it's not
You should use the component name instead of the div element :
<edu :plus="plus" v-if="plus" #open="plus = !plus">
abc
</edu>

How to pass data which is coming as response from backend to the another component in vue.js?

I am developing one page which is responsible for placing order Cart.vue which contains two api calls, handleMail() method is sending email and generating a orderID and i am getting response from backend in network section like this.
{
"message":"order created successfully",
"orderID":87450588
}
i am catching that orderID and stored as a orderNumber,Now i want to pass that orderNumber to another component called orderPlace.vue and i want to display that orderNumber inside template section.for this i am creating Event bus it's not working ,please help me to pass orderNumber to another component ...
Cart.vue
<template>
<div class="main">
<div class="first-section">
<div class="content">
<h5>My Cart({{books.length}})</h5>
</div>
<div v-for="book in books" :key="book.id" class="container">
<div class="mid-section">
<img class="img-section" v-bind:src="book.file" alt="not found">
<p class="title-section">{{book.name}}</p>
</div>
<div class="author-section">
<p class="author-name">by {{book.author}}</p>
</div>
<div class="price-section">
<h6>Rs.{{book.price}}</h6>
</div>
<div class="icons">
<i class="fas fa-minus-circle"></i>
<input class="rectangle" value=1>
<i class="fas fa-plus-circle"></i>
</div>
</div>
<div class="btn-grps">
<button class="btn" v-on:click="flip()" v-if="hide==true" type="submit">Place Order</button>
</div>
</div>
<div class="second -section">
<div class="details-box">
<input type="text" v-if="hide==true" class="initial-btn" placeholder="Customer Details" />
</div>
<div v-if="hide==false" class="fill-details">
<form #submit.prevent="" class="address">
<h4 class="heading">Customer Details</h4>
<div class="name">
<input type="name" required pattern="[A-Za-z]{3,10}" v-model="name">
<label class="label">Name</label>
</div>
<div class="name">
<input type="text" required v-model="phoneNumber">
<label class="label">Phone Number</label>
</div>
<div class="pin">
<input type="text" required v-model="pincode">
<label class="label">PinCode</label>
</div>
<div class="pin">
<input type="text" required v-model="locality">
<label class="label">Locality</label>
</div>
<div class="address-block">
<input class="address" type="text" required v-model="address">
<label id="Add" class="label">Address</label>
</div>
<div class="city-landMark">
<input type="text" required v-model="city">
<label class="label">City/Town</label>
</div>
<div class="city-landMark">
<input type="text" required v-model="landmark">
<label class="label">LandMark</label>
</div>
<div class="Radio-Buttons">
<p>Type</p>
<div class="radio-btns flex-container">
<div>
<input type="radio" id="Home" value="Home" name="type" v-model="type">
<div class="first-radio"> <label class="home" for="Home">Home</label></div>
</div>
<div>
<input class="work-round" type="radio" id="Work" value="Work" name="type" v-model="type">
<div class="second-radio"> <label for="Work" class="work-label">Work</label></div>
</div>
<div>
<input class="other-round" type="radio" id="Other" value="Other" name="type" v-model="type">
<div class="third-radio"><label class="other-label" for="Other">Other</label></div>
</div>
</div>
<div class="btn-continue">
<button type="submit" #click="handlesubmit();handleMail();" class="continue">continue</button>
</div>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User';
import { EventBus } from "./event-bus.js";
export default {
created() {
if (localStorage.getItem("reloaded")) {
localStorage.removeItem("reloaded");
} else {
localStorage.setItem("reloaded", "1");
location.reload();
}
service.userDisplayCart().then(response => {
this.books = response.data;
})
},
data() {
return {
flag: true,
hide: true,
booksCount: 0,
name: '',
phoneNumber: '',
pincode: '',
locality: '',
city: '',
address: '',
landmark: '',
type: '',
books: [],
cart:'MyCart',
nameField:'Name',
phoneField:'Phone Number',
pincodeField:'PinCode',
AddressField:'Address',
localityField:'Locality',
cityField:'CityTown',
landmarkField:'LandMark',
orderNumber:''
}
},
methods: {
flip() {
this.hide = !this.hide;
},
Togglebtn() {
this.flag = !this.flag;
},
handlesubmit() {
let userData = {
name: this.name,
phoneNumber: this.phoneNumber,
pincode: this.pincode,
locality: this.locality,
city: this.city,
address: this.address,
landmark: this.landmark,
type: this.type,
}
service.customerRegister(userData).then(response => {
return response;
}).catch(error=>{
alert("invalid customer address");
return error;
})
},
// handleMail(){
// service.confirmMail().then(response=>{
// alert("order placed successfully");
// let orderNumber= response.data.orderID;
// this.$router.push({path: '/ordersuccess'});
// console.log(response);
// return orderNumber;
// }).catch(error=>{
// alert("Error in sending email");
// return error;
// })
// }
handleMail(){
service.confirmMail().then(response=>{
alert("order placed successfully");
let orderNumber= response.data.orderID;
console.log("my num",orderNumber);
EventBus.$emit("emitting-order", orderNumber);
this.$router.push({path: '/ordersuccess'});
console(response);
return orderNumber;
})
},
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/Cart.scss";
</style>
OrderPlace.vue
<template>
<div class="order-place">
<div class="image-container">
<img src="../assets/success.png" alt="not found" />
</div>
<div class="title-container">
<p>Order placed Successfully</p>
</div>
<div class="message-section">
<p>Hurray!!!your order is confirmed {{orderNumber}} and placed successfully contact us in below details
for further communication..</p>
</div>
<div class="title-section">
<div class="email-us">
<p>Email-us</p>
</div>
<div class="contact-us">
<p>Contact-us</p>
</div>
<div class="address">
<p>Address</p>
</div>
</div>
<div class="email-sec">
<p>admin#bookstore.com</p>
</div>
<div class="contact-sec">
<p>+918163475881</p>
</div>
<div class="address-sec">
42, 14th Main, 15th Cross, Sector 4 ,opp to BDA complex, near Kumarakom restaurant, HSR Layout, Bangalore 560034
</div>
<div class="button">
<router-link to="/dashboard" class="btn">Continue Shopping</router-link>
</div>
</div>
</template>
<script>
import { EventBus } from "./event-bus.js";
export default {
name: 'OrderPlace',
data(){
return{
successTitle:'Order placed Successfully',
adminEmailSection:'Email-us',
adminContactSection:'Contact-us',
adminAddressSection:'Address',
adminEmail:'admin#bookstore.com',
adminMobNum:'+918163475881',
orderNumber: ''
}
},
mounted() {
EventBus.$on("emitting-order", orderNo=> {
this.orderNumber = orderNo;
console.log(`Oh, that's great ${orderNo})`);
return orderNo;
});
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/OrderPlace.scss";
</style>
event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
If the orderPlace.vue component is not active when you do the emit. It cannot receive the element.
You can try to register your order number, in the localStorage. Or call orderPlace.vue as a child component and pass the order number to the props

VueJS how to push form params to vue router?

I'm trying to create an edit form. How can I get the parameters from the url (vue-router) and pass them to the html form? And how can I push my form data to url?
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<form class="form-horizontal">
<!-- MultiSelect -->
<div class="form-group">
<label class="col-xs-2 col-md-1 control-label">User</label>
<div class="col-xs-10 col-md-3">
<multiselect
v-model="user"
:options="userList"
:searchable="true"
label="Name"
track-by="Name">
</multiselect>
</div>
</div>
<!-- MultiSelect -->
<div class="form-group">
<label class="col-xs-2 col-md-1 control-label">Team</label>
<div class="col-xs-10 col-md-3">
<multiselect
v-model="team"
:options="teamList"
:searchable="true"
label="Name"
track-by="Name">
</multiselect>
</div>
</div>
<!-- DatePicker -->
<div class="form-group">
<label class="col-xs-2 col-md-1 control-label">Birthday</label>
<div class="col-xs-10 col-md-3">
<date-picker
v-model="calendarDate"
:shortcuts="shortcuts"
:first-day-of-week="1"
appendToBody
></date-picker>
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-1 control-label"></label>
<div class="col-md-4">
<button #click="EditUser" class="btn btn-primary">Edit</button>
</div>
</div>
</form>
So I want to bind data between the models and vue-router. When I open edit.html#/user=5&team=3&bday=1991-01-10 appropriate multiselect fields must be filled. How to do it?
You can use query in your route URL:
Store all of your multiselect fields in a computed variable, and add a watcher to push the new parameters to your URL when there are any changes in the parameter:
computed: {
queryParams({ user, team, calendarDate }) {
return {
user: user,
team: team,
bday: calendarDate
}
}
},
watch: {
queryParams() {
this.$router.push( name: this.$route.name, query: this.queryParams })
}
}
When your page is created, use a function to get the query and set the form variables:
created() {
this.setQueryParams();
},
methods() {
setQueryParams() {
const query = this.$route.query;
this.user = query.user;
this.team = query.team;
this.calendarDate = query.bday;
// you might need to do some formatting for the birthday and do some checks to see if the parameter exists in the query so you don't get errors
}
}

vuejs :disabled doesn't work

I have a problem on reactivating the button even if the conditional statement works.
it looked like the v-model wasn't communicating with the data but with a simple interpolation the value was updated.
I don't really know where I'm doing wrong on the code.
<template>
<div class="col-sm-6 col-md-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">{{stock.name}}
<small>(Price: {{stock.price}})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input v-model="quantity" type="number" class="form-control" placeholder="Quantity">
</div>
<div class="pull-right">
<button class="btn btn-success" #click="buyStock" :disabled="isDisabled">Buy</button>
</div>
<p>{{quantity}}</p>
</div>
</div>
</div>
</template>
<script>
export default {
props: [
"stock",
],
data() {
return {
quantity: 0,
}
},
methods: {
buyStock() {
const order = {
stockId: this.stock.id,
stockPrice: this.stock.price,
quantity: this.quantity
};
console.log(order);
this.$store.dispatch("buyStock", order);
this.quantity = 0;
}
},
computed: {
isDisabled() {
if (this.quantity <= 0 || !Number.isInteger(this.quantity)) {
return true;
} else {
return false;
}
}
}
}
</script>
By default, the v-model directive binds the value as a String. So both checks in your isDisabled computed will always fail.
If you want to bind quantity as a number, you can add the .number modifier like so:
<input v-model.number="quantity" type="number" ... >
Here's a working example:
new Vue({
el: '#app',
data() {
return { quantity: 0 }
},
computed: {
isDisabled() {
return (this.quantity <= 0 || !Number.isInteger(this.quantity))
}
}
})
<template>
<div class="col-sm-6 col-md-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">{{stock.name}}
<small>(Price: {{stock.price}})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input v-model="quantity" type="number" class="form-control" placeholder="Quantity">
</div>
<div class="pull-right">
<button class="btn btn-success" #click="buyStock" :disabled="isDisabled">Buy</button>
</div>
<p>{{quantity}}</p>
</div>
</div>
</div>
</template>
<script>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<input v-model.number="quantity" type="number">
<button :disabled="isDisabled">Foo</button>
</div>