I have a nvd3 line chart where the x axis ticks don't match or align with the data points.
When i hover on Jul 2021 data point, the tooltip for sep 2021 is shown. Also some of the ticks are not shown at all. The line this.tickLength = this.ChartInputData[0].values.length; value is set to the ticks.
line chart html
<div class="row">
<div class="col-12">
<div style="margin-left: 4em; display: inline-flex; align-items: center;">
<label class="toyota-red bold-font margin-B0" for="">Y-Axis Format:</label>
<mat-button-toggle-group class="margin-L15" [(ngModel)]="forceZeroTo" (change)="expandCollapse($event)"
#group="matButtonToggleGroup">
<mat-button-toggle *ngIf="minMaxValue === 100" matRipple [matRippleColor]="'#c0000047'"
[matRippleUnbounded]="false" [value]="100"> 100% </mat-button-toggle>
<mat-button-toggle *ngIf="minMaxValue === 150" matRipple [matRippleColor]="'#c0000047'"
[matRippleUnbounded]="false" [value]="150"> 150% </mat-button-toggle>
<mat-button-toggle matRipple [matRippleColor]="'#c0000047'" [matRippleUnbounded]="false" [value]="0">Min-Max
Scale
</mat-button-toggle>
</mat-button-toggle-group>
</div>
</div>
<div class="col-12">
<nvd3 *ngIf="showMultiLineChart" #nvd3Chart [options]="multiLineChartOptions" [data]="chartData"></nvd3>
</div>
</div>
Line Chart component
import { DatePipe } from '@angular/common';
declare const $: any;
export interface ChartLabels {
labels: string;
}
@Component({
selector: 'app-line-chart',
templateUrl: './line-chart.component.html',
styleUrls: ['./line-chart.component.scss']
})
export class LineChartComponent implements OnInit, OnChanges {
@ViewChild('nvd3') nvd3Chart;
@Input('chartId') chartId: number;
@Input('dimensionLabel') dimensionLabel: string;
@Input('chartKey') chartKey: string;
@Input('xAxis') xAxis: string;
@Input('yAxis') yAxis: string;
@Input('width') width: string;
@Input('ChartInputData') ChartInputData: any;
@Input('resizeChart') resizeChart: string;
@Input('minMaxValue') minMaxValue: number;
@Input('showPercentage') showPercentage: boolean;
@Input('showDollar') showDollar: boolean;
public multiLineChartOptions;
public chartData: any;
public data;
public chartLabelsList: any;
public tickLength: number;
public showMultiLineChart = false;
customHeight: number;
forceZeroTo: number;
constructor() { }
formatDate(e) {
const datePipe = new DatePipe('en-US');
return datePipe.transform(e, 'MM-dd-yyyy');
}
public getLabels() {
this.chartLabelsList = [];
let chartLabels: ChartLabels;
for (let i = 0; i < this.chartData[0].values.length; i++) {
chartLabels = {
labels: this.chartData[0].values[i].label
};
this.chartLabelsList.push(chartLabels.labels);
}
return this.chartLabelsList;
}
/* to split the axis label and show ellipse*/
ellipseLabel(data) {
if (data) {
length = data.length;
if (length > 10) {
return data.substring(0, 7) + '...';
} else {
return data;
}
}
}
expandCollapse(event) {
this.forceZeroTo = event.value;
this.generateChart(this.showPercentage, this.showDollar);
}
ngOnChanges(changes) {
this.chartData = this.ChartInputData;
this.generateChart(this.showPercentage, this.showDollar);
setTimeout(() => {
const resizeEvent = window.document.createEvent('UIEvents');
resizeEvent.initEvent('resize', true, false);
window.dispatchEvent(resizeEvent);
}, 100);
}
ngOnInit() {
this.forceZeroTo = this.minMaxValue;
this.generateChart(this.showPercentage, this.showDollar);
this.chartData = this.ChartInputData;
this.delayTimer();
}
delayTimer() {
setTimeout(() => {
this.showMultiLineChart = true;
}, 100);
}
generateChart(showPercentage: boolean, showDollar: boolean) {
this.getLabels();
this.tickLength = this.ChartInputData[0].values.length;
this.multiLineChartOptions = {
chart: {
type: 'lineChart',
height: 350,
margin: {
top: 20,
right: 20,
bottom: 75,
left: 85
},
x: function (d) {
return d.index;
},
y: function (d) {
return Number(d.value);
},
showLegend: this.checkLegend(this.ChartInputData),
forceY: this.checkMinMaxValue(),
useInteractiveGuideline: false,
dispatch: {
stateChange: function (e) {
console.log('stateChange');
},
changeState: function (e) {
console.log('changeState');
},
tooltipShow: function (e) {
console.log('tooltipShow');
},
tooltipHide: function (e) {
console.log('tooltipHide');
}
},
xAxis: {
showMaxMin: false,
rotateLabels: -45,
tickFormat: (d, i = 0) => {
if (i <= this.ChartInputData[0].values.length) {
return this.chartLabelsList[i];
}
},
tickValues: this.tickLength
},
yAxis: {
tickFormat: function (d) {
if (showPercentage) {
return d3.format(',')(d.toFixed(2)) + ' %';
} else if (showDollar) {
return '$' + d3.format(',')(d);
} else {
return d3.format(',')(d);
}
},
axisLabelDistance: -10
},
tooltip: {
contentGenerator: function (e) {
let value;
if (showPercentage) {
value = e.point.value + ' %';
} else if (showDollar) {
value = e.point.value + ' $';
} else {
value = d3.format(',')(e.point.value);
}
return (
'<table><tr><td class="legend-color-guide"><div style="background-color:' +
e.point.color +
'"></div></td><td class="key">' +
e.series[0].key +
'</td><td class="key">' +
e.point.label +
'</td><td class="value">' +
value +
'</td></tr></table>'
);
}
},
callback: function (chart) { }
}
};
}
checkMinMaxValue() {
if (this.forceZeroTo === 100) {
return [0, 100]
} if (this.forceZeroTo === 150) {
return [0, 150]
} else {
return []
}
}
checkLegend(data: any) {
if (data && data.length === 1) {
return false;
} else {
return true;
}
}
}
methods tried
1.setting false to showMaxMin -> didnt work
I tried to set true instead of false to showMaxMin and set the rotateLabels: -70 the first data point Aug 2021 is repeated at the end.
2. using tickvalues instead of ticks --> didnt work
only first and last tick values are shown and first tick is repeated at the end


