How to pass data which is coming as response from backend to the another component in vue.js? - 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

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 change the dropdown item in vue after login successful

I have a navbar and login components in vue. So my target is when if the user login, on the dropdown in navbar, I want to show Profile as an item, and if the user is not login I want to show Sign In/Register item as an item. So I want to manipulate the dropdown that you see on the picture below:
And here is my Navbar component:
<template>
<nav class="navbar navbar-expand-lg shadow">
<div class="container navbar-container">
<div class="navbar navbar-profile">
<div class="dropdown">
<button class="btn dropdown-toggle" type="button" id="dropdownProfile" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-user"></i>
</button>
<ul class="dropdown-menu dropdown-menu-left">
<!-- User is not authenticated so show login/register-->
<li v-if="authenticated"><router-link :to="{ name: 'login' }">Login/Register</router-link></li>
<!-- User is authenticated so show profile-->
<li v-if="authenticated"><router-link :to="{ name: 'profile' }">Profile</router-link></li>
</ul>
</div>
</div>
</div>
</nav>
</template>
<script>
export default {
data() {
return {
authenticated: false,
authenticationFailed: false,
};
},
};
</script>
So in the code I didnt make anything with the authenticated/authenticationFailed values because I dont know how can I check it from the Login component. And here is my login component:
<template>
<div id="login" class="card-container">
<div class="card col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="headers">
<h3 class="card-header text-center active">LOGIN</h3>
<router-link :to="{name: 'register'}"><h4 class="card-header text-center non-active">REGISTER</h4></router-link>
</div>
<div class="card-body">
<div class="form-row">
<div class="form-group">
<input type="text" class="form-control" v-model="email" placeholder="Email" id="email" #keyup.enter="signIn" >
</div>
<div class="form-group">
<input type="password" class="form-control" v-model="password" placeholder="Password" id="password" #keyup.enter="signIn">
</div>
<div class="form-group form-check">
<input type="checkbox" id="rememberMeSignIn" class="form-check-input">
<label class="form-check-label" for="rememberMeSignIn">Remember Me</label>
</div>
<div class="form-group links">
<router-link :to="{name: 'forget-password'}">Forget Password?</router-link>
</div>
</div>
<div class="button-area">
<button v-on:click="signIn()" type="submit" class="btn button">Sign In</button>
</div>
</div>
</div>
</div>
</template>
<script>
import authHelper from "../helpers/authHelper";
import GENERAL_APP_CONSTANTS from "../constants/GeneralAppConstants";
export default {
name: 'Login',
beforeMount() {
this.authenticated = authHelper.validAuthentication();
},
mounted() {
EventBus.$on(GENERAL_APP_CONSTANTS.Events.CheckAuthentication, () => {
this.authenticated = authHelper.validAuthentication();
if (this.authenticated) {
this.email = this.password = "";
this.authenticationFailed = false;
}
});
EventBus.$on(GENERAL_APP_CONSTANTS.Events.LoginFailed, () => {
this.authenticationFailed = true
});
},
data () {
return {
authenticated: false,
authenticationFailed: false,
email: '',
password: '',
rememberMe: false,
}
},
methods: {
signIn: function () {
authHelper.signIn(this.email, this.password, () => {
this.$router.push({name: 'home'});
});
}
}
}
</script>
I tried to make it as simple as possible and if you can help me with this , I would be really glad.
PS: this is my index.vue that you can see the relation between Navbar and Login page and my login component is displaying when router-view changed:
<div id="app">
<Topnav/>
<Navbar/>
<router-view></router-view>
<Footer/>
</div>

Cloning div with vue

<template>
<b-form #submit.prevent="Submit" class="mb-5">
<div class="inputArea" v-for="input in inputs" :key="input.id">
<b-form-group label-cols-sm="2" label="Solution (EN)">
<ckeditor :editor="ckeditor" v-model="form.body.en" :config="ckeditorConfig"></ckeditor>
<div v-if="errors['body.en']">
<div v-for="err in errors['body.en']" :key="err">
<small class="text-danger">{{ err }}</small>
</div>
</div>
</b-form-group>
<b-form-group label-cols-sm="2" label="Body (FR)">
<ckeditor :editor="ckeditor" v-model="form.body.np" :config="ckeditorConfig"></ckeditor>
<div v-if="errors['body.np']">
<div v-for="err in errors['body.np']" :key="err">
<small class="text-danger">{{ err }}</small>
</div>
</div>
</b-form-group>
<b-form-group label-cols-sm="2" label="Body (IT)">
<ckeditor :editor="ckeditor" v-model="form.body.in" :config="ckeditorConfig"></ckeditor>
<div v-if="errors['body.in']">
<div v-for="err in errors['body.in']" :key="err">
<small class="text-danger">{{ err }}</small>
</div>
</div>
</b-form-group>
</div>
<b-form-group label="" label-cols-sm="2">
<b-button type="button" class="text-white" variant="dark" #click="addRow">Add row</b-button>
</b-form-group>
<b-form-group label="" label-cols-sm="2">
<b-button type="submit" class="text-white" variant="dark">Submit</b-button>
</b-form-group>
</b-form>
</template>
<style lang="scss">
</style>
<script>
import CKEditor from '#ckeditor/ckeditor5-vue2'
import ClassicEditor from '#ckeditor/ckeditor5-build-classic'
export default {
name: 'Interaction',
components: {
ckeditor: CKEditor.component
},
data(){
return{
counter: 0,
inputs: [
{
en: '',
np: '',
in: '',
}],
form: {
body: [
{
en: '',
np: '',
in: '',
}
],
},
errors: {},
ckeditorData: '<p></p>',
ckeditorConfig: {
// The configuration of the editor
},
ckeditor: ClassicEditor
}
},
methods: {
Submit(){
this.storing = true
this.errors = {}
var self = this
axios.post('/this-is-a-post-url', this.form)
.then(function(response){
console.log(response)
})
},
addRow() {
this.inputs.push({
en: '',
it: '',
fr: '',
id: `${++this.counter}`,
value: '',
});
}
}
}
</script>
I will have array coming in the body name so I am trying to to clone the clone body on a click of a button which has a function AddRow. I want to clone the three fields en,np,in and I want it work like normal html works in this. Example when we clone html form it create input field like so <input name="body['en'][0]"> and when we clone another time it creates something like this <input name="body['en'][1]">.
I have the above code, it clones the body but it also clones the added text before cloning. I want to add an empty field while cloning and also want to update v-model. How can I do that?
Refer below example:
https://codepen.io/telen/pen/OeNZVV
<main class="container">
<form id="app" data-apartments='[{ "price": "23000", "rooms": "12" }, { "price": "42000", "rooms": "32" }]'>
<h1>
Dynamic apartment forms
</h1>
<hr>
<div class="row">
<div class="col-xs-2">
<button type="button" v-on:click="addNewApartment" class="btn btn-block btn-success">
Add +
</button>
</div>
<div class="col-xs-10">
Would you like add more apartments?
</div>
</div>
<div v-for="(apartment, index) in apartments">
<div class="row">
<div class="col-xs-2">
<label> </label>
<button type="button" v-on:click="removeApartment(index)" class="btn btn-block btn-danger">
Rem -
</button>
</div>
<div class="form-group col-xs-5">
<label>Price (HUF)</label>
<input v-model="apartment.price" type="number"
name="apartments[][price]" class="form-control" placeholder="Price">
</div>
<div class="form-group col-xs-5">
<label>Rooms (PCS)</label>
<input v-model="apartment.rooms" type="number"
name="apartments[][rooms]" class="form-control" placeholder="Rooms">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-2">
<button type="submit" v-on:click.prevent="sumbitForm" class="btn btn-block btn-primary">
Submit
</button>
</div>
<div class="col-xs-10">
Open the console (F12) and see the result
</div>
</div>
<hr>
<pre>{{ $data }}</pre>
</form>
JS:
window.app = new Vue({
el: '#app',
data: {
apartment: {
price: '',
rooms: ''
},
apartments: [],
},
mounted: function () {
/*
* The "data-apartments" could come from serverside (already saved apartments)
*/
this.apartments = JSON.parse(this.$el.dataset.apartments)
},
methods: {
addNewApartment: function () {
this.apartments.push(Vue.util.extend({}, this.apartment))
},
removeApartment: function (index) {
Vue.delete(this.apartments, index);
},
sumbitForm: function () {
/*
* You can remove or replace the "submitForm" method.
* Remove: if you handle form sumission on server side.
* Replace: for example you need an AJAX submission.
*/
console.info('<< Form Submitted >>')
console.info('Vue.js apartments object:', this.apartments)
window.testSumbit()
}
}
})
/*
* This is not Vue.js code, just a bit of jQuery to test what data would be submitted.
*/
window.testSumbit = function () {
if (!window.jQuery) {
console.warn('jQuery not present!')
return false
}
console.info('Submitted (serverside) array:', jQuery('form').serializeJSON())
}

How to redirect after success fully registered in vue.js?

I developed one page which is responsible for displaying cart items and the response is coming from backend upto this it's working fine .Now inside same page one more registration page is there and that also integrated with backend API, after successfully registered it should be redirected to /orderSuccess page but it's not redirecting ,Here what is the issue please help me How to fix this issue.
After registered it changes my url path /cart to /ordersuccess also but page is not displaying..please help me to fix this issue
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 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 for="Other">Other</label></div>
</div>
</div>
<div class="btn-continue">
<button type="submit" #click="handlesubmit();" class="continue">continue</button>
</div>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
beforeMount() {
// 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: []
}
},
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 => {
alert("user registered successfully");
this.$router.push('/ordersuccess');
return response;
})
}
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/Cart.scss";
</style>
router.js
{
path:'/dashboard',
component:Dashboard,
children:[{
path:'/displaybooks',
component:DisplayBooks
},
{
path:'/sortLowtoHigh',
component:sortBooksLowtoHigh
},
{
path:'/sortHightoLow',
component:sortBooksHightoLow
},
{
path:'/cart',
component:Cart
},
{
path:'/ordersuccess',
component:OrderPlace
},
]
}
You can use the history mode on your router settings. This should fix the issue
const routes = {
path: 'Dashboard',
....
}
new VueRouter({routes, mode: 'history'});
this.$router.push({ path: '/ordersuccess' })
https://router.vuejs.org/guide/essentials/navigation.html

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

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>