I have a bar chart built with amcharts. I built the stacked bar chart following this example.
I am trying to change the alpha (or the color) of a box if I am hovering over some other element in my page (for example a table). For example, I would like to change alpha (or color) of the box for (2003, Europe) if a user hovers on the corresponding table cell somewhere on my page.
I have placed my chart in an angular 6 component but I guess this does not really matter as long as I capture the input change events properly.
EDIT 1.
I am still struggling navigating the objects. I build a bar chart and it looks OK
createSeries(field, name) {
var series = this.chart.series.push(new am4charts.ColumnSeries());
series.name = name;
series.dataFields.valueY = field;
series.dataFields.categoryX = "country";
series.sequencedInterpolation = true;
series.columns.template.fillOpacity = 0.5
// Make it stacked
series.stacked = true;
// Configure columns
series.columns.template.width = am4core.percent(60);
series.columns.template.tooltipText = "[bold]{name}[/]\n[font-size:14px]{categoryX}: {valueY}";
console.log(series)
return series
}
I have then four column series, one for each field in my data. I can gran the series by field.
grabBar(country, field) {
// If you don't have access to the chart code in your angular scope,
// use am4core global.
// no reason to loop through ALL series/columns, just whipped this up quick
let foundBar: am4charts.XYSeries | null;
this.chart.series.each(series => {
if (series.dataFields.categoryY == field) {
console.log("yay")
series.dataItems.values
}
});
return foundBar;
but I cannot figure out a way to access the element that corresponds to a country and a specific field. What I see is four, one for each field, ColumnSeries
that do not have any columns
property. My am4core.registry.baseSprites[0]
has no series
in there.
This is my full code:
import { Component, OnInit, NgZone, Input } from '@angular/core';
import * as am4core from "@amcharts/amcharts4/core";
import * as am4charts from "@amcharts/amcharts4/charts";
import am4themes_animated from "@amcharts/amcharts4/themes/animated";
import { CountryItinerary, Team } from "../model/classes"
am4core.useTheme(am4themes_animated);
@Component({
selector: 'barchart-distance',
templateUrl: './barchart-distance.component.html',
styleUrls: ['./barchart-distance.component.scss']
})
export class BarchartDistanceComponent implements OnInit {
constructor(private zone: NgZone) { }
_selectedTeam: Team | null
@Input()
set selectedTeam(team: Team | null) {
this._selectedTeam = team;
if (team) {
this.changeColorToBar(team.isocode, "week_1", 1.0)
}
}
chart: am4charts.XYChart
categoryAxis: any
valueAxis: any
ngOnInit() {
}
grabBar(country, weekId) {
// If you don't have access to the chart code in your angular scope,
// use am4core global.
// no reason to loop through ALL series/columns, just whipped this up quick
let foundBar: am4charts.XYSeries | null;
this.chart.series.each(series => {
if (series.name == "Week_2") {
console.log(series.dataItem)
}
});
return foundBar;
}
changeColorToBar(country, weekId, opacity) {
let bar = this.grabBar(country, weekId)
if (bar) {
bar.background.fillOpacity = opacity
}
}
createSeries(field, name) {
var series = this.chart.series.push(new am4charts.ColumnSeries());
series.name = name;
series.dataFields.valueY = field;
series.dataFields.categoryX = "country";
series.sequencedInterpolation = true;
series.columns.template.fillOpacity = 0.5
// Make it stacked
series.stacked = true;
// Configure columns
series.columns.template.width = am4core.percent(60);
series.columns.template.tooltipText = "[bold]{name}[/]\n[font-size:14px]{categoryX}: {valueY}";
console.log(series)
return series
}
drawWithData() {
this.zone.runOutsideAngular(() => {
this.chart = am4core.create("chartdiv2", am4charts.XYChart);
let data = [
new CountryItinerary("Italy", "IT", [10, 15, 15, 22]),
new CountryItinerary("Germany", "DE", [20, 30, 40, 24]),
new CountryItinerary("Greece", "GR", [13, 10, 20, 15])
]
this.chart.data = data.map ((el) => {
let newEl = { "country": el.isocode }
el.distances.forEach((item, index) => {
newEl["week_" + index] = item
})
return newEl
})
console.log(this.chart.data)
this.categoryAxis = this.chart.xAxes.push(new am4charts.CategoryAxis());
this.categoryAxis.dataFields.category = "country";
this.categoryAxis.renderer.grid.template.location = 0;
this.valueAxis = this.chart.yAxes.push(new am4charts.ValueAxis());
this.valueAxis.renderer.inside = true;
this.valueAxis.renderer.labels.template.disabled = true;
this.valueAxis.min = 0;
let numberOfTrips = data[0].distances.length
for (let i = 0; i < numberOfTrips; i++) {
this.createSeries("week_" + i, "Week_" + i)
}
console.log(this.chart.series)
})
}
ngAfterViewInit() {
this.drawWithData()
}
}
Up there, series.dataItem.dataContext
is undefined.
(Adding a different answer after he provided actual angular code and where things are hiccing up for him, my previous answer seems far less relevant.)
I passed my own method as the Input()
to the table component, but made sure to use an arrow function so it retains the scope of this chart component:
selectTeam = (team, weekN) => {
this._selectedTeam = team;
if (team) {
this.changeColorToBar(team.isocode, weekN, 1.0);
}
}
As for the column itself, since the value for the property you're looking to change may also change, maybe it makes less sense to use States and actually adjust properties directly on the object. In this case you would change fillOpacity
directly, not on the column's background
:
changeColorToBar(country, weekId, opacity) {
let bar = this.grabBar(country, weekId);
if (bar && bar !== this.currentBar) {
if (this.currentBar) this.currentBar.fillOpacity = 0.5; // Default
bar.fillOpacity = opacity;
this.currentBar = bar;
}
}
I had no issues finding the chart, its series, and columns. Timing of course may be an issue, i.e. the chart and series have to be actually loaded, but that will probably have happened by the time the user hovered:
grabBar(country, weekId = 0) {
const totalSeries = this.chart.series.length;
for (let i = 0; i < totalSeries; ++i) {
const series = this.chart.series.getIndex(i);
if (series.name === "Week_" + weekId) {
const totalColumns = series.columns.length;
for (let c = 0; c < totalColumns; ++c) {
const column = series.columns.getIndex(c);
if (
column.dataItem.dataContext.country === country
) {
return column;
}
}
}
}
}
Here's a demo:
https://stackblitz.com/edit/so-55597531-table-input-chart
If you're still not able to find columns within series and what not, perhaps put up a sample of your project on StackBlitz so we can try and see where things are going wrong.