Skip to content

Commit bf03cd6

Browse files
authored
Merge pull request #453 from SparkyTD/function-serializer
Add custom JsonConverters to ensure that JS function strings are evaluated into function when deserialized by JS
2 parents 3b0d0f3 + f92451d commit bf03cd6

File tree

5 files changed

+101
-1
lines changed

5 files changed

+101
-1
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace ApexCharts.Internal;
6+
7+
/// <summary>
8+
/// Ensures that JS functions are serialized with the key '@eval' so they can be appropriately evaluated on the client side
9+
/// Example:
10+
/// <code>
11+
/// myFunction: {
12+
/// "@eval": "function(value) { return value; }"
13+
/// }
14+
/// </code>
15+
/// </summary>
16+
internal class FunctionStringConverter : JsonConverter<string>
17+
{
18+
public override bool CanConvert(Type typeToConvert) => true;
19+
20+
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
21+
{
22+
throw new NotImplementedException();
23+
}
24+
25+
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
26+
{
27+
writer.WriteStartObject();
28+
writer.WritePropertyName("@eval");
29+
writer.WriteStringValue(value);
30+
writer.WriteEndObject();
31+
}
32+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.Json;
4+
using System.Text.Json.Serialization;
5+
6+
namespace ApexCharts.Internal;
7+
8+
/// <summary>
9+
/// Ensures that JS function arrays are serialized with the key '@eval' so they can be appropriately evaluated on the client side
10+
/// Example:
11+
/// <code>
12+
/// myFunction: {
13+
/// "@eval": [ "function(value) { return value; }" ]
14+
/// }
15+
/// </code>
16+
/// </summary>
17+
internal class FunctionValueOrListConverterConverter : JsonConverter<CustomFunction>
18+
{
19+
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(CustomFunction);
20+
21+
public override CustomFunction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
22+
{
23+
throw new NotImplementedException();
24+
}
25+
26+
public override void Write(Utf8JsonWriter writer, CustomFunction value, JsonSerializerOptions options)
27+
{
28+
writer.WriteStartObject();
29+
writer.WritePropertyName("@eval");
30+
if (value == null || value.Count == 0)
31+
JsonSerializer.Serialize(writer, null, options);
32+
else if (value.Count == 1)
33+
JsonSerializer.Serialize(writer, value[0], options);
34+
else
35+
JsonSerializer.Serialize<List<string>>(writer, value, options);
36+
writer.WriteEndObject();
37+
}
38+
}

src/Blazor-ApexCharts/Models/ApexChartOptions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Runtime.Serialization;
44
using System.Text.Json.Serialization;
5+
using ApexCharts.Internal;
56

67
namespace ApexCharts
78
{
@@ -381,16 +382,19 @@ public class AnnotationsPoint : IAnnotation
381382
/// <summary>
382383
/// Click function
383384
/// </summary>
385+
[JsonConverter(typeof(FunctionStringConverter))]
384386
public string Click { get; internal set; }
385387

386388
/// <summary>
387389
/// Mouse Enter function
388390
/// </summary>
391+
[JsonConverter(typeof(FunctionStringConverter))]
389392
public string MouseEnter { get; internal set; }
390393

391394
/// <summary>
392395
/// Mouse Leave function
393396
/// </summary>
397+
[JsonConverter(typeof(FunctionStringConverter))]
394398
public string MouseLeave { get; internal set; }
395399

396400

@@ -501,16 +505,19 @@ public class Label
501505
/// <summary>
502506
/// Click function
503507
/// </summary>
508+
[JsonConverter(typeof(FunctionStringConverter))]
504509
public string Click { get; internal set; }
505510

506511
/// <summary>
507512
/// Mouse Enter function
508513
/// </summary>
514+
[JsonConverter(typeof(FunctionStringConverter))]
509515
public string MouseEnter { get; internal set; }
510516

511517
/// <summary>
512518
/// Mouse Leave function
513519
/// </summary>
520+
[JsonConverter(typeof(FunctionStringConverter))]
514521
public string MouseLeave { get; internal set; }
515522

516523
internal void SetEventFunction(AnnotationEventType eventType)
@@ -1105,6 +1112,7 @@ public class ExportCSV
11051112
/// <summary>
11061113
/// If timestamps are provided as X values, those timestamps can be formatted to convert them to date strings.
11071114
/// </summary>
1115+
[JsonConverter(typeof(FunctionStringConverter))]
11081116
public string DateFormatter { get; set; }
11091117
}
11101118

@@ -1611,6 +1619,7 @@ public class ToolCustomIcon
16111619
/// Javascript function when the icon is clicked
16121620
/// if a OnClick callback is registered this will be overwritten.
16131621
/// </summary>
1622+
[JsonConverter(typeof(FunctionStringConverter))]
16141623
public string Click { get; set; }
16151624

16161625
/// <summary>
@@ -1768,6 +1777,7 @@ public class DataLabels
17681777
///
17691778
/// In the code above, seriesIndex is useful in multi-series chart, while dataPointIndex is the index of data-point in that series. w is an object consisting all globals and configuration which can be utilized the way mentioned in the above code.
17701779
/// </summary>
1780+
[JsonConverter(typeof(FunctionStringConverter))]
17711781
public string Formatter { get; set; }
17721782
}
17731783

@@ -2262,6 +2272,7 @@ public class Legend
22622272
/// },
22632273
/// </code>
22642274
/// </summary>
2275+
[JsonConverter(typeof(FunctionStringConverter))]
22652276
public string Formatter { get; set; }
22662277

22672278
/// <summary>
@@ -2278,6 +2289,7 @@ public class Legend
22782289
/// <remarks>
22792290
/// Note: This feature is only available in shared tooltips (when you have <see cref="Tooltip.Shared"/>: <see langword="true"/>).
22802291
/// </remarks>
2292+
[JsonConverter(typeof(FunctionStringConverter))]
22812293
public string TooltipHoverFormatter { get; set; }
22822294

22832295
/// <summary>
@@ -2391,6 +2403,7 @@ public class LegendMarkers
23912403
/// }
23922404
/// </code>
23932405
/// </summary>
2406+
[JsonConverter(typeof(FunctionStringConverter))]
23942407
public string CustomHTML { get; set; }
23952408
}
23962409

@@ -2915,6 +2928,7 @@ public class BarTotalDataLabels
29152928
/// <summary>
29162929
/// Applies a custom function for the total value. The function accepts 2 params where the 1st one is the value while the 2nd one is the config object.
29172930
/// </summary>
2931+
[JsonConverter(typeof(FunctionStringConverter))]
29182932
public string Formatter { get; set; }
29192933

29202934
/// <summary>
@@ -3284,6 +3298,7 @@ public class DonutLabelName
32843298
/// <summary>
32853299
/// A custom formatter function to apply on the name text in dataLabel
32863300
/// </summary>
3301+
[JsonConverter(typeof(FunctionStringConverter))]
32873302
public string Formatter { get; set; }
32883303
}
32893304

@@ -3330,6 +3345,7 @@ public class DonutLabelTotal
33303345
/// <summary>
33313346
/// A custom formatter function to apply on the total value. It accepts one parameter w which contains the chart's config and global objects. Defaults to a total of all series percentage divided by the length of series.
33323347
/// </summary>
3348+
[JsonConverter(typeof(FunctionStringConverter))]
33333349
public string Formatter { get; set; }
33343350
}
33353351

@@ -3371,6 +3387,7 @@ public class DonutLabelValue
33713387
/// <summary>
33723388
/// A custom formatter function to apply on the value label in dataLabel
33733389
/// </summary>
3390+
[JsonConverter(typeof(FunctionStringConverter))]
33743391
public string Formatter { get; set; }
33753392
}
33763393

@@ -3634,6 +3651,7 @@ public class RadialBarDataLabelsTotal
36343651
/// <summary>
36353652
/// A custom formatter function to apply on the total value. It accepts one parameter w which contains the chart's config and global objects. Defaults to a total of all series percentage divided by the length of series.
36363653
/// </summary>
3654+
[JsonConverter(typeof(FunctionStringConverter))]
36373655
public string Formatter { get; set; }
36383656
}
36393657

@@ -3670,6 +3688,7 @@ public class RadialBarDataLabelsValue
36703688
/// <summary>
36713689
/// A custom formatter function to apply on the value label in dataLabel
36723690
/// </summary>
3691+
[JsonConverter(typeof(FunctionStringConverter))]
36733692
public string Formatter { get; set; }
36743693

36753694
/// <summary>
@@ -4152,6 +4171,7 @@ public class Tooltip
41524171
/// <summary>
41534172
/// Draw a custom html tooltip instead of the default one based on the values provided in the function arguments.
41544173
/// </summary>
4174+
[JsonConverter(typeof(FunctionValueOrListConverterConverter))]
41554175
public CustomFunction Custom { get; set; }
41564176

41574177
/// <summary>
@@ -4329,6 +4349,7 @@ public class TooltipX
43294349
/// <summary>
43304350
/// A custom formatter function which you can override and display according to your needs (a use case can be a date formatted using complex moment.js functions)
43314351
/// </summary>
4352+
[JsonConverter(typeof(FunctionStringConverter))]
43324353
public string Formatter { get; set; }
43334354
}
43344355

@@ -4340,6 +4361,7 @@ public class TooltipYTitle
43404361
/// <summary>
43414362
/// The series name which appears besides values can be formatted using this function. Default behaviour is (seriesName) => returns seriesName
43424363
/// </summary>
4364+
[JsonConverter(typeof(FunctionStringConverter))]
43434365
public string Formatter { get; set; }
43444366
}
43454367

@@ -4367,6 +4389,7 @@ public class TooltipY
43674389
/// }
43684390
/// </code>
43694391
/// </summary>
4392+
[JsonConverter(typeof(FunctionStringConverter))]
43704393
public string Formatter { get; set; }
43714394
}
43724395

@@ -4383,6 +4406,7 @@ public class TooltipZ
43834406
/// <summary>
43844407
/// To format the z values of a Bubble series, you can use this function.
43854408
/// </summary>
4409+
[JsonConverter(typeof(FunctionStringConverter))]
43864410
public string Formatter { get; set; }
43874411
}
43884412

@@ -4833,6 +4857,7 @@ public class XAxisLabels
48334857
/// }
48344858
/// </code>
48354859
/// </summary>
4860+
[JsonConverter(typeof(FunctionStringConverter))]
48364861
public string Formatter { get; set; }
48374862

48384863
/// <summary>
@@ -4914,6 +4939,7 @@ public class YAxisLabels
49144939
/// <remarks>
49154940
/// Note: In horizantal bar charts, the second parameters also contains additional data like dataPointIndex &amp; seriesIndex.
49164941
/// </remarks>
4942+
[JsonConverter(typeof(FunctionStringConverter))]
49174943
public string Formatter { get; set; }
49184944

49194945
/// <summary>
@@ -5059,6 +5085,7 @@ public class AxisTooltip
50595085
/// }
50605086
/// </code>
50615087
/// </summary>
5088+
[JsonConverter(typeof(FunctionStringConverter))]
50625089
public string Formatter { get; set; }
50635090

50645091
/// <inheritdoc cref="ApexCharts.AxisTooltipStyle" />

src/Blazor-ApexCharts/Models/MultiType/CustomFunction.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Collections.Generic;
2+
using System.Text.Json.Serialization;
3+
using ApexCharts.Internal;
24

35
namespace ApexCharts
46
{

src/Blazor-ApexCharts/wwwroot/js/blazor-apexcharts.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,8 @@ window.blazor_apexchart = {
421421

422422
parseOptions(options) {
423423
return JSON.parse(options, (key, value) => {
424-
if ((key === 'formatter' || key === 'dateFormatter' || key === 'custom' || key === 'click' || key === 'mouseEnter' || key === 'mouseLeave' || key === 'tooltipHoverFormatter') && value.length !== 0) {
424+
if (value && typeof value === 'object' && '@eval' in value) {
425+
value = value['@eval'];
425426
if (Array.isArray(value))
426427
return value.map(item => eval?.("'use strict'; (" + item + ")"));
427428
else

0 commit comments

Comments
 (0)