how to reuse a first template inside a second div - vue.js

Here I have written two steps one is "step===1" and other one is "step===2". So here i wants to reuse step1 template to step2. That means we have step one template and at step1 we click on next button so at next step i want to show the same template like reusing..
<div v-if="step === 1">
<h1>Step One</h1>
<h4>Address</h4>
<label>Address</label>
<input type="text" v-model="address">
<label>City</label>
<input type="text" v-model="city">
<label>State</label>
<input type="text" v-model="state">
<button #click.prevent="next()">Next</button>
</div>
<div v-if="step === 2">
reuse step===1 template here
</div>
vue.js
export default {
data() {
return {
address: null,
city: null,
state: null
}
},
methods:{
prev() {
this.step--;
},
next() {
this.step++;
},
}
</script>

new Vue({
el: '#app',
data() {
return {
stepTotal: 2,
step: 1,
address: null,
city: null,
state: null
}
},
methods: {
prev() {
this.step--;
},
next() {
this.step++;
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="index of stepTotal">
<div v-if="step === index" :key="index">
<h1>Step {{index}}</h1>
<h4>Address</h4>
<label>Address</label>
<input type="text" v-model="address">
<label>City</label>
<input type="text" v-model="city">
<label>State</label>
<input type="text" v-model="state">
<br>
<button v-if="index != 1" #click.prevent="prev()">Prev</button>
<button v-if="index != stepTotal" #click.prevent="next()">Next</button>
</div>
</template>
</div>

I'd probably separate the form for each step into its own component. So for instance:
AddressForm.vue
<template>
<div>
<label>Address</label>
<input type="text" v-model="localValue.address" />
<label>City</label>
<input type="text" v-model="localValue.city" />
<label>State</label>
<input type="text" v-model="localValue.state" />
</div>
</template>
<script>
export default {
props: {
value: Object,
},
computed: {
localValue: {
get() { return this.value; },
set(value) { this.$emit("input", value); },
},
},
};
</script>
And then just reuse this component for each step:
<div v-if="step === 1">
<h1>Step One</h1>
<h4>Address</h4>
<AddressForm v-model="step1address />
<button #click.prevent="next()">Next</button>
</div>
<div v-if="step === 2">
<h1>Step Two</h1>
<h4>Address 2</h4>
<AddressForm v-model="step2address />
<button #click.prevent="next()">Next</button>
</div>
Here's a running sample of the above:
https://codesandbox.io/s/tender-antonelli-2n4vb

Related

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property

I am trying make form validation dynamically, all props from the JSON file here I am passing the props parent to child component and that props using for input v-model.
When enter a letter inside the text input, I am getting the following error
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"
parent component
<template>
<!--parent component-->
<form action="" #submit.prevent="onSubmit">
<div v-for="formInput_Item in formInput_list" :key="formInput_Item.id">
<ShortText
v-if="formInput_Item.type === 'text'"
:input_id="formInput_Item.input_id"
:form-input-item="formInput_Item.formInput_Item"
:label_type="label_type"
:validations="formInput_Item.validations"
:is-valid="isValid"
:value="value"
/>
<p v-if="!formIsValid" class="text-danger">
Please fix the above errors and submit again.
</p>
<div class="col-12 mt-4">
<button class="btn btn-primary float-end" type="submit">SUBMIT</button>
</div>
</div>
</form>
</template>
<script>
import ShortText from '#/components/FormInputs/ShortText'
export default {
components: {
ShortText,
},
data() {
return {
label_type: 'floating', // floating, overhead, below, left, right
formIsValid: true,
error: this.formInput_list[3].validations.required,
value: '',
isValid: true,
}
},
methods: {
validateForm() {
this.formIsValid = true
if (this.value === '') {
this.isValid = false
this.formIsValid = false
}
},
onSubmit() {
console.log(this.isValid)
console.log('value' + this.value)
this.validateForm()
alert('form')
console.log(this.validateForm())
if (!this.formIsValid) {
alert('error')
return
}
alert('succses')
},
},
}
</script>
Child component
<template>
<div>
<div v-if="label_type === 'floating'">
<div class="form-outline mt-4">
<!-- ESlint is complaining about the mutation on this line 👇 -->
<input v-model="value" type="text" class="form-control" />
<label for="" class="form-label" style="margin-left: 10px">
{{ formInput_Item }}
</label>
<div class="form-notch">
<div class="form-notch-leading" style="width: 15px"></div>
<div class="form-notch-middle" style="width: 100px"></div>
<div class="form-notch-trailing"></div>
</div>
</div>
<p v-if="!isValid" class="text-danger">
This field is must not be empty.
</p>
</div>
<div v-else-if="label_type === 'overhead'" class="form-group">
<label class="form-label" for="timeType">{{ formInput_Item }}</label>
<input id="timeType" type="text" class="form-control" required />
</div>
<div v-else-if="label_type === 'below'" class="form-group">
<input type="text" class="form-control" required />
<label class="form-label">{{ formInput_Item }}</label>
</div>
<div v-else-if="label_type === 'left'" class="row form-group">
<div class="col-lg-4">
<label class="form-label">{{ formInput_Item }}</label>
</div>
<div class="col-lg-7">
<input type="text" class="form-control" required />
</div>
</div>
<div v-else-if="label_type === 'right'" class="row form-group">
<div class="col-lg-7">
<input type="text" class="form-control" required />
</div>
<div class="col-lg-4">
<label class="form-label">{{ formInput_Item }}</label>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
input_id: {
type: Number,
required: true,
},
formInput_Item: {
type: String,
required: true,
},
label_type: {
type: String,
required: true,
},
validations: {
type: Object,
},
value: {
type: String,
default: '',
},
isValid: {
type: Boolean,
},
},
data() {
return {
error: this.validations.required,
}
},
}
</script>

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

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

How to prevent a form to navigate to next step. while input fields is empty. Using vue.js

Here I am trying to prevent a form to navigate to a next step while the input field is empty.I have written a method for that in vue But I am not able to achieve that as I am new in vue.js. So I am trying to achieve but i was failed. So if anyone Have an idea please help me.I want to make a border highlight when fields are empty and user clicks next then field border should highlight.
<div id="vk_app">
<form>
<div v-if="step === 1">
<h1>Step One</h1>
<p>
<legend for="name">Your Name:</legend>
<input id="name" name="name" v-model="category">
<input id="name" name="name" v-model="password">
<input id="name" name="name" v-model="Email">
</p>
<button #click.prevent="next()">Next</button>
</div>
<div v-if="step === 2">
<template>
<input id="name" name="name" v-model="address">
<input id="name" name="name" v-model="h_no">
<input id="name" name="name" v-model="mobile">
<button #click.prevent="prev()">Previous</button>
<button #click.prevent="next()">Next</button>
</template>
</div>
<div v-if="step === 3">
<template>
<input id="name" name="name" v-model="subject">
<input id="name" name="name" v-model="occupation">
<button #click.prevent="prev()">Previous</button>
<button #click.prevent="next()">Next</button>
</template>
<button #click.prevent="prev()">Previous</button>
<button #click.prevent="submit()">Save</button>
</div>
</form>
</div>
vue.js
<script>
const app = new Vue({
el:'#vk_app',
data() {
return {
step:1,
category:null,
street:null,
city:null,
state:null,
}
},
methods:{
prev() {
this.step--;
},
next() {
this.step++;
},
checkForm: function (e) {
if (this.name && this.age) {
return true;
}
this.errors = [];
if (!this.name) {
this.errors.push('Name required.');
}
if (!this.age) {
this.errors.push('Age required.');
}
e.preventDefault();
}
}
});
</script>
You have a method checkForm but it seems you never call it.
Just change your next and prev methods to use that method as follows:
prev() {
if(this.checkForm()) {
this.step--;
}
},
next() {
if(this.checkForm()) {
this.step++;
}
},

VueJS - how can I add a hidden input to a form?

I created a Vue component that has several forms that can be submitted by the user. On submit, the form will send some data to my backend. The problem is that my backend needs to know which form was submitted, and this input doesn't depend on the user, so I added a hidden input so that when the user send the data, my backend can also know which type of form was submitted. I tried the following:
<template>
<div>
<div v-if="order=='total'">
<form #submit="formSubmit">
<input type="hidden" v-model="order_type" value="form1">
<input type="text" class="form-control" v-model="amount">
<br>
<input type="text" class="form-control" v-model="price">
<br>
<button class="btn btn-primary">BUY</button>
</form>
</div>
<div v-else-if="order=='partial'">
<input type="hidden" v-model="order_type" value="form2">
<input type="text" class="form-control" v-model="amount">
<br>
<input type="text" class="form-control" v-model="price">
<br>
<button class="btn btn-primary">BUY</button>
</form>
</div>
<br>
</div>
</template>
<script>
import axios from 'axios'
export default {
props:{
order:{
type:String,
default:'total'
},
},
mounted() {
console.log('Component mounted.')
},
data() {
return {
name: '',
description: '',
output: ''
};
},
methods: {
formSubmit(e) {
e.preventDefault();
let currentObj = this;
axios.post('MYURL', {
order_type: this.order_type,
price: this.price,
amount: this.amount
})
.then(function (response) {
currentObj.output = response.data;
})
.catch(function (error) {
currentObj.output = error;
});
}
}
}
</script>
The problem with this code is that order_type doesn't get sent with the form. I have read somewhere that Vue doesn't allow hidden values, so is there any other way to do this?
You can pass a param to the method:
#submit.prevent="formSubmit("form1")"
And in the method:
formSubmit(value) {
let currentObj = this;
axios
.post("MYURL", {
price: this.price,
amount: this.amount,
order_type: value
})
.then(function(response) {
currentObj.output = response.data;
})
.catch(function(error) {
currentObj.output = error;
});
},
Your template becomes:
<template>
<div>
<div v-if="order=='total'">
<form #submit.prevent="formSubmit("form1")">
<input type="text" class="form-control" v-model="amount">
<br>
<input type="text" class="form-control" v-model="price">
<br>
<button class="btn btn-primary" type="submit">BUY</button>
</form>
</div>
<div v-else-if="order=='partial'">
<form #submit.prevent="formSubmit("form2")">
<input type="text" class="form-control" v-model="amount">
<br>
<input type="text" class="form-control" v-model="price">
<br>
<button class="btn btn-primary" type="submit">BUY</button>
</form>
</div>
<br>
</div>
</template>
SOLUTION 2
To avoid duplication you could also set a computed property:
orderType(){
if (this.order === "total") {
return "form1"
} else if (this.order === "partial") {
return "form2"
} else {
return null
}
With that in place you can simplify the template by removing the conditional and not needing to pass anything to the method:
<template>
<div>
<form #submit.prevent="formSubmit()">
<input type="text" class="form-control" v-model="amount" />
<br />
<input type="text" class="form-control" v-model="price" />
<br />
<button class="btn btn-primary" type="submit">BUY</button>
</form>
</div>
</template>
And in the method the value of order_type is calculated in the computed property:
formSubmit() {
let currentObj = this;
axios
.post("MYURL", {
price: this.price,
amount: this.amount,
order_type: this.orderType
})
.then(function(response) {
currentObj.output = response.data;
})
.catch(function(error) {
currentObj.output = error;
});
},