Vue JS passing variable names and using them - vue.js

I am trying to build a fairly simple program but maybe I am not really understanding how vue does things.
I need to add inventory to the top and than use it in any of the two options. So when I press + for clearance it removes from Inventory and when I press - it add to inventory until 0.
This works if I create only one with static variables, but since there will be more location I am trying to pass this ID of the location via on-click. I used words but there will be ID in the end. The issue is that I cant get them to work once I pass them. When I console log them I can see them but they still dont work. What am I missing here?
<tr>
<td>New</td>
<td>
<div class="row">
<div class="col">
<h1>#{{ inventory }}</h1>
</div>
<div class="col">
<button type="button" class="btn btn-danger" v-on:click.prevent="addInventory($event, 'inventory')">+</button>
</div>
</div>
</tr>
<tr>
<td>Floor Model</td>
<td>
<div class="row">
<div class="col">
<button type="button" class="btn btn-success" v-on:click.prevent="removeInventory($event, 'floor')">-</button>
</div>
<div class="col">
<h1>#{{ floor }}</h1>
</div>
<div class="col">
<button type="button" class="btn btn-danger" v-on:click.prevent="addInventory($event, 'floor')">+</button>
</div>
</div>
</td>
</tr>
<tr>
<td>Clearance</td>
<td>
<div class="row">
<div class="col">
<button type="button" class="btn btn-success" v-on:click.prevent="removeInventory($event, 'clearance')">-</button>
</div>
<div class="col">
<h1>#{{ clearance }}</h1>
</div>
<div class="col">
<button type="button" class="btn btn-danger" v-on:click.prevent="addInventory($event, 'clearance')">+</button>
</div>
</div>
</td>
</tr>
</tbody>
The Vue Code
<script>
var app = new Vue({
el: '#app',
data: {
inventory: 4,
clearance: 1,
floor: 0,
},
methods: {
addInventory: function (event, id){
if(this.inventory > 0){
this.id++ ;
this.inventory-- ;
console.log(id);
}
},
removeInventory: function (event, id){
if(this.id > 0){
this.id-- ;
this.inventory++ ;
console.log(id);
}
},
}
})
</script>
Thank you

Data must be returned as an object to avoid all components referring to the same data property.
data: () => ({
//properties here
})
or
data: () => {
return {
//properties here
}
}
For setting those values dynamically and avoiding a bunch of methods, look at this example:
https://codepen.io/arcaster42/pen/PooVYVE

Your data field should be a function that returns an object. This solves the problem of all copies of this component accessing the same data:
data() {
return {
inventory: 4,
clearance: 1,
floor: 0,
}
}
Edit: forgot return statement

First there is no point trying to use a single method, You can create multiple which handle it. If you want to use a single method you could but you would be using keys to find which one was selected.
You can also disable the button when a value is at 0, so you wont have to do any if statements inside of that method.
I'm not entirely sure if below is what you are after but it will give you a heads up.
<tbody>
<tr>
<td>New</td>
<td>
<div class="row">
<div class="col">
<h1>#{{ inventory }}</h1>
</div>
<div class="col">
<button
type="button"
class="btn btn-danger"
v-on:click.prevent="addInventory"
>
+
</button>
</div>
</div>
</td>
</tr>
<tr>
<td>Floor Model</td>
<td>
<div class="row">
<div class="col">
<button
type="button"
class="btn btn-success"
:disabled="floor <= 0"
v-on:click.prevent="removeFloorStock"
>
-
</button>
</div>
<div class="col">
<h1>#{{ floor }}</h1>
</div>
<div class="col">
<button
type="button"
class="btn btn-danger"
v-on:click.prevent="addFloorStock"
>
+
</button>
</div>
</div>
</td>
</tr>
<tr>
<td>Clearance</td>
<td>
<div class="row">
<div class="col">
<button
type="button"
class="btn btn-success"
:disabled="clearance <= 0"
v-on:click.prevent="removeClearanceStock"
>
-
</button>
</div>
<div class="col">
<h1>#{{ clearance }}</h1>
</div>
<div class="col">
<button
type="button"
class="btn btn-danger"
v-on:click.prevent="addClearanceStock"
>
+
</button>
</div>
</div>
</td>
</tr>
</tbody>
<script>
var app = new Vue({
el: '#app',
data: {
inventory: 4,
clearance: 1,
floor: 0,
},
methods: {
addFlorStock() {
this.floor = this.floor++;
},
removeFloorStock() {
this.floor = this.floor--;
},
addClearanceStock() {
this.clearance = this.clearance++;
},
removeClearanceStock() {
this.clearance = this.clearance--;
},
addInventory() {
this.inventory = this.inventory++;
},
removeInventory() {
this.inventory = this.inventory--;
},
}
})
</script>

Ok here is the answer I was looking for if anybody else needs it.
In short I wanted the code to work for any number of items in the array.
InventoryTotal is an array like this.
[
{
"id": 16,
"product_id": 504,
"location_id": 1,
"stock": 22,
"status_id": 1,
"created_at": "2019-12-02 00:00:00",
"updated_at": "2019-12-02 00:00:00",
"status": {
"id": 1,
"name": "New Stock"
}
},
{
"id": 17,
"product_id": 504,
"location_id": 2,
"stock": 3,
"status_id": 2,
"created_at": "2019-12-02 00:00:00",
"updated_at": "2019-12-02 00:00:00",
"status": {
"id": 2,
"name": "Clearance"
}
}
]
<td v-text="location.status.name"></td>
<td>
<div class="row">
<div class="col">
<button type="button" v-if="location.status.name != 'New Stock'" class="btn btn-warning" v-on:click.prevent="removeInventory(location.status.name , index , location.stock)">-</button>
</div>
<div class="col">
<h1 v-text="location.stock"></h1>
</div>
<div class="col">
<button type="button" v-if="location.status.name != 'New Stock'" class="btn btn-danger" v-on:click.prevent="addInventory(location.status.name , index , location.stock)">+</button>
</div>
</div>
</tr>
Script
var vm = new Vue({
el: '#app',
data() {
return {
inventoryTotal: '',
}
},
methods: {
addInventory: function (name ,index, item){
if(0 == index){
this.inventoryTotal[0].stock++ ;
}else if(this.inventoryTotal[0].stock > 0){
this.inventoryTotal[0].stock-- ;
this.inventoryTotal[index].stock++ ;
}
},
removeInventory: function (name, index, item){
if(0 == index && this.inventoryTotal[0].stock > 0){
this.inventoryTotal[0].stock-- ;
}else if(this.inventoryTotal[index].stock != 0){
this.inventoryTotal[index].stock-- ;
this.inventoryTotal[0].stock++ ;
}
},
}
})

Related

Vue.js 2 components data property bound to the first or to one only instance of component

Need help. I'm fairly new to Vue.js and need some help and advice.
Context:
I have a component with BS modal inside which is rendered in a for loop and obviously has many instances.
Issue:
The very first rendered component has its data received from parent via props, and the rest component have their own (like row.id and etc.)
Question: How to fix it? Maybe the problem in BS modal?
Component:
let vSelect = Vue.component("v-select", VueSelect.VueSelect);
var scoringButton = Vue.component("scoring-button", {
props: ["loadId", "causeData"],
template: "#scoring-template",
components: {
"v-select": vSelect,
},
data: function () {
return {
scoredLoadId: this.loadId,
scoring: null,
causeId: null,
selectedCause: null,
causeList: this.causeData,
};
},
computed: {
showCauseList() {
if (this.scoring === "1" || this.scoring === null) {
return true;
}
return false;
},
},
});
Template:
<template v-cloak id="scoring-template">
<div class="scoring-block" :id="scoredLoadId">
<div class="scoring">
<button
v-if="scoring === '1'"
title="Scoring button"
type="button"
class="btn btn-success scoring-btn"
data-bs-toggle="modal"
data-bs-target="#scoringModal"
>
<i class="bi bi-hand-thumbs-up-fill"></i>
</button>
<button
v-else-if="scoring === '2'"
title="Scoring button"
type="button"
class="btn btn-danger scoring-btn"
data-bs-toggle="modal"
data-bs-target="#scoringModal"
>
<i class="bi bi-hand-thumbs-down-fill"></i>
</button>
<button
v-else
title="Scoring button"
type="button"
class="btn btn-info scoring-btn"
data-bs-toggle="modal"
data-bs-target="#scoringModal"
>
<i class="bi bi-hand-thumbs-up-fill"></i>
<i class="bi bi-hand-thumbs-down-fill"></i>
</button>
</div>
<div
class="modal fade scoring-modal"
id="scoringModal"
tabindex="-1"
role="dialog"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" :key="loadId">
<div class="modal-header">
<h1 class="modal-title">Load Scoring</h1>
<button
type="button"
class="btn-close"
aria-label="Close"
data-bs-dismiss="modal"
></button>
</div>
<div class="modal-body">
<div class="scoring-content">
<input
type="radio"
class="btn-check"
name="btnScore"
id="btnLike"
autocomplete="off"
checked="scoring === '1'"
value="1"
v-model="scoring"
/>
<label
class="btn btn-outline-success"
for="btnLike"
#click="clearSelectedCause"
>
<i class="bi bi-hand-thumbs-up-fill"></i> Like
</label>
<input
type="radio"
class="btn-check"
name="btnScore"
id="btnDislike"
autocomplete="off"
:checked="scoring === '2'"
value="2"
v-model="scoring"
/>
<label class="btn btn-outline-danger" for="btnDislike">
<i class="bi bi-hand-thumbs-down-fill"></i> Dislike
</label>
</div>
<div class="scoring-cause">
<v-select
:disabled="showCauseList"
label="name"
:options="causeList"
v-model="selectedCause"
placeholder="Choose a cause option"
></v-select>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">
Save changes
</button>
</div>
</div>
</div>
</div>
</div>
</template>
Parent:
var mainTableApp = new Vue({
el: "#main-table",
data: {
tableData: [],
scoringCauseList: [
{
"id": 6,
"scoring_type": 0,
"name": "Late at loading",
"mark": "late_at_loading"
},
{
"id": 7,
"scoring_type": 0,
"name": "Special conditions were not respected",
"mark": "special_conditions_were_not_respected"
},
{
"id": 8,
"scoring_type": 0,
"name": "Bad/not enough information",
"mark": "bad_not_enough_information"
}
],
},
components:{
'scoring-button': scoringButton,
}
});
Component in the main app block:
<div
v-for="row in tableData"
class="row-container"
>
....
<scoring-button
:id="row.LOAD_ID"
:key="row.LOAD_ID"
:load-id="row.LOAD_ID"
:cause-data="scoringCauseList"
>
</scoring-button>
....
</div>
I tried to resetting BS modal's data, but it didn't work. So, I went back to look for a solution in Vue part.
I know I may construct the whole thing not enough in a very right way, but this code below is the last version after many other solutions, have been tried with v-model, $emit, props etc.
Update: found solution.
Added ":id" for all "input" fields I had in my component.
So, to have reusable components their own data properties you need to have dynamic ":id" properties. So, that each data flows into their own component.
<input
type="radio"
class="btn-check"
name="btnScore"
id="btnLike" // <-- old line
:id="`btnLike-${dynamicStr}`" // <-- new modified line
autocomplete="off"
checked="scoring === '1'"
value="1"
v-model="scoring"
/>
<label
class="btn btn-outline-success"
for="btnLike" // <-- old line
:for="'btnLike-${dynamicStr}'" // <-- new modified line
#click="clearSelectedCause"
>
<i class="bi bi-hand-thumbs-up-fill"></i> Like
</label>

vuejs invoice transactions, input item push

I am trying to make invoice transactions with vue js. my question is; The user may want to write a description for 1 product or may want to apply a discount. (BY REQUEST) I want the specified input to be shown whichever item he wants to add. (EVERY LINE CAN HAVE ONLY 1 EXPLANATION, DISCOUNT)
Therefore
on demand
When you press the "DESCRIPTION, DISCOUNT AND DISCOUNT RATE" buttons, the input of the relevant line will be pushed."
Thank you in advance for your help.
jsfiddle
const app = new Vue({
el: "#app",
data: {
invoiceItems: [
{
name: "",
quantity: 0,
unit_price: 0,
vat_rate: 18,
net_total: 0,
description: '',
discount_value: 0,
discount_rate:'usd'
},
],
},
methods: {
addInvoice() {
this.invoiceItems.push({
name: "",
quantity: 0,
unit_price: 0,
vat_rate: 18,
net_total: 0,
description: '',
discount_value: 0,
discount_rate:'usd'
});
},
removeIncoiceItem(index) {
this.invoiceItems.splice(index, 1);
},
},
});
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<section class="container">
<div class="row">
<table class="table">
<thead class="thead-dark">
<tr>
<th style="width:17%">Name</th>
<th style="width:14%">Unit Price</th>
<th style="width:15%">Vat Rate</th>
<th style="width:20%">Action</th>
</tr>
</thead>
</table>
<div v-for="(item, index) in invoiceItems" :key="index" style="margin-bottom: 10px">
<div class="row">
<div class="col-md-2">
<input type="text" v-model="item.name">
</div>
<div class="col-md-2">
<input type="text" v-model="item.unit_price">
</div>
<div class="col-md-2">
<input type="text" v-model="item.net_total">
</div>
<div class="col-md-5">
<button class="btn btn-primary btn-sm">Add Description</button>
<button class="btn btn-secondary btn-sm">Add Discount</button>
<button class="btn btn-warning btn-sm">Add discount rate</button>
<button class="btn btn-danger btn-sm" #click="removeIncoiceItem(index)">X</button>
</div>
<div class="row" style="margin-top:20px;">
<div class="col-md-2">
<input type="text" placeholder="description">
</div>
<div class="col-md-2">
<button class="btn btn-danger btn-sm">Delete Desc.</button>
</div>
<div class="col-md-3">
<div class="input-group">
<input type="text" placeholder="discount_value">
<select class="form-select-new">
<option value="dollar">USD</option>
<option value="percent">&</option>
</select>
</div>
</div>
<div class="col-md-1">
<button class="btn btn-danger btn-sm">Delete Disc.</button>
</div>
<div class="col-md-2">
<input type="text" placeholder="discount rate">
</div>
<div class="col-md-2">
<button class="btn btn-danger btn-sm">Delete discount rate</button>
</div>
</div>
</div>
<hr>
</div>
<hr>
<div style="margin-top:10px">
<button class="btn btn-warning" #click="addInvoice()"> Add Item</button>
</div>
</div>
</section>
</div>
To show the input only when you press a button you should use v-if and check if the key exist on the item.
I will show you an example for description but you can apply it to all the inputs you want.
So when you add new item, add it without description like so:
methods: {
addInvoice() {
this.invoiceItems.push({
name: "",
quantity: 0,
unit_price: 0,
vat_rate: 18,
net_total: 0,
});
},
},
And check if item.description exists on the input of description:
<div class="col-md-2">
<input type="text" v-if="item.description !== undefined" v-model="item.description" placeholder="description"> </div>
...
<button class="btn btn-primary btn-sm" #click="addDesc(index)" >Add Description</button>
...
<div class="col-md-2">
<button class="btn btn-danger btn-sm" #click="deleteDesc(index)" >Delete Desc.</button>
</div>
The addDesc method will add the key to the item and set it as empty:
addDesc(index){
Vue.set(this.invoiceItems[index], "description", "");
}
The deleteDesc method will remove the key entirely from the item:
deleteDesc(index){
Vue.delete(this.invoiceItems[index], "description");
}
Now when you click on add description button - the description input will appear, and when you click delete description button - the description input will disappear.

Id of item do not pass to the bootstarap modal in vue

I have this code that does not pass the id of the item of array to the bootstrap modal
I need help with this because it takes too much time.
<script>
export default {
data() {
return {
lists: [
{id: 1, name: "Name 1"},
{id: 2, name: "Name 2"},
{id: 3, name: "Name 3"},
{id: 4, name: "Name 4"}
],
delete_id: null
}
},
methods: {
passId(id){
this.delete_id = id;
},
postDelete() {
// this part return null
console.log(this.delete_id)
}
}
}
</script>
<template>
<div>
<!-- delete card item -->
<div class="modal fade" id="delete_todo_item" tabindex="-1" user="dialog" aria-labelledby="exampleModalCenterTitle"aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" user="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" >delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div style="margin: 0 auto;" class="text-center">
<h2 style="font-size: 15px;color: red;">sure_to_delete</h2>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">close
</button>
<button class="btn btn-primary" data-dismiss="modal" #click="postDelete()">delete
</button>
</div>
</div>
</div>
</div>
<div v-for="item in lists" :key="item.id">
{{item.name}}
<i class="la la-trash" href="#delete_todo_item" data-toggle="modal" #click="passId(item.id)"></i>
</div>
</div>
</template>
The issue is when I click on the delete button of the modal I return null in console.
My version of vue is 2.6.1.
I need to know if there is an issue. I really can't understand what is going wrong.

Local-Storage in Vue.JS

I am working on Vue.JS and I tried to use local-storage with it to save data. In my code, I can store and retrieve all data with local-storage except line-through effect. Here, I am trying to store actual boolean value of line-through effect in local-storage and want to retrieve that value on to-do list app.
<title>To Do List</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
<style>
.taskDone {
text-decoration: line-through;
}
</style>
</head>
<body>
<div id="todo-list" class="container">
<div class="container col-sm-8 col-sm-offset-2">
<h1 class="text-center"> <big><b> To Do List </b> </big></h1>
<h5 class="text-center"> <span v-show="itemsTodo.length"> ({{ itemsTodo.length }} pending) </span></h5>
<div class="col-md-8">
<button v-if="state === 'default'" class="btn btn-primary" #click="changeState('edit') ">Add Item</button>
<button v-else class="btn btn-info" #click="changeState('default')">Cancel</button>
</div>
<br>
<br>
<div v-if="state === 'edit'" >
<div class="col-sm-4">
<input class='form-control' v-model="newItem" type="text" placeholder="Type here" #keyup.enter="saveItem" >
</div>
<div class="col-sm-4">
<input class="form-control" v-model="newdate" type="date" type="text"/>
</div>
<div class="col-sm-4">
<button class='btn btn-primary btn-block' v-bind:disabled="newItem.length === 0"#click= "saveItem">Save Item</button>
</div>
</div>
<br>
<br>
<ul type="none" class="list-group">
<li class="list-group-item" v-for="(item,index,date) in items" :class="{taskDone : item.completed}" >
<h4>
<input type="checkbox" v-model="item.completed" #click="item.completed = !item.completed">
<button class="btn btn-primary " #click.stop="removeitems(index)">× </button>
<b><i> {{ item.label }} {{ item.date }} </i></b></h4>
</li>
</ul>
<h2 v-if="items.length === 0">Nice Job! Nothing in TO DO LIST</h2>
<div class="col-sm-4">
<button class="btn btn-warning btn-block" #click="clearcompleted"> Clear Completed</button>
</div>
<div class="col-sm-4">
<button class="btn btn-danger btn-block" #click="clearAll"> Clear All</button>
</div>
</div>
</div>
2. Vue.JS code
<script src="https://unpkg.com/vue" ></script>
<script>
var todolist = new Vue({
el: '#todo-list',
data : {
state : 'edit',
header: 'To Do List',
newItem: '',
newdate: '',
items: [
{
label:'coffee',
completed:false,
date:'2019-06-20' ,
},
{
label:'tea',
completed:false,
date:'2019-06-19' ,
},
{
label:'milk',
completed:false,
date:'2019-06-19' ,
},
]
},
computed:{
itemsDone(){
return this.items.filter(items => items.completed)
},
itemsTodo(){
return this.items.filter(items =>! items.completed)
},
},
methods:{
saveItem: function(){
if (this.newItem != ''){
this.items.push({
label:this.newItem,
completed: false,
date : this.newdate,
});
this.newItem = '';
this.newdate = '';
}},
changeState: function(newState){
this.state = newState;
this.newItem = '';
this.newdate = '';
},
removeitems(index){
this.items.splice(index,1);
},
clearcompleted (){
this.items = this.itemsTodo;
},
clearAll: function(){
this.items = [ ];
},
},
mounted(){
console.log('App Mounted!');
if (localStorage.getItem('items')) this.items = JSON.parse(localStorage.getItem('items'));
},
watch: {
items:{
handler(){
localStorage.setItem('items',JSON.stringify(this.items));
},
},
},
});
</script>
I expect correct boolean value of line-through effect to be stored in local-storage. So that, appropriate effect will show on browser.
You are just watching items. If you change something in a item (in your case completed) the handler will not be called and your change is not stored.
You could use a "deep" watcher but i suggest to call your save logic whenever you changed something.

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>