I'm building a little pension calculator where I want to show the value of the pension pot depending on the users current and retirement age.
I'm looking to take the user's data, number fields bound to a Vue model and then computing the value of the pot based on that data.
That works just fine but having an issue with not being able to have Highcharts redraw the chart when the models change.
The redrawChart method fires but the chart stays the same with the console telling me Chart.redraw is not a function.
Redraw is an option for the Highcharts API so not sure where I'm failing.
Here's the markup:
<div id="app">
<input type="number" min="55" v-model.number="age" v-on:change="redrawChart">{{ age }}
<br>
<input type="number" min="1" max="1000000" v-model.number="currentPensionValue" v-on:change="redrawChart">{{ currentPensionValue }}
<br>
<input type="number" min="56" v-model.number="retireAge" v-on:change="redrawChart">{{ retireAge }}
<br>
<Chart :data="data" :steven="steven" :age-pot-value="agePotValue"></Chart>
and the associated Vue code
const Chart = Vue.component('Chart', {
props: ['data', 'steven', 'agePotValue'],
template: `
<div>
<p>{{ data }}</p>
<h1>{{ steven }}</h1>
<h1>{{ agePotValue }}</h1>
<div id="thechart"></div>
</div>
`,
mounted() {
var highchartsOptions = {
chart: {
type: 'area',
renderTo: 'thechart'
},
credits: {
enabled: false
},
tooltip: {
enabled: false
},
title: {
text: ''
},
xAxis: {
allowDecimals: false,
title: {
text: 'Age'
}
},
yAxis: {
title: {
text: 'Pot Value'
},
labels: {
formatter: function () {
return '£' + this.value / 1000 + 'k';
}
},
opposite: true,
},
plotOptions: {},
series: [{
name: '',
data: this.agePotValue
}],
credits: false
}
Highcharts.chart(highchartsOptions)
}
});
new Vue({
el: '#app',
data: {
age: 55,
currentPensionValue: 22000,
retireAge: 87
},
computed: {
data() { return (this.currentPensionValue) / (this.retireAge - this.age) },
steven() { return this.data * 1.4 },
agePotValue() {
var vm = this;
var agePotValue = [[vm.age, (vm.data)], [vm.retireAge, vm.currentPensionValue]];
return agePotValue;
}
},
components: { Chart },
methods: {
redrawChart() {
Chart.redraw();
}
}
})
Here's the the fiddle https://jsfiddle.net/stevieg_83/naLqzunz/11/
Any help appreciated.
Please see this working fiddle
https://jsfiddle.net/naLqzunz/12/
I make a little change on your approach, component will be watching for changes
watch:{
data(){this.redraw()},
steven(){this.redraw()},
agePotValue(){this.redraw()},
},
and redraws method just update chart data (that fires redraw automatically)
methods:{
redraw(){
this.chart.series[0].setData(this.agePotValue,true);
}
},
Related
Im stuck with rendering Chart from Primevue components. It's based on chart.js library. At this moment I have simple vue component created:
<template>
<div class="p-chart">
<h2>Chart:</h2>
<chart type="line" :data="chartData" />
</div>
</template>
<script>
import Chart from "primevue/chart";
export default {
data() {
return {
chartData: {
labels: ["Label"],
datasets: [
{
label: "Dataset",
backgroundColor: "#5F5F5F",
data: [99],
},
],
},
};
},
components: {
Chart,
},
};
</script>
Unfortunately the chart does not appear moreover I don't see any js erros in brwoser console. Can someone help what I'm missing here? Any additional setup needed?
Remove chart.js and then install this version, worked for me (but i use vue 3, maybe you need another version) :
npm i chart.js#2.9.4
According to documentation on https://www.primefaces.org/primevue/showcase/#/chart/line
I think you are missing the options attribute inside Chart tag:
<chart type="line" :data="chartData" :options="chartOptions" />
And put the object inside the data return from vue export:
data() {
return {
chartData: {
labels: ["Label"],
datasets: [
{
label: "Dataset",
backgroundColor: "#5F5F5F",
data: [99],
},
],
},
chartOptions: {
plugins: {
legend: {
labels: {
color: '#495057'
}
}
},
scales: {
x: {
ticks: {
color: '#495057'
},
grid: {
color: '#ebedef'
}
},
y: {
ticks: {
color: '#495057'
},
grid: {
color: '#ebedef'
}
}
}
}
};
This is my VueJS code, i use this lib https://www.npmjs.com/package/highcharts-vue
So i dont know how i can get data and set it to series before the graph is drawn. Or if this is not posible, how can i redraw graph properly? Becouse now i set some default data, then get data from page, and redraw graph, but when its done and i see my graph, the scrollbar go to the left side and has a very small range. So how set options without change scrollbar and range selector?
<template>
<highcharts :constructor-type="'stockChart'" :options="options" :updateArgs="[true, false]" ref="linerchart"></highcharts>
</template>
<script>
import {Chart} from 'highcharts-vue'
import Highcharts from 'highcharts'
import stockInit from 'highcharts/modules/stock'
stockInit(Highcharts)
export default {
data: () => ({
obj: [],
names: ['CAFF'],
options: {
credits: { enabled: false },
rangeSelector: {
selected: 1,
inputEnabled: false,
buttonTheme: {
visibility: 'visible'
}
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showCheckbox: false,
events: {
checkboxClick: function (event) {
if (event.checked) {
this.show();
} else {
this.hide();
}
}
}
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> USD<br/>',
split: true
},
series: [{
name: "CAFF",
data: [1,2,3,4,5,6]
}]
}
}),
methods: {
linerGraph() {
var that = this;
this.names.forEach(function(name, i){
axios.get('/test/account/CAFF')
.then(response => {
console.log(response.data)
that.obj.push({name: name, data: response.data});
});
});
this.options.series = that.obj
},
},
components: {
highcharts: Chart
},
mounted() {
console.log(this.$refs.linerchart)
this.linerGraph();
}
}
</script>
You should use other VueJS lifecycle hook point to run axios request, because you are trying to download it when the component is already mounted(), so please try to use one of hook points before that one, e.g created().
Here is the Lifecycle diagram from Vue General Documentation:
I'm looking to export a customized chart simliar to the post here Export Highcharts to PDF (using javascript and local server - no internet connection) that references http://fiddle.jshell.net/leighking2/dct9tfvn/. This has the exact functionality that I'm looking for, however I have my chart/data in a sepearte file/controller. Does anyone know how I could take the concept from the post I listed above, but incorporate it into my controller?
Didn't want to include my whole index.html file but here are the two charts from it.
<div id="allCharts" class="col-md-6" >
<button class="export_all" >export all</button>
<highchart id="chart1" config="chartConfig" class="span9 myChart" ></highchart>
<hr>
<highchart id="chart2" config="chartConfig2" class="span9 myChart" ></highchart>
</div>
'use strict';
var myapp = angular.module('myapp', ["highcharts-ng"]);
myapp.controller('myctrl', function ($scope) {
$scope.chartConfig = {
options: {
chart: {
type: 'line'
},
xAxis: {
categories: ['Year 1', 'Year 2', 'Year 3', 'Year 4', 'Year 5'],
title: {
text: null
}
},
plotOptions: {
line: {
events: {
legendItemClick: function () {
return false;
}
}
}, allowPointSelect: false,
series: {
stacking: ''
}
}
},
series: [{ "name": "Purchase Costs", data: $scope.purchaseChartData }, { "name": "Rental Costs", data: $scope.rentalChartData }],
title: {
text: 'Rental and Purchase Costs'
},
credits: {
enabled: true
},
loading: false,
size: {}
};
$scope.chartConfig2 = {
options: {
chart: {
type: 'column'
},
xAxis: {
categories: ['Year 1', 'Year 2', 'Year 3', 'Year 4', 'Year 5'],
title: {
text: null
}
},
plotOptions: {
column: {
events: {
legendItemClick: function () {
return false;
}
}
}, allowPointSelect: false,
series: {
stacking: ''
}
}
},
exporting:{
allowHTML:true
},
series: [{ "name": "Annual Savings", data: $scope.savingsChartData }],
title: {
useHTML:false,
text: 'Savings Compounded Annually'
},
credits: {
enabled: true
},
loading: false,
size: {}
};
$scope.reflow = function () {
$scope.$broadcast('highchartsng.reflow');
};
});
Question Update
Thanks Vaelyr! I now have the print button working; however I only have it adding text. I'm just having trouble linking my chart to the imageData. For now I just have the script within my html page until I get this working.
Attaching the charts to the imageData (via a loop) isn't working for me. I have class="myCharts" attached to both of my charts, but no luck. Now I'm just bacially trying to add my charts to the imageData.
<div id="allCharts" class="col-md-6" >
<button class="exportAll" onClick="printPDF()" id="export_btn">export all</button>
<highchart id="chart1" config="chartConfig" class="span9 myChart" ></highchart>
<hr>
<highchart id="chart2" config="chartConfig2" class="span9 myChart" ></highchart>
function printPDF() {
var doc = new jsPDF();
// chart height defined here so each chart can be palced
// in a different position
var chartHeight = 80;
// All units are in the set measurement for the document
// This can be changed to "pt" (points), "mm" (Default), "cm", "in"
doc.setFontSize(40);
doc.text(35, 25, "My Exported Charts");
//loop through each chart
$('.myChart').each(function (index) {
var imageData = $(this).highcharts().createCanvas();
// add image to doc, if you have lots of charts,
// you will need to check if you have gone bigger
// than a page and do doc.addPage() before adding
// another image.
/**
* addImage(imagedata, type, x, y, width, height)
*/
doc.addImage(imageData, 'JPEG', 45, (index * chartHeight) + 40, 120, chartHeight);
//add.addImage();
});
//save with name
doc.save('demo.pdf');
};
I found the functionality that I was looking for here https://github.com/pablojim/highcharts-ng/issues/185.
--moe
I am trying to prepare a Tornado Chart using the column chart in Highcharts. Here is my fiddle.
My current code is:
$('#container').highcharts({
chart: {
type: 'columnrange',
inverted: true
},
title: {
text: 'Net Sales'
},
subtitle: {
text: 'MM $'
},
xAxis: {
categories: ['Annual Revenue', 'Number of Years', 'Annual Costs']
},
yAxis: {
title: {
text: 'MM $'
}
},
plotOptions: {
columnrange: {
dataLabels: {
enabled: true,
formatter: function () {
return this.y;
}
}
},
scatter:{
marker:{
symbol:'line',
lineWidth:11,
radius:8,
lineColor:'#f00'
}
}
},
legend: {
enabled: false
},
series: [{
name: 'Temperatures',
data: [
[12.15, 46.86],
[15.45, 42.28],
[27.77, 31.24]
]
},
{
name:'Base',type: 'scatter',data:[120],
}]
});
The problem is that the last series (Annual Costs) does not show, as it is in reversed order. Also, I'd like the Tornado Chart to look more like this:
Note that the labels in this chart are different from the actual values plotted. Also note that the bar in the center - in the example code, there would be a vertical line at 29.5. I would also like to support a combined uncertainty bar like the one at the bottom. Any suggestions would be greatly appreciated.
Your last bat is not showing, because first number is lower than second, see: http://jsfiddle.net/kErPt/1/
If you want to display another values at labels, then add that info first. Example:
data: [{
low: 12,
high: 15,
lowLabel: 35,
highLabel: 46
}, {
low: 2,
high: 35,
lowLabel: 15,
highLabel: 26
} ... ]
And then use dataLabels.formatter for series.
To add vertical line use plotLines.
I'm not sure what is the last bar called 'combined uncertainty'.
I've used Highcharts with separate series (thanks jlbriggs) to create a Tornado Chart: http://jsfiddle.net/uRjBp/
var baseValue = 29.5;
var outputTitle = "Net Sales";
var chart = new Highcharts.Chart({
chart: {
renderTo:'container',
//type:'column'
//type:'area'
//type:'scatter'
//type:'bubble'
},
credits: {},
exporting: {},
legend: {},
title: {
text: outputTitle
},
subtitle: {
text: "MM $"
},
tooltip: {
formatter: function() {
var msg = "";
var index = this.series.chart.xAxis[0].categories.indexOf(this.x);
var low = round(this.series.chart.series[0].data[index].y+baseValue);
var high = round(this.series.chart.series[1].data[index].y+baseValue);
if (this.x === "Combined Uncertainty") {
msg = "Combined Uncertainty in "+outputTitle+": "+low+" to "+high;
} else {
var lowLabel = this.series.chart.series[0].data[index].label;
var highLabel = this.series.chart.series[1].data[index].label;
msg = '<b>'+outputTitle+'</b> goes from '+ low +' to '+ high+'<br/> when '+this.x +
' goes from <br/> '+lowLabel+" to "+highLabel;
}
return msg;
}
},
plotOptions: {
series: {
dataLabels: {
enabled: true,
formatter: function () {
var index = this.series.chart.xAxis[0].categories.indexOf(this.x);
if (this.series.userOptions.labels === undefined) {
return this.y+baseValue;
}
return this.key === "Combined Uncertainty" ? "":this.series.userOptions.labels[index];
}
}
}
},
xAxis: {
title: {
text: 'Factor'
},
allowDecimals:false,
categories: ['Annual Revenue', 'Number of Years', 'Annual Costs', 'Combined Uncertainty']
},
yAxis: {
title: {
text: 'MM $'
},
labels: {
formatter:function() {
return this.value+baseValue;
}
}
},
series:[{
name: 'Low',
grouping:false,
type:'bar',
data:[{y:12.15-baseValue, label:10},{y:15.45-baseValue, label:1},{y:31.25-baseValue, label:2},{y:12.15-baseValue, color:'#99CCFF', label: ""}],
labels:[10,1,2,]
},{
name: 'High',
grouping:false,
type:'bar',
data:[{y:46.86-baseValue, label:30},{y:42.28-baseValue, label:3},{y:27.77-baseValue, label:4},{y:46.86-baseValue, color:'#99CCFF', label:""}],
labels:[30,3,4,]
},
{
name: 'Median',
type: 'scatter',
data: [null,null, null,27-baseValue],
marker: {
lineWidth: 2,
lineColor: Highcharts.getOptions().colors[3],
fillColor: 'white'
}
}]
});
function round(num) {
return Math.round(num*100)/100;
}
usually, this kind of chart is done using a separate series for the left and right portions
One way to do this is by setting one set of data as negative numbers, and then using the formatters to make the axis labels, datalabels, and tooltips display the absolute values
example:
http://jsfiddle.net/jlbriggs/yPLVP/68/
UPDATE:
to show a line as in your original chart, you can extend the marker symbols to include a line type, and use a scatter series to draw that point:
http://jsfiddle.net/jlbriggs/yPLVP/69/
If you don't want to have the extra code for the line marker type, you could use any of the other existing marker symbols for the scatter series.
Can the highlighted "green" part be removed?
code:
chart = new Highcharts.StockChart({
chart: {
renderTo: renderTo,
},
title: {
text: 'test - ' + title
},
zoomType: false,
subtitle: {
text: 'some text'
},
rangeSelector: {
selected: 4
},
yAxis: {
labels: {
formatter: function() {
return (this.value / 1000000) + "mil";
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
credits: {
enabled:false
},
plotOptions: {
series: {
marker : {
enabled : true,
radius : 3
}
}
},
},
series: seriesOptions
});
Documentation is your friend.
Highstock Demo Gallery - Disabled navigator
http://www.highcharts.com/stock/demo/navigator-disabled
Necessary code:
navigator : {
enabled : false
},
Working Example: http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/stock/demo/navigator-disabled/
err...
quick and dirty
<div id="highchart">
highchart code here
</div>
<div style="background:white;display:block;position:relative;width:400px;margin-top:-150px;">
the rest of your site/content here
</div>
witht he limited amount of info you've provided this is all I can think of.