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

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"
/>

Related

Use methods and computed properties in child component

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

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.

vue.js: vue-multiselect, clearing input value after selected

I am working on my project in laravel/vue.js and I decided to use simple vue-multiselect to choose categories from my database. Thing is I want to clear the value from input field (to look for item in list).
My multiselect component:
<multiselect
v-model="value"
placeholder="Find the category"
label="category_name"
:options="categories"
#input="addReceiversFromCategory">
</multiselect>
I try to clear an v-model but it work only on first select after page load (and also it is not a smart way..).
Last thing i try was the :clear-on-select="true" but it works onlu when multiple is true (which I dont want to be true).
I think its simple to do but I didn't find any way in documentation doc
If your v-model is just modeling the value selected, then you need to use that value however you want and reset value to null. I don't really know how your component is set up but it would look something like this:
<template>
<select v-model="value" v-on:change="doSomething()">
<option :value="null">-- Select --</option>
<option value="foo">Foo</option>
<option value="bar">Bar</option>
</select>
</template>
<script>
module.exports = {
data: function(){
return {
value: null
};
},
methods: {
doSomething: function() {
if( this.value ) {
var data = this.value; // now you can work with data
this.value = null; // reset the select to null
}
}
}
}
</script>

VueJS multiple selection

I tried to multiple select with select2
i used laravel and my data on userRoles is returned data on User model with relation roles, and allRoles is just all in Role model
code on view
<select class="form-control" name="roles[]" id="roles" multiple>
<option v-for="role in allRoles"
:value="role.id"
>
#{{ role.name }}
</option>
</select>
how to set selected options using userRoles array?
Add a computed hashMap (object) to search for selected roles:
computed: {
selectedRoleMap() {
var map = {};
this.userRoles.forEach(role => {
map[role] = 1;
});
return map;
}
}
Then add :selected="selectedRoleMap.hasOwnProperty(role.id)" to options.
Working fiddle here: https://jsfiddle.net/df7j8xhz/
Alternatively, you can add a hasRole method to loop through userRoles to see if an id is in userRoles, but build a hashMap and search in it has a better performance than looping the array allRoles.length times.

AngularJS Form Validation inside an ng-repeat

So I am trying to validate the input of one item inside of an ng-repeat. For examples sake lets say that I have 5 items (1,2,3,4,5) and I only want to validate the form if the 4th item is selected.
I have used ng-pattern before to validate forms, but not one that had a dropdown menu to select item.name
I have included the regex I would like the 4th item to be validated with inside the ng-pattern.
<div>
<select name="name" ng-model="item.name" ng-options="item for item in items" required></select>
</div>
<div>
<input name="results" type="text" ng-model="item.results" ng-pattern="/^\d\d\d\/\d\d\d/" required>
</div>
Any suggestions as to the correct way to validate this situation would be greatly appreciated. I have thought about creating a directive to validate this, but that feels like is an overly complicated solution to this since I would not use the directive more than once in this app.
//////////////////////////////////////////////////
It wouldn't let me answer my own question so here is the answer I figured out.
What I ended up having to do was use ng-pattern and pass it a function.
<input name="results" type="text" ng-model="vital.results" ng-pattern="vitalRegEx()" required>
Here is the controller code
$scope.item4RegEx = /^\d{2,3}\/\d{2,3}$/;
$scope.itemRegEx = function() {
if($scope.item && $scope.item.name === "fourth item")
return $scope.item4RegEx;
else return (/^$/);
};
or else...
add ng-change directive on the select dropdown which calls a Controller method and that controller method sets a flag whether to validate form or not.
eg.
<select ng-change="checkIfFormShouldbeValidated()" ng-model="item.name"></select>
// Inside controller
$scope.checkIfFromShouldBeValidated = function(){
if( $scope.item.name == 4th Item ) $scope.shouldValidate = true;
else $scope.shouldValidate = false;
};
$scope.formSubmit = function(){
if(($scope.shouldValidate && form.$valid) || (!$scope.shouldValidate)){
// Submit Form
}
};
See if it helps.
I wrote this recursive function inside my controller to check the validity of all child scopes.
function allValid(scope) {
var valid = true;
if (scope.$$childHead) {
valid = valid && allValid(scope.$$childHead);
}
if (scope.$$nextSibling) {
valid = valid && allValid(scope.$$nextSibling);
}
if (scope.scorePlannerForm) {
valid = valid && scope.myForm.$valid;
}
return valid;
}
Then in my controller I check this with the controller scope.
function formSubmit() {
if (allValid($scope)) {
// perform save
}
}

Resources