I have a form in vue.js with dynamic rows and I am using a classical HTML form tag to get the results in a php page.
My output in Php does not show the $post as expected.
A jsfiddle with the form is available;
my code is as follow:
html
<form method="post" action="action_page.php">
<div id="app">
<table class="table">
<thead>
<tr>
<td><strong>Date</strong></td>
<td><strong>Account</strong></td>
<td><strong>Debit</strong></td>
<td><strong>Credit</strong></td>
<td></td>
</tr>
</thead>
<tbody>
<tr v-for="row in rows">
<td>
<input class="form-control" type="date" v-model="row.myDate">
</td>
<td>
<v-select :options="['Account1','Account2','Account3']"></v-select>
</td>
<td>
<input class="form-control" type="text" v-model="row.debit" v-on:keypress="isNumber(event)">
</td>
<td>
<input class="form-control" type="text" v-model="row.credit" v-on:keypress="isNumber(event)">
</td>
<td ><i class="fa fa-trash-o fa-2x"style="color:brown;" #click="removeRow(row)"></i> <i class="fa fa-plus-circle fa-2x"style="color:#428bca" #click="addRow"></i> </td>
</tr>
</tbody>
<tfooter>
<td class="al-g"> <button class="button btn-primary" #click="addRow">Add Line</button></td>
<td> </td>
<td class="al-r">tot D.: {{ totaldebit | roundme }}</td>
<td class="al-r">tot Cr.:{{ totalcredit | roundme}}</td>
<td class="al-r">Dif: {{ totaldebit-totalcredit | roundme}}</td>
</tfooter>
</table>
</div>
<button class="button btn-danger" type="submit">Post</button>
</form>
</body>
and I have the following script:
<script>
Vue.filter('roundme', function (value) {
return value.toFixed(3);
})
Vue.component('v-select', VueSelect.VueSelect);
var app = new Vue({
el: "#app",
data: {
rows: [{debit:0, credit:0},
]
},
computed: {
totaldebit() {
return this.rows.reduce((total, row) => {
return total + Number(row.debit);
}, 0);
},
totalcredit() {
return this.rows.reduce((total, row) => {
return total + Number(row.credit);
}, 0);
}
},
methods: {
addRow: function() {
this.rows.push({myDate:"",
account:"",
debit: "",
credit: ""
});
},
removeRow: function(row) {
//console.log(row);
this.rows.$remove(row);
},
isNumber: function(evt) {
evt = (evt) ? evt : window.event;
var charCode = (evt.which) ? evt.which : evt.keyCode;
if ((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 46) {
evt.preventDefault();;
} else {
return true;
}
}
}
});
</script>
my php page action_page is as follow:
<?php
echo '$post detail';
foreach ($_POST as $key => $value)
echo "Field ".htmlspecialchars($key)." is ".htmlspecialchars($value)."<br>";
?>
Just add name attribute to inputs.
<input class="form-control" type="date" name="date" v-model="row.myDate">
or dynamical
<input class="form-control" type="date" :name="'date'+index" v-model="row.myDate">
Try this https://jsfiddle.net/jp1zw25a/ and check results in browser dev-tools
Related
I am very new to vue and I am trying to validate an array using vuelidate which is used to render a dynamic table. The problem is with the validation() method as I can comprehend.
According to vuelidate docs, https://vuelidate.js.org/#sub-collections-validation, the $each method supports array. When I use it, the validation never fails. However, when I omit $each, and, try to validate first index of the array, it returns as corrected - validation fails.
To be more precise, I am trying to validate each added row(s), and if there's a problem with the validation, it'd add a class to the affected row.
Component App.vue,
This is the HTML code:
<template>
<div>
<br>
<div class="text-center">
<button type="button" class="btn btn-outline-primary" #click="addRow()">Shto</button>
</div>
<br>
<p
v-for="error of v$.$errors"
:key="error.$uid"
>
<strong>{{ error.$validator }}</strong>
<small> on property</small>
<strong>{{ error.$property }}</strong>
<small> says:</small>
<strong>{{ error.$message }}</strong>
</p>
<form name="articles" #submit.prevent="submitForm">
<table class="table table-hover table-responsive">
<thead class="thead-inverse">
<tr>
<th>Nr.</th>
<th class="col-3">Numri</th>
<th class="col-4">Përshkrimi</th>
<th class="col-1">Sasia</th>
<th class="col-1">Çmimi</th>
<th class="col-2">Shuma</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(item, idx) in items_table" :key="item">
<td>
{{idx+1}}
</td>
<td>
<input v-model="item.part_no" name="part_no[]" class="form-control" type="text" />
</td>
<td>
<textarea v-model="item.part_name" name="part_name[]" class="form-control" type="text"></textarea>
</td>
<td>
<input v-model="item.part_qty" name="part_qty[]" class="form-control " type="number" step="0.01">
</td>
<td>
<input v-model="item.part_price" name="part_price[]" class="form-control" type="number" step="0.01">
</td>
<td>
<input :value="item.part_total" name="part_total[]" class="form-control text-center border-0" style="background-color: transparent; font-size: 18 px;" type="text" disabled>
</td>
<td>
<button type="button" #click="deleteRow(idx)" class="btn btn-danger btn-md btn-block">X</button>
</td>
</tr>
</tbody>
</table>
<div class="text-center">
<button type="submit" name="" id="" class="btn btn-primary btn-lg btn-block">Valido</button>
</div>
</form>
<div class="text-center">
<textarea name="" id="verbose_log" cols="70" rows="15" refs="logg"></textarea>
</div>
</div>
</template>
This is the content from script tag:
<script>
import useVuelidate from '#vuelidate/core'
import { required } from '#vuelidate/validators'
export default {
name: 'App',
setup: () => ({ v$: useVuelidate() }),
validation() {
return {
items_table: {
$each: {
part_no: {
required,
}
}
},
}
},
data() {
return {
items_table: [
{
part_no: '', part_name: '', part_qty: '', part_price: '', part_total: ''
}
],
items_subtotal: 0.00,
items_total: 0.00,
}
},
methods: {
deleteRow(index) {
if(this.items_table.length == 1) {
this.items_table.splice(index, 1);
this.items_subtotal = 0.00;
this.items_total = 0.00;
this.addRow();
} else if(this.items_table.length > 0) {
this.items_table.splice(index, 1);
}
},
addRow() {
this.items_table.push({part_no: '', part_name: '', part_qty: '', part_price: '', part_total: ''});
},
submitForm() {
this.v$.$touch();
//if (this.v$.$error) return;
console.log(this.v$);
document.getElementById('verbose_log').innerHTML = JSON.stringify(this.$data, null, 4);
}
},
computed: {
//
}
}
</script>
For the sake of clarity, I have excluded two methods which calculate line total and the total itself.
I'm trying to calculate a value from 2 input boxes and then get the total of those input boxes. I'm then trying to get the all my amounts and total them and add them to the subtotal but the issue I'm having is that when I type in a number in the first box my output is NaN instead of 0 and I would like for it to show me a 0 instead.
Here is my code
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Unit</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product['name'] }}</td>
<td>
<input type="text" class="form-control" v-model="unit[product['unit']]" #change="calculateCost(product['name'])">
</td>
<td>
<input type="text" class="form-control" v-model="price[product['price']]" #change="calculateCost(product['name'])">
</td>
<td>
{{ cost[product['name']] }}
</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>
Subtotal: {{ subTotal }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: [],
data(){
return {
products: [],
unit: {},
price: {},
cost: []
}
},
computed:{
subTotal(){
if(this.cost!== null)
{
if(Object.keys(this.cost).length !== 0){
return Object.keys(this.cost).reduce((carry, item) => {
carry+= Number(this.cost[item])
return carry;
}, Number(0))
}else{
return 0;
}
}
}
},
methods: {
getProducts(){
axios.get(`/api/product/all`).then(response => {
this.products = response.data.products;
});
},
calculateCost(item){
this.cost[item] = Number(this.unit[item]) * Number(this.price[item]);
},
},
mounted() {
this.getProducts();
}
}
</script>
Almost all type of inputs return a string. You can use
<input type="number" class="form-control" v-model="unit[product['unit']]" #change="calculateCost(product['name'])">
or
<input type="text" class="form-control" v-model.number="unit[product['unit']]" #change="calculateCost(product['name'])">
The problem is the v-model for unit and price are set to the different keys than the one given to calculateCost(), which causes the lookups to fail and results in NaN:
<input v-model="unit[product['unit']]" #change="calculateCost(product['name'])"> ❌
^^^^^^ ^^^^^^
<input v-model="price[product['price']]" #change="calculateCost(product['name'])"> ❌
^^^^^^^ ^^^^^^
<input v-model="unit[product['name']]" #change="calculateCost(product['name'])"> ✅
<input v-model="price[product['name']]" #change="calculateCost(product['name'])"> ✅
Setting the keys to product['name'] ensures the correct lookup for unit and price in calculateCost(). Since the user could enter invalid values (non-numeric strings) or omit a value, it's possible to get NaN, so it would be a good idea to add a NaN-check in calculateCost():
calculateCost(item) {
const unit = Number(this.unit[item]);
const price = Number(this.price[item]);
if (Number.isNaN(unit) || Number.isNaN(price)) return;
this.cost[item] = unit * price;
},
Also, you probably want the cost to react to user input, so switch from #change to #input:
<input v-model="unit[product['name']]" #input="calculateCost(product['name'])">
<input v-model="price[product['name']]" #input="calculateCost(product['name'])">
demo
I'm working on a web application using Vue and Firebase-Real-time Database. I managed to include all the CRUD functions following several tutorials in Youtube. The problem right now is that i am unable to show or hide a certain element when changing the {Edit:} condition of the user via v-if/v-else. Below will be the code;
<template>
<div class="row">
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>ID</th>
<th>Actions</th>
</tr>
</thead>
<tbody v-for="userProfile in Users" :key="userProfile.key">
<tr v-if="userProfile.edit">
<td>{{userProfile.username}}</td>
<td>{{userProfile.email}}</td>
<td>{{userProfile.userId}}</td>
<td>
<button #click.prevent="updateUser(userProfile.key)" class="btn btn-primary"> Update </button>
<button #click.prevent="removeUser(userProfile.key)" class="btn btn-danger">Delete</button>
</td>
</tr>
<tr v-else>
<td> <input type="text" v-model="userProfile.username"> </td>
<td> <input type="text" v-model="userProfile.email"> </td>
<td> <input type="text" v-model="userProfile.userId"> </td>
<td>
<button #click.prevent="saveUser(userProfile)" class="btn btn-primary"> Save </button>
<button #click.prevent="cancel(userProfile.key)" class="btn btn-danger"> Cancel </button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
As for my script
import { usersRef } from '../main.js';
export default {
data() {
return {
Users: [],
}
},
created() {
usersRef.once('value', (Users) => {
Users.forEach((doc) => {
this.Users.push({
key: doc.key,
email: doc.child('email').val(),
username: doc.child('username').val(),
userId: doc.child('userId').val(),
})
})
})
},
methods:{
removeUser: function(key) {
usersRef.child(key).remove()
.then(function() {
console.log("deleted Sucessfully");
alert("Sucessfully Deleted");
}).catch(function(error) {
console.log(error);
});
},
updateUser: function(key) {
usersRef.child(key).update({edit:false})
.then(function() {
console.log("update Sucessfully");
alert("Sucessfully Update");
}).catch(function(error) {
console.log(error);
});
},
cancel:function(key){
usersRef.child(key).update({edit:false})
},
saveUser:function(userProfile) {
const key = userProfile.key;
usersRef.child(key).set({username:userProfile.username,email:userProfile.email,userId:userProfile.userId,edit:true})
.then(function() {
console.log("Saved Sucessfully");
alert("Successfully Saved");
}).catch(function(error) {
console.log(error);
});
}
}
}
am i doing something wrong? thanks in advance!
I am learning Vue.
Now, I am trying to add data with the price and finally, it calculates total price:
Here is the HTML
<div id="app">
<form #submit.prevent="addItem">
<table border="1" cellpadding="10" width="300">
<tr>
<td colspan="2"><strong>Add New Item</strong></td>
</tr>
<tr>
<td>
<input type="text" name="" v-model="newItem" placeholder="Item Name">
</td>
<td>
<input type="number" name="" v-model="newItemPrice" placeholder="Item Price">
</td>
</tr>
</table>
</form>
<br>
<table border="1" cellpadding="10" width="400">
<tr>
<th>Item Name</th>
<th>Item Price</th>
</tr>
<tr v-for="(item, index) in items" :key="index">
<td>{{ item.name }}</td>
<td><input type="number" name="" v-model="item.price"></td>
<td><button #click="removeItem(index)">X</button></td>
</tr>
<tr>
<td>Total</td>
<td><strong>{{ total }}</strong></td>
</tr>
</table>
</div>
Here is the Vue Instance:
new Vue({
el : '#app',
data : {
items: [
{ name: 'Rice', price : 12.60 },
{ name: 'Oil', price : 22.00 },
{ name: 'Mango', price : 32.50 },
{ name: 'Orange', price : 42.00 },
],
newItem : '',
newItemPrice : '',
},
computed: {
total() {
var total = 0;
this.items.forEach( item => {
total += parseFloat( item.price );
})
return total;
}
},
methods: {
addItem() {
this.items.push({
name: this.newItem,
price: 0
});
},
removeItem( index ) {
this.items.splice( index, 1 )
}
}
});
You can see it's by default showing item name and price. I want to add new item using the v-model called newItem But It's not adding the new item to the table
BUT
If I remove the Item Price column I mean this line:
<td>
<input type="number" name="" v-model="newItemPrice" placeholder="Item Price">
</td>
then it's adding the new item perfectly :(
can you tell me what's wrong here?
See two issues with the fiddle:
There is no way to submit the form data
When pushing the price field was not added to the object
After fixing both of them it works well in this fiddle.
This happens because of browser implementation. As mentioned in W3C Specs:
When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.
But in case of multiple elements, the enter keypress does not trigger the form submit and thus you get this behaviour.
To resolve this you can simply use #keyup.enter.prevent="addItem" to listen to the enter keypress on each input and call the addItem() function like:
new Vue({
el: '#app',
data: {
items: [{name:"Rice",price:12.6},{name:"Oil",price:22},{name:"Mango",price:32.5},{name:"Orange",price:42}],
newItem: '',
newItemPrice: null,
},
computed: {
total() {
var total = 0;
this.items.forEach(item => {
total += parseFloat(item.price);
})
return total;
}
},
methods: {
addItem() {
this.items.push({
name: this.newItem,
price: 0
});
},
removeItem(index) {
this.items.splice(index, 1)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<div id="app">
<form #submit.prevent="addItem">
<table border="1" cellpadding="10" width="300">
<tr>
<td colspan="2"><strong>Add New Item</strong></td>
</tr>
<tr>
<td>
<input type="text" name="" v-model="newItem" placeholder="Item Name"
#keyup.enter.prevent="addItem">
</td>
<td>
<input type="number" name="" v-model="newItemPrice" placeholder="Item Price"
#keyup.enter.prevent="addItem">
</td>
</tr>
</table>
</form>
<br>
<table border="1" cellpadding="10" width="400">
<tr>
<th>Item Name</th>
<th>Item Price</th>
</tr>
<tr v-for="(item, index) in items" :key="index">
<td>{{ item.name }}</td>
<td><input type="number" name="" v-model="item.price"></td>
<td><button #click="removeItem(index)">X</button></td>
</tr>
<tr>
<td>Total</td>
<td><strong>{{ total }}</strong></td>
</tr>
</table>
</div>
You should put a new line in your form, my suggestion is to put just above the close form tag </form>:
<input type="submit" value="add">
Another fix to do is in your methods addItem()
addItem() {
this.items.push({
name: this.newItem,
price: this.newItemPrice
});
}
Where it is the number 0 you should provide the this.newItemPrice to it work properly.
I try to enable/disable each input from a loop. The problem is that my method it works just after refresh. After I modify something in code and save, then the input works.
<tr v-for="(item, index) in customer_names" :key="item.id">
<td>
<input :disabled="!item.disabled"
v-model="item.name"
type="text"
</td>
</tr>
<div class="edit_mode"
:class="{'display-none':!item.disabled}">
<i class="fa fa-save" #click="disableInput(index)" aria-hidden="true"></i>
</div>
<div class="edit_mode"
:class="{'display-none':item.disabled}">
<i class="fa fa-edit" #click="enableInput(index)" aria-hidden="true"></i>
</div>
props:['customer_names'],
data(){
return{
disabled: [],
}
}
enableInput(index){
console.log('enableInput',this.customer_names[index].disabled);
this.customer_names[index].disabled = false;
},
disableInput(index){
console.log('disabeInput',this.customer_names[index].disabled);
this.customer_names[index].disabled = true;
}
I didn't fully understand your problem. I deduced that you might want to enable or disable the text fields that you create from the data provided. If this is still not what you meant, correct your question by pasting more source code, and explain your problem in more detail.
Vue.component("custom", {
template: "#custom-template",
props: ["customer_names"],
methods: {
enableInput(item) {
item.disabled = false;
},
disableInput(item) {
item.disabled = true;
},
toggleInput(item) {
item.disabled = !item.disabled;
}
}
});
new Vue({
data() {
return {
items: [
{ name: "fus", disabled: false },
{ name: "ro", disabled: false },
{ name: "dah", disabled: true }
]
};
}
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<custom :customer_names="items" />
</div>
<script type="text/x-template" id="custom-template">
<table cellpadding=5>
<tr>
<th>Input</th>
<th>Version 1</th>
<th>Version 2</th>
</tr>
<tr v-for="item in customer_names" :key="item.id">
<td>
<input :disabled="item.disabled" v-model="item.name" type="text" />
</td>
<td>
<button #click="item.disabled = false">E</button>
<button #click="item.disabled = true">D</button>
<button #click="item.disabled = !item.disabled">T</button>
</td>
<td>
<button #click="enableInput(item)">E</button>
<button #click="disableInput(item)">D</button>
<button #click="toggleInput(item)">T</button>
</td>
</tr>
</table>
</script>