Open psiedlinski opened 7 years ago
This is exactly what I'm looking for as well, have you implemented a workaround for it?
@delsner No, I didn't, sorry. I don't think workarounds are worth doing in this specific case. Maybe few more votes and somebody pays attention and simply develop it
Actually, I found an easy workaround by simply extending the existing line-chart
and circle-series
. See line-chart and circle-series for the code of the parent components. In case this issue gets more attention I might put more thought into this and create a PR, but I think for a lot of cases it's easier to extend and customize the existing components.
// custom-chart.component.ts
@Component({
selector: 'custom-line-chart',
template: `
<ngx-charts-chart
[view]="[width, height]"
[showLegend]="legend"
[legendOptions]="legendOptions"
[activeEntries]="activeEntries"
[animations]="animations"
(legendLabelClick)="onClick($event)"
(legendLabelActivate)="onActivate($event)"
(legendLabelDeactivate)="onDeactivate($event)">
<svg:defs>
<svg:clipPath [attr.id]="clipPathId">
<svg:rect
[attr.width]="dims.width + 10"
[attr.height]="dims.height + 10"
[attr.transform]="'translate(-5, -5)'"/>
</svg:clipPath>
</svg:defs>
<svg:g [attr.transform]="transform" class="line-chart chart">
<svg:g ngx-charts-x-axis
*ngIf="xAxis"
[xScale]="xScale"
[dims]="dims"
[showGridLines]="showGridLines"
[showLabel]="showXAxisLabel"
[labelText]="xAxisLabel"
[tickFormatting]="xAxisTickFormatting"
(dimensionsChanged)="updateXAxisHeight($event)">
</svg:g>
<svg:g ngx-charts-y-axis
*ngIf="yAxis"
[yScale]="yScale"
[dims]="dims"
[showGridLines]="showGridLines"
[showLabel]="showYAxisLabel"
[labelText]="yAxisLabel"
[tickFormatting]="yAxisTickFormatting"
[referenceLines]="filteredReferenceLines"
[showRefLines]="showRefLines"
[showRefLabels]="showRefLabels"
(dimensionsChanged)="updateYAxisWidth($event)">
</svg:g>
<svg:g [attr.clip-path]="clipPath">
<svg:g *ngFor="let series of results; trackBy:trackBy" [@animationState]="'active'">
<svg:g ngx-charts-line-series
[xScale]="xScale"
[yScale]="yScale"
[colors]="colors"
[data]="series"
[activeEntries]="activeEntries"
[scaleType]="scaleType"
[curve]="curve"
[rangeFillOpacity]="rangeFillOpacity"
[hasRange]="hasRange"
[animations]="animations"
/>
</svg:g>
<svg:g *ngIf="!tooltipDisabled" (mouseleave)="hideCircles()">
<svg:g ngx-charts-tooltip-area
[dims]="dims"
[xSet]="xSet"
[xScale]="xScale"
[yScale]="yScale"
[results]="results"
[colors]="colors"
[tooltipDisabled]="tooltipDisabled"
[tooltipTemplate]="seriesTooltipTemplate"
(hover)="updateHoveredVertical($event)"
/>
<!-- ############### START NEW PART ############### -->
<svg:g *ngIf="customSeries">
<svg:g custom-circle-series
[xScale]="xScale"
[yScale]="yScale"
[colors]="colors"
[data]="customSeries"
[scaleType]="scaleType"
[visibleValue]="hoveredVertical"
[activeEntries]="activeEntries"
[tooltipDisabled]="true"
(select)="onClick($event, customSeries)"
(activate)="onActivate($event)"
(deactivate)="onDeactivate($event)"
/>
</svg:g>
<!-- ############### END ############### -->
<svg:g *ngFor="let series of results">
<svg:g ngx-charts-circle-series
[xScale]="xScale"
[yScale]="yScale"
[colors]="colors"
[data]="series"
[scaleType]="scaleType"
[visibleValue]="hoveredVertical"
[activeEntries]="activeEntries"
[tooltipDisabled]="tooltipDisabled"
[tooltipTemplate]="tooltipTemplate"
(select)="onClick($event, series)"
(activate)="onActivate($event)"
(deactivate)="onDeactivate($event)"
/>
</svg:g>
</svg:g>
</svg:g>
</svg:g>
<svg:g ngx-charts-timeline
*ngIf="timeline && scaleType === 'time'"
[attr.transform]="timelineTransform"
[results]="results"
[view]="[timelineWidth, height]"
[height]="timelineHeight"
[scheme]="scheme"
[customColors]="customColors"
[scaleType]="scaleType"
[legend]="legend"
(onDomainChange)="updateDomain($event)">
<svg:g *ngFor="let series of results; trackBy:trackBy">
<svg:g ngx-charts-line-series
[xScale]="timelineXScale"
[yScale]="timelineYScale"
[colors]="colors"
[data]="series"
[scaleType]="scaleType"
[curve]="curve"
[hasRange]="hasRange"
[animations]="animations"
/>
</svg:g>
</svg:g>
</ngx-charts-chart>
`,
encapsulation: ViewEncapsulation.None,
styleUrls: ['./custom-chart.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
trigger('animationState', [
transition(':leave', [
style({
opacity: 1,
}),
animate(500, style({
opacity: 0
}))
])
])
]
})
export class CustomChartComponent extends LineChartComponent {
@Input() customSeries: any;
// ...
}
// custom-circle-series.component.ts
@Component({
selector: 'g[custom-circle-series]',
template: `
<svg:g *ngFor="let circle of circles">
<defs>
<svg:g ngx-charts-svg-linear-gradient
orientation="vertical"
[name]="gradientId"
[stops]="circle.gradientStops"
/>
</defs>
<svg:g ngx-charts-circle
class="circle"
[cx]="circle.cx"
[cy]="circle.cy"
[r]="circle.radius"
[fill]="circle.color"
[class.active]="true"
[pointerEvents]="circle.value === 0 ? 'none': 'all'"
[data]="circle.value"
[classNames]="circle.classNames"
(select)="onClick($event, circle.label)"
(activate)="activateCircle()"
(deactivate)="deactivateCircle()"
ngx-tooltip
[tooltipDisabled]="true"
[tooltipPlacement]="'top'"
[tooltipType]="'tooltip'"
[tooltipTitle]="tooltipTemplate ? undefined : getTooltipText(circle)"
[tooltipTemplate]="tooltipTemplate"
[tooltipContext]="circle.data"
/>
</svg:g>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
trigger('animationState', [
transition(':enter', [
style({
opacity: 0,
}),
animate(250, style({opacity: 1}))
])
])
]
})
export class CustomCircleSeriesComponent extends CircleSeriesComponent {
getCircles(): any[] {
if (!this.data || !this.data.name) {
return [];
}
const seriesName = this.data.name;
return this.data.series.map((d, i) => {
const value = d.value;
const label = d.name;
const tooltipLabel = formatLabel(label);
if (value) {
let cx;
if (this.scaleType === 'time') {
cx = this.xScale(label);
} else if (this.scaleType === 'linear') {
cx = this.xScale(Number(label));
} else {
cx = this.xScale(label);
}
const cy = this.yScale(this.type === 'standard' ? value : d.d1);
const radius = 5;
const height = this.yScale.range()[0] - cy;
let opacity = 1;
if (label && this.visibleValue && label.toString() === this.visibleValue.toString()) {
opacity = 1;
}
let color;
if (this.colors.scaleType === 'linear') {
if (this.type === 'standard') {
color = this.colors.getColor(value);
} else {
color = this.colors.getColor(d.d1);
}
} else {
color = this.colors.getColor(seriesName);
}
const data = {
series: seriesName,
value,
name: label
};
return {
classNames: [`circle-data-${i}`],
value,
label,
data,
cx,
cy,
radius,
height,
tooltipLabel,
color,
opacity,
seriesName,
gradientStops: this.getGradientStops(color),
min: d.min,
max: d.max
};
}
}).filter((circle) => circle !== undefined);
}
}
@delsner Thanks a lot, I will take a look in a free moment and see if it works for me!
Does anyone know if this feature has been added yet?
I kind of used one different approach to achieve something similar, In my case i need to highlight few points of line chart in red and few in green,
What i did was,
this._values = [...data, highlightSeries]
Yes, i created one extra line series with only the highlight points. And i use some conditions in my line chart to not to draw line for this extra series.
const createHighlightSeries = () => {
const series = []
data.forEach((lineData) => {
lineData.series.forEach((d) => {
if (d.hasFav) {
series.push({
name: d.name,
value: d.value,
})
}
})
})
return {
name: 'no_line',
series: series,
}
}
<ngx-charts-line-chart
#chart
[results]=[...values, createHighlightSeries()]
...
>
</ngx-charts-line-chart>
And here https://github.com/swimlane/ngx-charts/issues/467#issuecomment-645795010, i made few changes to not to display line in svg for this new series
import { Injectable } from '@angular/core';
@Injectable()
export class CustomLinerChartService {
/**
* custom: override SVG to have the dots display all the time over the liner chart
* since it's not supported anymore from ngx chart
*/
showDots(chart) {
let index = 0;
const paths = chart.chartElement.nativeElement.getElementsByClassName(
'line-series'
);
const color = chart.chartElement.nativeElement.getElementsByClassName(
'line-highlight'
);
no_line_index = 2
for (let path of paths) {
const chrtColor = color[index].getAttribute('ng-reflect-fill');
const pathElement = path.getElementsByTagName('path')[0];
const pathAttributes = {
...(no_line_index===index ? { stroke: 'none' } : {}),
'marker-start': `url(#dot${index})`,
'marker-mid': `url(#dot${index})`,
'marker-end': `url(#dot${index})`
};
this.createMarker(chart, no_line_index===index ? chrtColor : 'red', index);
this.setAttributes(pathElement, pathAttributes);
index += 1;
}
}
/**
* create marker
*
*/
createMarker(chart, color, index) {
const svg = chart.chartElement.nativeElement.getElementsByTagName('svg');
var marker = document.createElementNS(
'http://www.w3.org/2000/svg',
'marker'
);
var circle = document.createElementNS(
'http://www.w3.org/2000/svg',
'circle'
);
svg[0].getElementsByTagName('defs')[0].append(marker);
marker.append(circle);
const m = svg[0].getElementsByTagName('marker')[0];
const c = svg[0].getElementsByTagName('circle')[0];
const markerAttributes = {
id: `dot${index}`,
viewBox: '0 0 10 10',
refX: 5,
refY: 5,
markerWidth: 5,
markerHeight: 5
};
const circleAttributes = {
cx: 5,
cy: 5,
r: 5,
fill: color
};
m.append(circle);
this.setAttributes(m, markerAttributes);
this.setAttributes(c, circleAttributes);
}
/**
* set multiple attributes
*/
setAttributes(element, attributes) {
for (const key in attributes) {
element.setAttribute(key, attributes[key]);
}
}
}
I'm submitting a ... (check one with "x")
Current behavior The "point" color is the same as the line color and can't be customized.
Expected behavior That would be great if series could have one more parameter to represent given point's color, like in the attachment.
What is the motivation / use case for changing the behavior? More flexibility. An example use case: One may like to present server's response times in a period of time, and whenever the response code is not HTTP-200, he would like the point to be red (when highlighting/hoovering).