Using an array data item length as part of string in a vue computed property - vuejs2

I have a data item
items: [],
which i am using in a computed property that returns the lengh of that array as part of a string.
itemSummary : function() {
return this.items.length === 0 ? "No Items" : "`${this.items.length}` items selected"
}
Is it possible to do this with string interpolation....?

You almost had it in your question. I think the code should be:
itemSummary() {
return this.items.length === 0 ? "No Items" : `${this.items.length} items selected`
}
With an interpolated string you put everything inside back quotes and interpolate variables or code snippets by wrapping them in ${ ... }.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

Related

react-i18next function this.props.t('id', value)

I am able to this.props.t('id') function which return string translation for given id.
Now, I have a requirement to get string translation for "Change value for " + (this.state !== null ? this.state.selectedData.length : 0) + " selected data".
In this translation, I'll have to use a variable inside string translation.
Is there any method such as : this.props.t('id', value)??
How can I get this working?
Thanks in Advance!
Read docs, this called interpolation, you can set in your translation value:
{
...
"MY_KEY": "Change value for {{value}} selected data"
..
}
And then call
this.props.t('MY_KEY', { value: this.state !== null ? this.state.selectedData.length : 0 });

How to create v-autocomplete which first shows items startsWith and then shows items indexOf

I would like to create a vuetify autocomplete with a custom filter that first shows the hits that start with the searchtext and then show the hits that not start with the searchtext, but have the searchtext somewhere in the middle.
I now have a custom filter like this, but this filter is not prioritizing words that start with the searchtext:
customFilter(item, queryText) {
const textOne = item.description.toLowerCase();
const textTwo = item.code.toLowerCase();
const searchText = queryText.toLowerCase();
return (
textOne.indexOf(searchText) > -1 || textTwo.indexOf(searchText) > -1
);
}
},
Codepen (Type 'ma' for example)
I believe you need to sort it manually, filter returns only true/false whether item is a match.
// https://stackoverflow.com/a/36114029/1981247
var _sortByTerm = function (data, term) {
return data.sort(function (a, b) {
// cast to lowercase
return a.toLowerCase().indexOf(term) < b.toLowerCase().indexOf(term) ? -1 : 1;
});
};
Then pass sorted array to items prop
<v-autocomplete :items="computedItems"
...
computed: {
computedItems() {
return _sortByTerm(this.states, this.search.toLowerCase())
},
},
Note this is just to get you started, and you might need to change code a bit according to the data (and filters) you are using, e.g. _sortByTerm works only on array of strings, but in the link there is a solution for sorting arrays of objects also.

Vue Error in render: "RangeError: Invalid array length"

Vue: v2.*
In my project vuejs
I use v-for range
with computed
Computed
computed: {
numberOfPages() {
const result = Math.ceil(this.collection.total / this.collection.per_page)
return (result < 1) ? 1 : result
}
},
template
<li class="waves-effect" v-for="(number,index) in numberOfPages"
:key="index" :class="collection.current_page == number ? 'active' : ''"
#click="currentPage(number)">
<a class="">{{number}}</a>
</li>
Error Console
1 - [Vue warn]: Error in render: "RangeError: Invalid array length"
2 - RangeError: Invalid array length
The most likely candidate for your problem is that your computed property returns NaN or Infinity. Without seeing all of your code, the most likely reason for that is one of the following:
You initialize collection to an empty Object. const result = Math.ceil(undefined / undefined) will return NaN
You do correctly prevent the property from being calculated before the result comes in, but the response from the server that populates collection has a per_page of 0. The calculation mentioned above would return Infinity, and Vue would not be able to create a range from that.
There are multiple ways of dealing with this problem. The easiest way is, if you can be certain that per_page is always > 0, to put a v-if on the element around your loop. If there is no convenient element, you can use the <template> element to put around it.
Otherwise you can check in your computed property if de data you are going to calculate with, is actually correct, and otherwise return a default number.
numberOfPages() {
if (
!this.collection ||
Number.isNaN(parseInt(this.collection.total)) ||
Number.isNaN(parseInt(this.collection.per_page)) ||
this.collection.per_page <= 0
) {
return 0;
}
const result = Math.ceil(this.collection.total / this.collection.per_page)
return (result < 1) ? 1 : result
}
Like someone else said, carefully check your computed properties. I had two different "interesting" situations (bugs that I introduced):
(1) First I forgot to include the "return" keyword in my computed property!
So I was doing:
```
myComputedProp () {
arr.length
}
```
which should have been return arr.length ... easy to overlook :-)
(2) Second, the result of my calculation (which I used as an array length/size) was not an integer but a real (broken number). Solution was of course to add Math.round() or Math.trunc() ... :-)

Nested arrays to call map and shorten repeated maps for single object omit property

I have a situation here where I want to omit the nested maps and get it done in one liner . Can it be done using chain or any other ways .
self.workorder.tasklist = _.map(self.workorder.tasklists, function (tasklist) {
tasklist.tasklistGroups = _.map(tasklist.tasklistGroups, function (tasklistGroup, tgKey) {
tasklistGroup.tasklistItems = _.map(tasklistGroup.tasklistItems, function (taskListItem, tKey) {
taskListItem = _.omit(taskListItem, ["open"]);
return taskListItem;
});
return tasklistGroup;
});
return tasklist;
});
I don't want so many nested map calls .
Because you are modifying your items in place I would say this is possible:
_.chain(self.workorder.tasklists).map(function(tasklist) {
return tasklist.tasklistGroups;
}).flatten().map(function(group) {
return group.tasklistItems
}).flatten().forEach(function(item) {
delete item.open;
}).value();
Jsfiddle
The idea is to flatten your array to last level (level of items) and then modify them using forEach.

Vue.js: error setting a computed property

in the following code (jsbin available here) I have two input elements, a range and a text, bound together via a computed property.
var vm = new Vue({
el: '#main-container',
data: {
sliderValue: 100,
},
computed: {
actualValue: {
get: function() {
if (this.sliderValue <= 100) {
return this.sliderValue;
} else {
return Math.round(this.sliderValue * 12.5 - 1150);
}
},
/* set won't work for val > 100*/
set: function(val) {
if (val <= 100) {
this.sliderValue = val;
} else {
this.sliderValue = Math.round((val + 1150)/12.5);
}
}
}
},
methods: {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<div id="main-container">
<input type="range" v-model="sliderValue" min=1 max=132>
<input type="text" v-model="actualValue">
<p>Slider: {{sliderValue}}</p>
<p>Actual: {{actualValue}}</p>
</div>
The range goes from 1 to 132, and its range is mapped [1..500] in the text input, with a simple transformation (basically it's a linear mapping with two different slopes for [1..100] and [101..132]) using the actualValue computed property.
Getting actualValue works as expected: dragging the slider correctly updates the input text with appropriate values in the range [1..500].
I'm not able to find a way to set actualValue, though. I'd like to be able to type a value in the text input, and make the slider's thumb update accordingly to the inverse transformation (val + 1150)/12.5.
It works as long as the typed number is in the range [1..100], but it "explodes" for numbers >100, e.g. 101 makes the sliderValue jump at 80892 and actualValue is then re-calculated as 1010000. As far as I understand, it's a looping-feedback scenario.
I've tried also alternative approaches (watch; v-on:change in the text input; using a third variable) to no avail.
Thanks in advance for any suggestion!
It's an amazing puzzle, and challenged me for a long time!
Look at the screenshot below. Your sliderValue and actualValue are strings, not integers. When you set actualValue as 101, you are actually setting it as a string value of "101"
Now, your sliderValue = ((actualValue + 1150)/12.5)
"101" + 1150 = "1011150" (another string!, try it in the developer console)
That messes up your entire calculation. Now you know how to fix it :-)
And you need to get that Vue devtools from here: https://github.com/vuejs/vue-devtools
EDIT: Response to comment #3
Here is the modified jsBin: http://jsbin.com/mahejozeye/1/edit?html,js,output
The only difference is introduction of two console.log statements in your map2 function. This helps you identify if your non-linear mapping function is working correctly or not. If you keep your developer console open, you will see what is happening in this function.
Case 1: When you set the value radius = 25 using the text box, your sliderRadius gets set to 111.55518394648828 and your radius again gets re-calculated as 25. So it comes around in a full circle and everything is stable here.
Case 2: When you set the value radius = 55, your sliderRadius gets set to 173.03607214428857 through your non-linear map2() function, which resets radius to 51.29869180420927
Clearly there is a circular dependency issue. Your sliderRadius and radius are very much dependent on each other and therefore radius is unable to take the value between 51 and 58.
You need to evaluate why it happens, as it has a lot to do with the non-linear mapping function that you have defined. The moment radius can take stable values at 55 (through the map2 function), then your current problem will be resolved.
The simplest fix is to set your input type to number:
<input type="number" v-model="actualValue">
or you can convert your value to an integer with something like:
set: function(val) {
var intVal = parseInt(val, 10);
if (!isNaN(intVal)) {
if (intVal <= 100) {
this.sliderValue = Math.max(1, intVal);
} else {
this.sliderValue = Math.min(132, Math.round((intVal + 1150) / 12.5));
}
}
}