I recently started out with vuejs and the idea is to make a POS system with it. So currently I have an array of products and display them etc that all works just fine. Now my problem is when trying to calculate the sub-total price (price without the added VAT) of all products together.
So here is what I currently have:
<div class="col-md-12 productsList">
<div class="product" v-for="product in products">
<p>
<span class="col-md-4">#{{ product.name }}</span>
<span class="col-md-3 col-md-offset-1">#{{ product.amount }}</span>
<span class="col-md-3 col-md-offset-1" v-text="(parseFloat(product.price) * product.amount) | float"></span>
</p>
</div>
</div>
<script>
new Vue({
el: '.container-custom',
data: {
products: []
},
ready: function() {
var self = this;
},
methods: {
fetchSingleProduct: function(productId) {
this.$http.get('get/products/'+productId).then((product) => {
var existingProducts = this.products.filter(function (item) { return (item.id == product.data.id)});
if (existingProducts.length) {
existingProducts[0].amount += 1;
} else {
product.data.amount = 1;
this.products.push(product.data);
}
});
},
},
filters: {
float: function (value) {
return value.toFixed(2);
}
}
});
Each product holds:
name, amount, price, vat
Each product's vat can be different too (some products have 6%, some 21%) etc. How would I accomplish such thing with vue?
Just add all products that you need to sum into array and then create a computed property that returns the sum of that array with either array.reduce or a simple for loop ...
Related
I want to display the designated data that is found for a particular code match. I have a data set that will come in model. I want if the data-set, subject property has the first 2-3 characters found in it, to display the corresponding name. Based on the first 3 characters begins with LA_, which is found in the first index, only the first set of content should appear (Name: Library Arts Department: ACSF-LA Identifier: 6774). I know i would need to slice the character off, with string slice, but what if sometimes the name has like LAX_ (SO I want to be sure to check if the subjects have any that match--). So basically to check everything before the first "_"
new Vue({
el: "#app",
data: {
Name:"LA_123_cc",
todos: [{"Name":"Library Arts","Identifier":"6774","Code":"AACSF-LA","Subjects":["LA_","AEL","APC","GAP","FAC","GLM","GS","MPT","PRO","WNM"]},
{"Name":"Violin Dance","Identifier":"6169","Code":"Avvv-VA","Subjects":["VA","VL","VPC","AAP","YAC","XLM","GXS","XPT","IRO","CNM"]}
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Name: {{Name}}</h2>
<ol>
<li v-for="todo in todos">
Name: {{todo.Name}} <br>
Department: {{todo.Code}}<br>
Identifier: {{todo.Identifier}}
</li>
</ol>
</div>
Create a computed property that uses Array.prototype.filter on the todos[]. The callback to filter() receives each array item, and returns true if the item should be in the result. In this callback, you can check if each item contains the leading characters (before the underscore) in the search string (LA in your example):
export default {
computed: {
computedTodos() {
const searchLetters = this.Name.split('_')[0].split('') /* get characters of first part of string before underscore */
.filter(x => /\w/.test(x)) /* keep only letters */
return this.todos.filter(item => {
/* check if each letter appears in order within the item's name */
let i = 0
return searchLetters.every(letter => {
i = item.Name.indexOf(letter, i)
return i > -1
})
})
}
}
}
Then update the template to use the computed prop instead of todos[]:
<!-- <li v-for="todo in todos"> -->
<li v-for="todo in computedTodos">
new Vue({
el: "#app",
data: {
Name:"LA_123_cc",
todos: [{"Name":"Library Arts","Identifier":"6774","Code":"AACSF-LA","Subjects":["LA_","AEL","APC","GAP","FAC","GLM","GS","MPT","PRO","WNM"]},
{"Name":"Violin Dance","Identifier":"6169","Code":"Avvv-VA","Subjects":["VA","VL","VPC","AAP","YAC","XLM","GXS","XPT","IRO","CNM"]}
]
},
computed: {
computedTodos() {
const searchLetters = this.Name.split('_')[0].split('').filter(x => /\w/.test(x))
return this.todos.filter(item => {
let i = 0
return searchLetters.every(letter => {
i = item.Name.indexOf(letter, i)
return i > -1
})
})
}
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="Name">
<h2>Name: {{Name}}</h2>
<ol>
<li v-for="todo in computedTodos">
Name: {{todo.Name}} <br>
Department: {{todo.Code}}<br>
Identifier: {{todo.Identifier}}
</li>
</ol>
</div>
I have a simple number array generated at random that is rendered by a v-for, I also want to be able to filter it by writing the desired numbers in a input, I do this by using the vanilla JS filter() method. However it returns the error
TypeError: "num.includes is not a function"
I don't know what am I doing wrong, here's the html:
new Vue({
el: "#app",
data: {
numberArray: [],
searching: ''
},
methods: {
random() {
this.numberArray = Array.from({
length: 40
}, () => Math.floor(Math.random() * 40));
},
search(){
return this.numberArray.filter((num) => num.includes(this.searching));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="random()">
Generate
</button>
<hr>
<input type="search" v-model="searching" placeholder="search">
<ul>
<li v-for="num in search">
{{ num }}
</li>
</ul>
</div>
includes() is a function defined on strings and arrays, not numbers.
Also search should be a computed property instead of a method.
Did you mean to do this:
computed: {
search() {
return this.numberArray.filter(num => String(num).includes(this.searching));
}
}
I used AngularJS for a long time and now I'm making the switch to VueJS, but I can't figure out why this simple Angular code isn't easily converted to in VueJS.
This is a search-field:
<input type="search" ng-model="searchFor.$">
And then I'm using it like this:
<ul>
<li ng-repeat="user in users | filter: search">
{{ user.email }}
</li>
</ul>
This filter is an easy thing and search in everything in the 'users'-array, so not even the mailaddresses.
How can I do this easily in Vue? Can't figure it out, only can find solutions where you define the specific column it should look.
In this case you must use a computed property that returns a filtred array. The computed array will recursively search in each string properties of your user.
Here is an example
new Vue({
el: '#app',
data() {
return {
search : '',
users : [{name : "John Doe", email : "xerox#hotmail.us"}, {name : "Jane Doe"}],
}
},
computed : {
filteredUsers() {
if (!this.search) return this.users
var find = function(object, search) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (typeof object[property] == "object"){
find(object[property]);
} else if (object[property].includes !== undefined){
if (object[property].includes(search)) return true;
}
}
}
return false;
}
return this.users.filter(user => {
return find(user, this.search)
})
}
}
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="app">
<input type="text" v-model="search" placeholder="Filter users">
<p v-show="!filteredUsers.length">No results</p>
<ul>
<li v-for="user in filteredUsers">{{user.name}}, email : {{user.email || 'N/A'}}</li>
</ul>
</div>
Why v-model does not work with a filter getUppercase in <input v-model="filterText | getUppercase">
HTML
<template>
<div class="wrapper">
Check if fruit exist: <input v-model="filterText | getUppercase">
<ul v-for="fruit in filteredFruits">
<li> {{ fruit }} </li>
</ul>
</div>
</template>
VueJS
export default {
name: "filterText",
data() {
return {
msg: "Welcome to Your Vue.js App",
filterText: "",
fruits: ["Apple", "Banana", "Orange", "PineApple", 'Pina Colada']
};
},
computed: {
filteredFruits: function() {
var vm = this;
return vm.fruits.filter(function(item) {
return item.match(vm.filterText)
});
}
},
filters: {
getUppercase: function(obj) {
return this.obj.toUpperCase();
}
}
};
I can see what you are trying to do, however, because of the two way binding when using v-model, it will be better to just apply the getUppercase filter when displaying.
Your template would be something like this:
<template>
<div class="wrapper">
Check if fruit exist: <input v-model="filterText">
<ul v-for="fruit in filteredFruits">
<li> {{ fruit | getUppercase}} </li>
</ul>
</div>
</template>
But if you still wish to transform the filterText model value, you can use a directive. In that case, your VueJS code will be something like :
Vue.directive('getUppercase', {
twoWay: true, // this transformation applies back to the filterText
bind: function () {
var self = this;
self.handler = function () {
self.set(self.el.value.toUpperCase());
}
self.el.addEventListener('input', self.handler);
},
unbind: function () {
this.el.removeEventListener('input', this.handler);
}
});
Now use this directive in your template like :
<input v-model="filterText" v-get-uppercase="filterText">
It will do the same thing as <input v-model="filterText | getUppercase">
Two ways filters are replaced in vue.js please read the docs for more information.It is good to know.
However,as i understood you want to implement a search in array.See it in action here, or take a look below
<div id="app">
Check if fruit exist: <input v-model="filterText">
<ul v-for="fruit in filteredFruits">
<li> {{ fruit }} </li>
</ul>
</div>
new Vue({
el: "#app",
data: {
filterText: "",
fruits: ["Apple", "Banana", "Orange", "PineApple", 'Pina Colada']
},
computed: {
filteredFruits() {
return this.fruits.filter(item => item.toLowerCase().match(this.filterText.toLowerCase()))
}
}
})
My vue component, you can see below :
<template>
<div>
<div class="panel-group" v-for="item in list">
...
{{ total = 0 }}
<tr v-for="product in item.products">
...
<td>
<b>Price</b><br>
<span>{{ product.quantity * product.price }}</span>
</td>
</tr>
{{ total += (product.quantity * product.price) }}
<tr>
<td colspan="3" class="text-right">
<b>Total: {{ total }} </b>
</td>
</tr>
</div>
</div>
</template>
<script>
export default {
...
computed: {
list: function() {
return this.$store.state.transaction.list
},
...
}
}
</script>
I try like above code
But, seems it still wrong
How can I solve it correctly?
I'm still newbie in vue.js 2
Since, TypeError: this.$store.state.transaction.list.reduce is not a function is an error marked in Frank's answer I presume this.$store.state.transaction.list is not an Array but an object as v-for iterates through both.
total: function() {
var list = this.$store.state.transaction.list
var sum = 0
for(var listProps in list) {
list[listProps].products.forEach(function (product) {
sum += product.pivot.quantity * product.pivot.price
})
}
return sum;
}
Use another computed property
<script>
export default {
...
computed: {
list: function() {
return this.$store.state.transaction.list
},
total: function() {
return this.$store.state.transaction.list.reduce(function(sum, item) {
sum += item.products.reduce(function(tmp, product) { tmp += product.quantity * product.price; return tmp; }, 0);
return sum;
}, 0);
}
...
}
}
</script>
Use a nested Array.reduce to get the total of your structure where the list has many items and an item has many products