Since building charts in angular with dates on the x-axis (on a day interval) was a lot of work, we build our own easy to use and responsive chart that does all the annoying stuff for you.
We also added a really cool feature (see 'customDrawing'), where you have access to all parameters of our chart from the outside. This enables you to draw individual stuff with d3 directly on the chart, which was previously really complex and required hacking the code.
- for a quick demo, check out stackblitz
- otherwise, just clone the code and head and build it yourself :) PRs are always welcome :)
- npm
- simple bar chart with one bar per day
- bar series with multiple bars per day
- stacked bars for each day
npm install ngx-date-bar-chart
In your module add NgxDateBarChartModule to the imports:
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
NgxDateBarChartModule
],
providers: [],
bootstrap: [AppComponent]
})
You can check out more examples in the demo project, for this just head over to github and and build the demo :)
- Set up some data
const data: INgxDateValue[] = [];
const today = new Date();
for (let i = 0; i < 15; ++i) {
const date = new Date(today);
date.setDate(today.getDate() + i);
data.push({
new Date(date),
value: i * i,
});
}
- Use the chart in you html template and maybe add some nice colors
<ngx-date-bar-chart
[colors]="['navy', 'dodgerblue']"
[data]="data"
></ngx-date-bar-chart>
- If you want multiple values per day just change the type of data to 'INgxDateValueSeries[]' and you are good to go:
const data: INgxDateValueSeries[] = [];
const today = new Date();
for (let i = 0; i < 15; ++i) {
const date = new Date(today);
date.setDate(today.getDate() + i);
data.push({
new Date(date),
values: [i, 2*i, 3 * i],
});
}
- If you want your data stacked, just set the stacked attribute in the html-template.
<ngx-date-bar-chart
[colors]="['navy', 'dodgerblue']"
[data]="data"
[stacked]="true"
></ngx-date-bar-chart>
Use these inputs to style the chart the way you want it
data: INgxDateValue[] | INgxDateValueSeries[]
aka the 'input', has to be set
stacked: boolean
whether the chart is stacked or not
barSpacingPercentage: number
amount of space that is distributed between the bars as space (range from 0 to 1)
barSeriesInnersSpacing: number
only relevant for series; amount of space that is distributed between the bars for each date
rounded: boolean
: whether the bars are rounded (defaults to true)
barRadiusFunction: ((barWidth: number) => number)
use this, if you want your bar-radius to be custom
colors: string[]
set some nice colors, if more colors are present than required (e.g. single bars and two colors) they just repeat
yMax: number
set a maximum y-value (all higher values are clamped)
yMin: number
set a minimum y-value (e.g. 0, all lower values will be clamped). If the type is stacked, 0 is default
xMax: Date
set a maximum x-value (all higher values are clamped)
xMin: Date
set a minimum x-value (all lower values are clamped)
formatDateFunction: ((date: Date) => string)
use this to format the x-axis dates the way you want them
fixedXTicks: Date[]
use specific x-ticks instead of the automatically generated ones
fixedYTicks: number[]
same as x-ticks
minSpacePerXTick: number
minimum amount of space required to display an x tick (in px)
legendLabels: string[]
list of the legend labels for each data row
legendPosition: LegendPosition
position of the legend (e.g. BottomLeft (default))
xAxisLabel: string
label for the x-axis
yAxisLabel: string
label for the y-axis
xAxisHeight: number
height of the x-axis (aka space for the ticks and the x-axis-label)
yAxisWidth: number
width of the y-axis (aka space for the ticks and the y-axis-label)
fontSizeTicks: string
font size of ticks and axis-descriptions
[horizontalLine]="{yValue: 20, color: '#40088BFF', widthPx: 2}"
yValue: number (required)
color: hexString (required)
widthPx: number (required)
customDrawing: ((
boundingSvgSelection: any,
fullWidth: number,
fullHeight: number,
chartHeight: number,
chartWidth: number,
barWidth: number,
padding: { top: number; left: number; right: number; bottom: number },
xScale: any,
yScale: any,
dataSingle: INgxDateValue[],
dataSeries: INgxDateValueSeries[],
xDomain: [Date, Date],
yDomain: [number, number]
) => void
With this method you can draw on the chart yourself and access internal variables like chartWidth
or xScale
(for more info on scales consult the d3 docs).
Having access to e.g. the chart-width and chart-height enables you to draw a custom trendline.
To illustrate this functionality, the following code draws a line from the top left corner of the chart, to the top of the bar that is exactly in the middle. This makes no sense, but it demonstrates, how simple it is to add your custom stuff.
customDrawing = (
boundingSvgSelection: any,
fullWidth: number,
fullHeight: number,
chartHeight: number,
chartWidth: number,
barWidth: number,
padding: { top: number; left: number; right: number; bottom: number },
xScale: any,
yScale: any,
dataSingle: INgxDateValue[],
dataSeries: INgxDateValueSeries[],
xDomain: [Date, Date],
yDomain: [number, number]
) => {
// lazy removal of all stuff (so it won't get rendered twice
boundingSvgSelection
.selectAll('.custom-after-rendering')
.selectAll('line')
.remove();
/*
* append a red line
* use the other g containers to render the line on the level you want
* 'custom-before-rendering'
* 'custom-between-bar-and-axis'
* 'custom-after-rendering'
*/
boundingSvgSelection
.selectAll('.custom-after-rendering')
.append('line')
.style('stroke', 'red')
.style('stroke-width', 3)
.attr('x1', padding.left)
.attr('y1', padding.top)
.attr(
'x2',
padding.left +
xScale(dataSingle[Math.floor(dataSingle.length / 2)].date)
)
.attr(
'y2',
padding.top +
yScale(dataSingle[Math.floor(dataSingle.length / 2)].value)
);
};
All you need to do now, is to pass the function in the html template like this: [customDrawing]="customDrawing"
:)