I'm using HighCharts and the Angular highcharts-ng directive. And most actions I take have a high likely hood of causing the price line vanish, or rather actually, not display.
^ The above % are based on feeling :(
^ Also note that the line graph inside of the focus timeline navigator also disappears. Also if you hover over the chart, the points for the missing line show up.
Below is what the chart should look like (however the focus line is gone below as well):
The chart I'm using is a type of multi chart where I have 1 line graph(stock price) and up to 3 area graphs charting other data. I'm also using highstocks 4.2.0. Our app is quite complicated and hard to replicate in plunkr.
generateChartObject();
function generateChartObject() {
// Create the chart
vs.chartConfig = {
options: {
ignoreHiddenSeries: false,
credits: { enabled: true, text: 'tickertags' },
legend: {
itemStyle: {
color: "#333333",
cursor: "pointer",
fontSize: "10px",
fontWeight: "normal"
},
enabled: true,
floating: true,
align: 'left',
verticalAlign: 'top',
x: 60
},
chart : {
title: { text: '' },
subtitle: { text: '' },
renderTo: 'chart1',
zoomType: 'x',
events: {
load: function () {
broadcastChartloaded();
vs.chartObject.hideLoading();
}
}
},
scrollbar: {
enabled: false,
liveRedraw: false
},
navigator : {
enabled: true,
adaptToUpdatedData: true,
series : {
// data : quote_data
}
},
rangeSelector: {
enabled: false,
},
tooltip: {
pointFormatter: tooltipFormatter,
shared: true
},
exporting: { enabled: false },
plotOptions: {
series: {
point: {
events: {
click: afterClick,
}
}
},
area: {
stacking: 'normal',
},
column: {
stacking: 'normal',
}
}
},
exporting: { enabled: false },
useHighStocks: true,
xAxis : {
dateTimeLabelFormats : {
hour: '%I %p',
minute: '%I:%M %p'
},
events : {
afterSetExtremes : afterSetExtremes,
setExtremes : setExtremes
},
minRange: 3600 * 1000 // one hour
},
yAxis: [{ // Primary yAxis
labels: {
format: '${value:.2f}',
style: {
color: '#4C73FF',
}
},
title: {
text: 'Price',
style: {
color: '#4C73FF',
}
}
},
{ // Secondary yAxis
gridLineWidth: 0,
title: {
text: 'Mentions',
style: {
color: '#FDE18D'
}
},
labels: {
formatter: volumeFormatter,
style: {
color: '#FDE18D'
}
},
opposite: false
}],
func: function(chart) {
vs.chartObject = chart;
}
};
}
How my chart is generated:
Here in my tickersController I broadcast which ticker to graph a line for out to the chartController:
$scope.$emit("display.chart", vm.ticker.ticker);
My highChartsController receives the event and starts it's work:
$scope.$on("display.chart", function(event, ticker) {
displayChart(ticker)
.then(function (data) {
// console.log('"displayChart TICKER data returned"',data);
vs.chartObject.addSeries({
zIndex: 1000,
showInLegend: true,
yAxis: 0,
type: 'line',
name: ticker,
color: '#4C73FF',
data: data,
dataGrouping: {
enabled: true
}
}, true);
checkForTags();
}).catch(function(error) {
console.error('error', error);
});
}
);
Here is the displayChart function with a Promise waiting on an API GET (The promise returns with the data in quote_data):
function displayChart(ticker) {
console.log('displayChart()');
var deferred = $q.defer(),
promise;
if (ticker === undefined) ticker = 'SPY';
vs.ticker = ticker; // Bugged, sometimes ticker is an Object
vs._ticker = ticker; // Quick fix
symbolUrls = {};
var url = '/app/api/tickers/quotes/'+ticker;
symbolUrls[ticker] = url;
promise = ApiFactory.quotes(buildFullUrl('quotes', url)).then(function (data) {
var quote_data = formatQuotes(data, 'quotes');
// Remove any old chart series:
if (!_.isEmpty(vs.chartObject)) {
while(vs.chartObject.series.length > 0)
vs.chartObject.series[0].remove(true);
}
deferred.resolve(quote_data);
});
return deferred.promise;
}
Now back in my eventListener $scope.$on("display.chart", function(event, ticker) {
After displayChart returns with the Promise and the data, I add the series, then call checkForTags()
:
displayChart(ticker)
.then(function (data) {
// console.log('"displayChart TICKER data returned"',data);
vs.chartObject.addSeries({
zIndex: 1000,
showInLegend: true,
yAxis: 0,
type: 'line',
name: ticker,
color: '#4C73FF',
data: data,
dataGrouping: {
enabled: true
}
}, true);
checkForTags();
function checkForTags() {
var tags = TagFactory.retrieveTickerTags('all');
// Up to 3 tags in the tags Array (The Area graphs):
if (tags.length) {
getTickerTags(tags);
}
}
function getTickerTags(tags) {
prepareTags(tags).then(function(data) {
// Once data comes back from prepareTags, the loop below,
// generates up to 3 tag Area Graphs in the chart:
for (var i = data.length - 1; i >= 0; i--) {
vs.chartObject.addSeries({
showInLegend: true,
zIndex: 900,
yAxis: 1,
type: 'area',
// name: term,
color: lookupColors[i],
data: data[i],
dataGrouping: {
enabled: false
}
}, true);
if (i === 0) {
handleRedraw(); // function below
}
}
})
.catch(function(error) {
console.error('error', error);
});
}
function prepareTags(tags) {
var deferred = $q.defer();
var tagsComplete = 0;
var allTags = [];
// Request ALL Tag series quote data:
for (var i=0; i<tags.length; i++) {
var url = '/app/api/twitter/' + tags[i].term_id;
getTagData(url, tags[i].term, term_positions[tags[i].term_id]).then(function(data) {
// Push tag data into allTags Array:
allTags.push( data );
tagsComplete++;
// When loop is complete, resolve all the Promises:
if (tagsComplete === tags.length) {
deferred.resolve(allTags);
}
});
}
return deferred.promise;
}
function getTagData(url, term, i) {
var e = vs.chartObject.xAxis[0].getExtremes();
var final_url = buildFullUrl(url, Math.round(e.min/1000), Math.round(e.max/1000));
// Response returned:
return ApiFactory.quotes(final_url).then(function(data) {
// Formatted data returned:
return formatQuotes(data);
});
}
// Finally the handlRedraw function is called at the end:
function handleRedraw() {
vs.chartObject.hideLoading();
vs.chartObject.redraw();
}
Below is the window resize function with broadcasts an event to the chartController, then restoreChartSize is hit then finally changePeriodicity:
window.onresize = function(event) {
$scope.$emit("window.resize");
$scope.$emit("restore.chart");
};
function restoreChartSize() {
if (!vs.chartObject.reflowNow) {
vs.chartObject.reflowNow = vs.chartObject.reflowNow = function() {
this.containerHeight = this.options.chart.height || $(this.renderTo).height();
this.containerWidth = this.options.chart.width || $(this.renderTo).width();
this.setSize(this.containerWidth, this.containerHeight, true);
this.hasUserSize = null;
}
}
vs.chartObject.reflowNow();
changePeriodicity(vs.interval);
}
function changePeriodicity(newInterval) {
vs.interval = newInterval;
var rightDate = new Date();
var leftDate = new Date();
if ( newInterval == "hour" ) {
leftDate.setHours(rightDate.getHours()-1);
}
else if ( newInterval == "day" ) {
leftDate.setDate(rightDate.getDate()-1);
}
else if ( newInterval == "week" ) {
leftDate.setDate(rightDate.getDate()-7);
}
else if ( newInterval == "month" ) {
leftDate.setDate(rightDate.getDate()-30);
}
else if ( newInterval == "year" ) {
leftDate.setDate(rightDate.getDate()-365);
}
if (vs.chartObject.xAxis) {
vs.chartObject.showLoading('Loading data...');
vs.chartObject.xAxis[0].setExtremes(leftDate.getTime(), rightDate.getTime());
}
// if (vs.chartObject.loadingShown) {
// handleRedraw();
// }
}
Any thoughts or directions here are appreciated!
We finally frigging figured it out!!!
It was this tooltipFormatter
function that was screwing everything up for some reason.
After commenting it out, we can't get the Price line to disappear anymore! Still no idea why the bug happened in the first place though :o here is the tooltipFormatter
function:
function tooltipFormatter(that) {
if (this.color === "#4C73FF" && this.y > 0) {
this.y = '$' + this.y.formatMoney(2);
} else if (this.y > 999) {
this.y = this.y.formatCommas();
}
return '<span style="color:' + this.color + '">\u25CF</span> ' + this.series.name + ': <b>' + this.y + '</b><br/>';
}