Use methods and computed properties in child component - methods

In my List component I have a method which count the length of the array within certain categories.
methods: {
getLengthofaCategory(cat) {
const LowerCaseSearch = this.search.toLowerCase();
let categoryCount = this.products.filter(
product =>
(product.name.toLowerCase().includes(LowerCaseSearch) ||
product.category.toLowerCase().includes(LowerCaseSearch)) &&
(!this.checked.length || this.checked.includes(product.category)) &&
product.category === cat
);
return categoryCount.length;
}
}
See here my setup in this sandbox.
But I want the values next to the checkboxes (which are coming from my CheckBox component).
How do I get the logic from the method getLengthofaCategory into my CheckBox component?
So I am able to use {{ getLengthofaCategory('tennis') }} in the v-for loop, inside the CheckBox component. And then maybe I can also use category.value instead of hardcoding e.g 'tennis' as the paramater?

In your list.vue, you can use the already created computed function filteredData instead of doing the filter again. This saves some performance because in Vue, computed properties are "cached" once run.
So you can create a new computed function that creates an object with keys per category and value can either be just the amount or an array of products in this category.
I would then pass this computed value to the CheckBox component via a prop, then inside the CheckBox component, you can display the .length or value regarding how many items each category has:
List.vue:
computed: {
//...
amountPerCategory() {
return this.filteredData.reduce((categories, product) => {
if (!(product.category in categories)) {
categories[product.category] = [];
}
categories[product.category].push(product);
return categories;
}, {});
}
}
CheckBox.vue:
<span class="ml-2 text-gray-700 capitalize">{{ category.value }}</span> -
<span
v-if="count[category.value]"
class="ml-2 text-gray-700 capitalize"
>{{ count[category.value].length }}</span>
count: {
type: Object,
default: () => ({})
}
https://codesandbox.io/s/admiring-ellis-4hojl?file=/src/components/CheckBox.vue

Related

How to create method to sort by property value (string)?

I'm creating the following list in Vue by iterating through payeefundingAccountList.
<cx-select id="payee-select" #cxChange="changeSelectedPayee" class="dropdown">
<option value="" disabled selected>Choose payment account</option>
<option
v-for="(account, index) in payeefundingAccountsList"
:value="index"
:key="index"
:checked="index === paymentAccountSelectedIndex"
>{{ account.bankName }}...{{account.displayAccountNumber}}({{account.accountType}})</option>
<option
v-bind:key="payeefundingAccountsList.length + 1"
v-bind:value="payeefundingAccountsList.length +1"
>Add a payment account</option>
</cx-select>
I need to sort this list by the property account.accountType. This property can have two values: CHECKING or SAVINGS and I need the items with accountType CHECKING to be sorted to the top.
I'm trying to create a method to run the list through before iterating, as such:
v-for="(account, index) in sortByAccountType(payeefundingAccountsList)"
...but, I'm having some trouble getting any sort of results from the sortByAccountType method. I feel like my approach is way off. If anyone could point me in the right direction, I'd be over the moon.
#Nikolas Yankov is right. But do yourself a favor and import the one function from lodash:_.sortBy. You can:
import _sortBy from ‚lodash/sortBy‘
export default {
computed: {
sortedAccountsList() {
return _sortBy(this.payeeFundingAccountsList, [‚accountType‘])
}
}
}
You can use a computed property where you can make your sorting before v-for loop start using it. For instance, if your collection "payeefundingAccountsList" is a data property in data, then the computed property would look like this:
computed: {
sortedList() {
return this.payeefundingAccountsList.sort(this._compare);
},
},
methods: {
_compare(a, b) {
if (a.accountType < b.accountType) {
return -1;
}
if (a.accountType > b.accountType) {
return 1;
}
return 0;
},
},
And then use this computed property in your v-for:
<option
v-for="(account, index) in sortedList"
/>

Calling a huge object inside v-for causing massive lag?

recently I'm working on a project showing sport events data
While v-for through the events array (which is around 500~2000 length), I'll call a method in each to show their participants name
<div v-for="event in eventArr" :key="event.id">
<template v-if="mappingObj[event.id]">
<div class="participantName">
{{ getCorrespondParticipants (
mappingInfo[event.id][mappingId],
mappingInfo[event.id][sportId])
}}
</div>
</template>
</div>
what it looks like in template, mappingObj is a object collecting all event data with event id as key, its length is around 500~2000(same as the origin events array)
getCorrespondParticipants (pidArr, sportId) {
this.participantListObj[sportId];
});
// this part was supposed to get corresponded participants data from the object, returning this.participantListObj[sportId][pid in pidArr]
return pArr;
},
The method was trying to get the participants info, tho even if I remove all the lines just left 'this.participantListObj[sportId]' there(so it basically did nth but calling the obj), it still causing massive lag result to slow performance for my page, not to mention there's another method to get the league info
participantListObj () {
let cloneObj = JSON.parse(
JSON.stringify(this.totalParticipantList)
);
for (let sportId in cloneObj) {
cloneObj[sportId] = cloneObj[sportId].reduce(
(pObj, item) =>
Object.assign(pObj, { [item.participant_id]: item }),
{}
);
}
return cloneObj;
},
which would looks like...
participantListObj :{
1: {
123:{
participant_id: 123
name_EN: 'Player',
name_CN: '玩家'
},
...(18000+)
}
}
The obj is participants list object under each sport Id, made from a participants array list which length could be around 18000, in this case the page has only one sport that is soccer. Did I did anything wrong?

Get index within v-for using computed or method

I was wondering if it is possible in vue to get the index of an object array directly within the v-for in and pass this value to a computed property or a method, similar to this here, or even a computed property
<div v-for="(object, index) in objects(index)"></div>
methods: {
objects(index){
const categoryId = Object.keys(this.data);
return this.data[categoryId[index]].extras;
}
}
I need to have the index as it is more convenient for me to return the correct value based on defined key, is there some way to achieve this?
Transform your data using a computed value and loop over that. I am not sure what your this.data looks like, but something like this should work (tweak it to suit your needs):
<div v-for="object in computed_objects"></div>
computed: {
computed_objects(){
return Object.keys(this.data).map(categoryId => this.data[categoryId].extras)
}
}
You can bind a method call on each element created by the v-for directive, so for example any time a user clicks on <li> element, it will gets the index of that clicked item:
new Vue({
el: '#app',
data: {
clickedIndex: null,
weekDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']
},
methods: {
handleClick(i) {
this.clickedIndex = i;
}
}
})
Vue.config.productionTip = false;
Vue.config.devtools = false;
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<ul>
<li v-for="(day,i) in weekDays" v-on:click="handleClick(i)">{{day}}</li>
</ul>
<p>Index clicked {{ clickedIndex }}</p>
</div>

Output user first name

I want to get the name of the user to put it on an h1.
What dies this line stand for?
#select="option => selected = option">
I'm using Buefy for the vue components.
<template>
<section>
<div class="field">
<b-switch v-model="keepFirst">
Keep-first <small>(will always have first option pre-selected)</small>
</b-switch>
</div>
<p class="content"><b>Selected:</b> {{ selected }}</p>
<b-field label="Find a name">
<b-autocomplete
v-model="name"
placeholder="e.g. Anne"
:keep-first="keepFirst"
:data="filteredDataObj"
field="user.first_name"
#select="option => selected = option">
</b-autocomplete>
</b-field>
</section>
</template>
<script>
import data from '#/assets/data_test.json'
// Data example
// [{"id":1,"user":{"first_name":"Jesse","last_name":"Simmons"},"date":"2016-10-15 13:43:27","gender":"Male"},
// {"id":2,"user":{"first_name":"John","last_name":"Jacobs"},"date":"2016-12-15 06:00:53","gender":"Male"},
// {"id":3,"user":{"first_name":"Tina","last_name":"Gilbert"},"date":"2016-04-26 06:26:28","gender":"Female"},
// {"id":4,"user":{"first_name":"Clarence","last_name":"Flores"},"date":"2016-04-10 10:28:46","gender":"Male"},
// {"id":5,"user":{"first_name":"Anne","last_name":"Lee"},"date":"2016-12-06 14:38:38","gender":"Female"}]
export default {
data() {
return {
data,
keepFirst: false,
name: '',
selected: null
}
},
computed: {
filteredDataObj() {
return this.data.filter((option) => {
return option.user.first_name
.toString()
.toLowerCase()
.indexOf(this.name.toLowerCase()) >= 0
})
}
}
}
</script>
# is shorthand for v-on:, so it's handling a select event with a function that receives option as a parameter and assigns it to selected.
Since v-model is bound to name, you should be able to do <h1>{{name}}</h1> to have the same value show up in an H1.
The data section has the main variables for your object. name is there. There is also a computed (named filteredDataObj) that should return an array (length of zero or one) with the matching test data. If you want other fields (like id) you would need to look there. Something like
{{filteredDataObj.length ? filteredDataObj.id : ''}}
would give the id if name matched anything in the data set.

I want to check at least one value in ng-repeat, if found, then to display <div> class

In my ng-repeat, scored/not scored are there. I want to display <div> class if at least one item in the ng-repeat has "Not Scored"
You should be determining whether or not you are displaying that div inside your controller. Doing it within the ng-repeat would mean you would have logic in your view and that's just not good practice. Here is a simple example on how to accomplish what you're wanting.
In your controller:
$scope.showDiv = false;
$scope.getItems = function () {
// fetch items via ajax...
for (var i = 0; i < data.items.length; i++) {
if (data.items[i].foo == 'Not Scored') {
showDiv = true;
}
}
}
And on your view:
<div ng-show="showDiv">
// do your ng-repeat here
</div>

Resources