VueJS: Sum up computed values to one computed value - vue.js

I have a html form and based on the user input I want to give a total score of completion (e.g 80%).
My plan is to do that with computed values that are watching seperate parts of the form and for the total score I want to sum them up like so:
simplified version of the vue file:
[..]
computed: {
Chk_input1: function(){
// Here will be a check if at least 4 characters in input field, if yes
return 30
},
Chk_input2: function(){
// Here will be a check if multiple conditions are met, if yes
return 30
},
Chk_input3: function(){
// Here will be a check if at least 4 characters in input field, if yes
return 26
},
total_score: function(Chk_input1, Chk_input2, Chk_input3){
// finally sum up the computed values input1 - input3
return Chk_input1 + Chk_input2 + Chk_input3
}
},
But the result is:
total_score = [object Object]undefinedundefined%
How do I sum up these values properly?

Wherever you're calling total_score, you're passing an object for the first argument, and undefined to the second two.
total_score should be written like this:
total_score: function() {
return this.Chk_input1 + this.Chk_input2 + this.Chk_input3
}
The difference between my code and yours is that my code is accessing the other computed properties to return a sum, and yours is accepting arguments, which it accesses to calculate the sum.

Related

Vue-ApexChart Set color on individual bars based on another series value

I'm using ApexChart-Vue and my graph has two series one bar and one line. The line is the goal for that specific date and the bar is the value for that date. I'm trying to set the color of the bar to red if the value is lower than the goal and to green if the value is equal or higher that the goal.
I understand that a can add a function to the colors array, but only one function per series.
Basically I want to do way the doc says here: https://apexcharts.com/docs/options/colors/
colors: [function({ value, seriesIndex, w }) {
if (value < 55) {
return '#7E36AF'
} else {
return '#D9534F'
}
}, function({ value, seriesIndex, w }) {
if (value < 111) {
return '#7E36AF'
} else {
return '#D9534F'
}
}]
But I need to pass the the goal for the current date into the function some how.
Any ideas?
As I understand it, you wish to be able to customize the values 55 and 111 based on some changing goal eg the target on Bar 1 is 10 but on Bar 2 is 20.
According to the source code, color functions receive the following object:
{
seriesIndex: this.seriesIndex, // the data series being coloured
dataPointIndex: opts.dataPointIndex, // the index for that bar
value: opts.value, // the value for that bar
w: w // ??
}
My approach would be to store the targets as an array then just access the threshold for each date using the index value
colors: [
function ({ value, dataPointIndex }) {
if (targets[dataPointIndex] > ) {
return Colors.red
} else {
return Colors.green
}
}]

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() ... :-)

How to create a Calculated Field in dimple.js

How do i add a calculated field (a field which does calculation of two or more data fields) in dimple.js?
eg. I have two fields
1. "Sales Value"
2. "Sales Volume"
Now i have to calculate a field ASP = Sales Value /Sales Volume.
I'm afraid dimple doesn't have a built in way to handle this. I assume dimple is aggregating the data for you - hence the difficulty. But here you have no option but to pre-aggregate to the level of a data point and add the calculated field yourself. For example if your data had Brand, SKU and Channel but your chart was at the Brand, Channel level you would need to pre-process the data like this:
// var chartData is going to be the aggregated level you use for your chart.
// var added is a dictionary of aggregated data returning a row index
// for each Brand/Channel combination.
var chartData = [],
added = {};
// Aggregate to the Brand/Channel level
data.forEach(function (d) {
var key = d["Brand"] + "|" + d["Channel"],
i = added[key];
// Check the output index
if (i !== undefined) {
// Brand/Channel have been added already so add the measures
chartData[i]["Sales Value"] += parseFloat(d["Sales Value"]);
chartData[i]["Sales Volume"] += parseFloat(d["Sales Volume"]);
} else {
// Get the index for the row we are about to add
added[key] = chartData.length;
// Insert a new output row for the Brand/Channel
chartData.push({
"Brand": d["Brand"],
"Channel": d["Channel"],
"Sales Value": parseFloat(d["Sales Value"]) || 0,
"Sales Volume": parseFloat(d["Sales Volume"]) || 0
});
}
});
// Calculate ASP
chartData.forEach(function (d) {
d["ASP"] = d["Sales Value"] / d["Sales Volume"];
});
// Draw the chart using chartData instead of data
...

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));
}
}
}

Summing a filtered column in DataTables

I'm trying to sum the results of filtered columns in DataTables. I've viewed the questions asked on their website and folks have had success using this method.
However, similar code for me produces 'Uncaught TypeError: undefined is not a function."
data = table._('td:nth-child(10)', {"filter": "applied"});
where 'table' is:
var table = $('#dataTable').DataTable({
// my initialization data
});
_ (the underscore function) seems to be deprecated in dataTables 1.10.x. In theory it should work with $('#dataTable').dataTable() (the old constructor) but this does not give the expected result (as least not for me).
But see this -> http://datatables.net/plug-ins/api/sum()
jQuery.fn.dataTable.Api.register( 'sum()', function () {
return this.flatten().reduce( function ( a, b ) {
return (a*1) + (b*1); // cast values in-case they are strings
});
});
var table = $("#example").DataTable();
$("#example").on('search.dt', function() {
console.log(table.column( 0, {page:'current'} ).data().sum() );
});
would give the same functionality in dataTables 1.10.x as you want in the question header.
see demo -> http://jsfiddle.net/6qLwkwud/
table.column( 0, {"filter": "applied"} ).data().sum() works perfectly well also.