I'm new to coding with a very basic understanding to Javascript. I created a table that will push an update to my tables upon clicking a button. However, i do want to limit the number of rows within my table. say for example upon pushing the sixth data to the table, i do want the first data to be removed from the row.
I tried searching everywhere with no luck. Maybe it's because my basic understand to javascript is pretty basic. Im a newbie haha. I am using vue.js for this code.
HTML
<form class="form-inline">
<div class="form-group mx-sm-3 mb-2">
<input
type="number"
v-model="newElement.buy"
class="form-control">
</div>
<button
v-on:click="buyStock"
type="button"
class="btn btn-success mb-2">BUY
</button>
</form>
<section class="stock_tables">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">{{codeBuy}}</th>
<th><a v-html="negative"></a> Buy</th>
</tr>
</thead>
<tbody>
<tr v-for="u in stocks">
<th></th>
<td>{{u.buy}}</td>
</tr>
</tbody>
</table>
</section>
Script
<script>
new Vue({
el: '#app',
data: {
codeBuy: "12345",
stocks: [],
newElement: {buy:"",sell:""}
},
methods: {
buyStock: function(){
this.stocks.push({buy:this.newElement.buy});
this.newElement = {buy:""};
}
}
});
</script>
So basically everytime i enter the amount of the stocks and press buy it will update.
You have some options to modify the buyStock method:
this.stocks.shift()
this.$delete(this.stocks, 0)
this.stocks = this.stocks.slice(1);
this.stocks = this.stocks.slice(-5);
this.stocks = this.stocks.splice(0, 1);
or, without the if use:
this.stocks = [...this.stocks, {
buy: this.newElement.buy
}].slice(-5);
Demo below:
new Vue({
el: '#app',
data: {
codeBuy: "12345",
stocks: [],
newElement: {
buy: "",
sell: ""
},
negative: ''
},
methods: {
buyStock: function() {
this.stocks.push({
buy: this.newElement.buy
});
if (this.stocks.length > 5) {
// this.$delete(this.stocks, 0);
// this.stocks = this.stocks.slice(1);
// this.stocks = this.stocks.slice(-5);
// this.stocks = this.stocks.splice(0, 1);
this.stocks.shift();
}
//this.stocks = [...this.stocks, {
// buy: this.newElement.buy
//}].slice(-5);
this.newElement = {
buy: ""
};
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="app">
<form class="form-inline">
<div class="form-group mx-sm-3 mb-2">
<input type="number" v-model="newElement.buy" class="form-control">
</div>
<button v-on:click="buyStock" type="button" class="btn btn-success mb-2">BUY</button>
</form>
<section class="stock_tables">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">{{codeBuy}}</th>
<th><a v-html="negative"></a> Buy</th>
</tr>
</thead>
<tbody>
<tr v-for="u in stocks">
<th></th>
<td>{{u.buy}}</td>
</tr>
</tbody>
</table>
</section>
</div>
Related
I'm trying to create a form that will update a product's pricing when adding the unit amount of the product and
a price of the product, but when I type into one of my textboxes I end up getting this error
The specified value "unit_product_1" cannot be parsed, or is out of range
I don't know what that means or how to solve it.
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="product['unit']">
</td>
<td>
<input type="text" class="form-control" v-model="product['price']">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: [],
data(){
return {
products: [],
unit_product_1: null,
price_product_1: null,
}
},
computed:{
},
methods: {
getProducts(){
axios.get(`/api/product/all`).then(response => {
this.products = response.data.products;
});
}
},
mounted() {
this. getProducts();
}
}
</script>
unit_product_1: null,
price_product_1: null
You don't use them anywhere.
If you want to mutate your incoming products, you should make a new POST request and send the data back to the server. In this scenario you must know how the server waits to receive the data and send them back as requested.
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 have a couple of vue pages, one page is a list of users, the second is a list of permissions and the third is a vue component
that displays the list of users and another vue component that shows the details of a user.
What I'm trying to do is, if I'm on the permissions page and I would like to go to a specific user's details I would like to redirect
with that panel open.
I am able to redirect the the users page by doing window.location.href = '/admin/users'; but I'm not sure how I'm going to get
the user panel open.
I thought that if I could do $emit then that could pass the user object over to the users main page, but that won't work if I do
a window.location.href = '/admin/users';
Here is my code. This has 2 components displays a list of users and their details
<template>
<div class="row">
<div class="col-md-6">
<users :emit="emit" :users="users"></users>
</div>
<div class="col-md-6">
<detail v-if="userDetails" :emit="emit" :user="manageUser"></detail>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
userDetails: false,
manageUser: null
}
},
mounted() {
this.emit.$on('user', payload => {
this.manageUser = payload.user;
this.userDetails = true;
});
}
}
</script>
This is my users lists
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title text-success" style="cursor: pointer;" #click="createUser"> New User</h3>
</div>
<div class="card-body">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Name</th>
<th style="width:120px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{user.name}}</td>
<td style="text-align: right;">
<button class="btn btn-sm btn-outline-info" #click="detail(user)">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
}
},
methods: {
detail(user) {
this.emit.$emit('user', { user: user });
}
}
}
</script>
and this is my permissions pages
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{permission.name}}</strong></h3>
</div>
<div class="card-body p-0">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>User Name</th>
<th style="width:120px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{user.name}}</td>
<td style="text-align: right;">
<button class="btn btn-sm btn-outline-info" #click="detail(user)">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit'],
data() {
return {
users: []
}
},
mounted() {
this.allUsers();
},
methods: {
allUsers() {
this.loading = true;
axios.get('/users').then(response => {
this.users = response.data.users;
});
},
detail(user) {
window.location.href = '/admin/users';
}
}
}
</script>
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 want to pass a piece of HTML to a table-data-element using VueJS. The following demonstrates my scenario:
<template>
<div>
<div v-if="someObject.properties" style="margin-top: 20px;" class="table-responsive-md">
<table class="table table-striped">
<thead>
<tr>
<th style="text-align: left" scope="col">Some icons</th>
</tr>
</thead>
<tbody v-for="(property, index) in someObject.properties" :key="index">
<tr>
<td style="text-align: center" v-html="getIconWhenSomeRequirementIsMet(property)"/>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script lang="ts">
...
getIconWhenSomeRequirementIsMet (property: any): string {
if (property.type === 'animal') return '<b-icon-check class="h3 mb-0" style="color:green;"/>'
if (property.type === 'human') return '<b-icon-check class="h3 mb-0" style="color:yellow;"/>'
return '<b-icon-x class="h3 mb-0" style="color:red;"/>'
}
</script>
The code above is a minimal example of my Vue single file component. However, this way, I get empty fields in my table instead of the actual icons. Isn't there a simple and clean approach to achieve this?
The reason it doesn't work is because you can't use v-html to render custom components.
Instead, here's two different ways you can do this.
The first is to pre-define your b-icon-* and use v-if, v-else-if and v-else to match which icon to show.
The second is to dynamically bind properties using v-bind, this way you can use a method to do it, like you are now, but instead return the properties based on the type.
new Vue({
el: "#app",
data() {
return {
items: [
{ type: "animal" },
{ type: "human" },
{ type: "alien" },
],
fields: ['Type', 'Icon 1', 'Icon 2']
}
},
methods: {
getIconWhenSomeRequirementIsMet (type) {
/* Default properties */
const properties = {
icon: 'x',
style: 'color: red',
class: 'h3 mb-0'
};
if (type === 'animal') {
properties.icon = 'check';
properties.style = 'color: green;';
}
else if (type === 'human') {
properties.icon = 'check';
properties.style = 'color: yellow;';
}
return properties;
}
}
})
<link href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="//unpkg.com/bootstrap-vue#2.7.0/dist/bootstrap-vue.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="app">
<div class="table-responsive-md">
<table class="table table-striped">
<thead>
<tr>
<th v-for="field in fields" >{{ field }}</th>
</tr>
</thead>
<tbody>
<tr v-for="{ type } in items">
<td>
{{ type }}
</td>
<td>
<b-icon-check v-if="type === 'animal'" variant="success" class="h3 mb-0">
</b-icon-check>
<b-icon-check v-else-if="type === 'human'" variant="warning" class="h3 mb-0">
</b-icon-check>
<b-icon-x v-else variant="danger" class="h3 mb-0">
</b-icon-x>
</td>
<td>
<b-icon v-bind="getIconWhenSomeRequirementIsMet(type)"></b-icon>
</td>
</tr>
</tbody>
</table>
</div>
</div>