Skip to content

Commit 1133cd4

Browse files
Yokesh-SF4393YokeshSF4393PrinceOliver
authored
Fixed blazor toolkit chart rendering issue in SSR (#66)
* 1052437: Fixed blazor toolkit chart rendering issue in SSR * 1052437: Addressed review changes for the SSR issue. --------- Co-authored-by: YokeshSF4393 <yokesh.sf4393@gmail.com> Co-authored-by: Prince Oliver <31838171+PrinceOliver@users.noreply.github.com>
1 parent 346cb12 commit 1133cd4

8 files changed

Lines changed: 132 additions & 18 deletions

File tree

‎src/Components/Charts/Chart/Renderer/ChartElementRenderer.cs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
128128
return;
129129
}
130130

131+
if (IsStaticSSR())
132+
{
133+
SetDefaultRendererValues();
134+
}
135+
131136
this.CreateCascadingValue(builder, 0, 1, this, 2,
132137
BuildRenderers);
133138
RendererShouldRender = false;
@@ -203,6 +208,13 @@ internal void SetDefaultRendererContainerValues()
203208
internal void Prerender()
204209
{
205210
ContainerUpdate = true;
211+
212+
if (IsStaticSSR())
213+
{
214+
ContainerPrerender = true;
215+
RendererShouldRender = true;
216+
SetDefaultRendererValues();
217+
}
206218
_ = InvokeAsync(StateHasChanged);
207219
}
208220
#endregion

‎src/Components/Charts/Chart/Renderer/SeriesRenderers/BaseRenderers/SeriesContainer.cs‎

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,10 @@ private static void CreateSeriesNestedElements(RenderTreeBuilder builder, ChartS
381381
{
382382
int seq = 0;
383383
ChartSeries series = element;
384-
if (series.Marker?.RendererType is not null)
384+
385+
bool shouldOpenMarker = series.Marker?.RendererType is not null &&
386+
(series.Marker.Visible || series.Marker.DataLabel.Visible);
387+
if (shouldOpenMarker)
385388
{
386389
builder.OpenComponent(seq++, series.Marker.RendererType);
387390
builder.AddAttribute(seq++, "Series", series);
@@ -978,14 +981,21 @@ internal override void SetDefaultRendererValues()
978981
ProcessData();
979982
Owner?._legendRenderer?.SetDefaultRendererValues();
980983
Owner?._axisContainer?.SetDefaultRendererContainerValues();
981-
}
982-
catch
983-
{
984-
if (!IsDisposed)
984+
if (Owner?.InitialRect is not null)
985985
{
986-
throw;
986+
HandleChartSizeChange(Owner.InitialRect);
987+
foreach (var seriesRenderer in Renderers.OfType<ChartSeriesRenderer>())
988+
{
989+
seriesRenderer.Series?.Marker?.Renderer?.HandleChartSizeChange(Owner.InitialRect);
990+
}
991+
Owner?._striplineBehindContainer?.SetDefaultRendererValues();
992+
Owner?._striplineOverContainer?.SetDefaultRendererValues();
987993
}
988994
}
995+
catch (Exception exception) when (IsDisposed)
996+
{
997+
System.Diagnostics.Debug.WriteLine($"Chart series renderer initialization failed during disposal: {exception}");
998+
}
989999
}
9901000

9911001
internal void OnThemeChanged()
@@ -1105,6 +1115,19 @@ public override void HandleChartSizeChange(Rect rect)
11051115
{
11061116
renderer.HandleChartSizeChange(rect);
11071117
}
1118+
1119+
if (IsStaticSSR())
1120+
{
1121+
// Markers again if they were null during series pass
1122+
foreach (var seriesRenderer in Renderers.OfType<ChartSeriesRenderer>())
1123+
{
1124+
seriesRenderer.Series?.Marker?.Renderer?.HandleChartSizeChange(rect);
1125+
}
1126+
1127+
// Striplines after axis + series clip are final
1128+
Owner?._striplineBehindContainer?.UpdateStriplineCollection();
1129+
Owner?._striplineOverContainer?.UpdateStriplineCollection();
1130+
}
11081131
}
11091132

11101133
/// <summary>

‎src/Components/Charts/Chart/Renderer/SeriesRenderers/MarkerRenders/ChartMarkerRenderer.cs‎

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ internal class ChartMarkerRenderer : ChartRenderer, IChartElementRenderer
1313
#region Fields
1414
private string? _seriesIndex;
1515
private List<SymbolOptions> _symbolOptions = [];
16+
// Static SSR is allowed once; subsequent passes use the normal renderer gate.
17+
private bool _staticSSRRenderPending = true;
1618
#endregion
1719

1820
#region Properties
@@ -359,6 +361,16 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
359361
RenderMarkers(builder);
360362
builder.CloseElement();
361363
}
364+
365+
if (IsStaticSSR())
366+
{
367+
_staticSSRRenderPending = false;
368+
}
369+
}
370+
371+
protected override bool ShouldRender()
372+
{
373+
return RendererShouldRender || (IsStaticSSR() && _staticSSRRenderPending);
362374
}
363375

364376
#endregion
@@ -377,7 +389,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
377389
/// <returns>Calculated SymbolOptions instance.</returns>
378390
internal static SymbolOptions CalculateSymbol(ChartEventLocation location, string shape, Size size, string url, PathOptions option, SfChart chart)
379391
{
380-
ChartEventLocation currentLocation = null!;
392+
ChartEventLocation currentLocation = chart.IsStaticServerRendering() ? location : null!;
381393
if (chart is not null && shape == "Circle")
382394
{
383395
string[] locations = ChartHelper.AppendTextElements(chart, option.Id, location.X, location.Y, "cx", "cy");
@@ -388,7 +400,10 @@ internal static SymbolOptions CalculateSymbol(ChartEventLocation location, strin
388400

389401
if (shapeoption.ShapeName == ShapeName.Path)
390402
{
391-
shapeoption.PathOption.Direction = ChartHelper.AppendPathElements(chart ?? null!, shapeoption.PathOption.Direction, shapeoption.PathOption.Id);
403+
if (chart != null)
404+
{
405+
shapeoption.PathOption.Direction = ChartHelper.AppendPathElements(chart, shapeoption.PathOption.Direction, shapeoption.PathOption.Id);
406+
}
392407
shapeoption.PathOption.Visibility = option.Visibility;
393408
}
394409

@@ -506,6 +521,20 @@ internal void UpdateMarkerBorderWidth()
506521
}
507522
}
508523

524+
/// <summary>
525+
/// SSR entry point – computes marker symbols when OnAfterRenderAsync is unavailable.
526+
/// </summary>
527+
internal override void SetDefaultRendererValues()
528+
{
529+
SeriesRenderer = Series?.Renderer;
530+
if (SeriesRenderer != null && (Series?.Marker?.Visible ?? false))
531+
{
532+
CalculateRenderTreeBuilderOptions(); // fills _symbolOptions
533+
SeriesRenderer.CalculateMarkerClipPath();
534+
}
535+
RendererShouldRender = true;
536+
}
537+
509538
#endregion
510539

511540
#region Public Methods

‎src/Components/Charts/Chart/Renderer/StriplineRenderer/ChartStriplineContainer.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ internal override void SetDefaultRendererValues()
6565
{
6666
ClipRect = Owner._axisContainer.AxisLayout.SeriesClipRect ?? null!;
6767
}
68+
6869
HandleChartSizeChange(Owner?.InitialRect ?? new Rect(0, 0, 0, 0));
70+
RendererShouldRender = true;
6971
}
7072
#endregion
7173

‎src/Components/Charts/Chart/Renderer/StriplineRenderer/ChartStriplineRenderer.cs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,18 @@ internal void UpdateCustomization(string property)
643643
}
644644
}
645645

646+
/// <summary>
647+
/// SSR entry point – calculates stripline geometry when OnAfterRenderAsync is unavailable.
648+
/// </summary>
649+
internal override void SetDefaultRendererValues()
650+
{
651+
if (Stripline != null)
652+
{
653+
InitStripline(); // this is the method that already exists and fills the path/rect collections
654+
}
655+
RendererShouldRender = true;
656+
}
657+
646658
#endregion
647659

648660
#region Public Methods

‎src/Components/Charts/Chart/Series/Marker/ChartMarker.cs‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using System.ComponentModel;
2-
using Microsoft.AspNetCore.Components;
1+
using Microsoft.AspNetCore.Components;
32
using Microsoft.AspNetCore.Components.Rendering;
3+
using Syncfusion.Blazor.Toolkit.Charts.Internal;
4+
using System.ComponentModel;
45

56
namespace Syncfusion.Blazor.Toolkit.Charts
67
{
@@ -99,9 +100,26 @@ private void UpdateLegend()
99100
/// </summary>
100101
private void SetDefaultRendererValues()
101102
{
102-
if (Series?.Container is not null && Series.Renderer is not null && Series.Renderer.IsStaticSSR())
103+
// Guard everything – any of these can still be null on the first SSR pass
104+
if (Series == null || Series.Container == null || Series.Renderer == null)
103105
{
104-
Series.Container._seriesContainer?.Prerender();
106+
return;
107+
}
108+
109+
if (!Series.Renderer.IsStaticSSR())
110+
{
111+
return;
112+
}
113+
114+
// 1. Force the series container to run its layout / prerender
115+
Series.Container._seriesContainer?.Prerender();
116+
117+
// Recalculate symbols when the marker renderer component already exists.
118+
var markerRenderer = Series.Marker?.Renderer;
119+
if (markerRenderer != null)
120+
{
121+
var rect = Series.Container.InitialRect ?? new Rect(0, 0, 0, 0);
122+
markerRenderer.HandleChartSizeChange(rect);
105123
}
106124
}
107125

‎src/Components/Charts/Chart/SfChart.razor.cs‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ private double GetInitialCoordinate(double adaptiveMargin, double staticMargin,
588588
private void CalculateAvailableSize()
589589
{
590590
double height = Height != NullDimensionValue ? ChartHelper.StringToNumber(Height, _elementOffset.Height) : ChartDefaultHeight;
591-
double width = Width != NullDimensionValue ? ChartHelper.StringToNumber(Width, _elementOffset.Width) : ChartDefaultHeight;
591+
double width = Width != NullDimensionValue ? ChartHelper.StringToNumber(Width, _elementOffset.Width) : ChartDefaultWidth;
592592

593593
AvailableSize = new Size(width > 0 ? width : AvailableSize.Width, height > 0 ? height : AvailableSize.Height);
594594
if (EnableAdaptiveRendering)
@@ -1724,16 +1724,26 @@ internal void InitializeStaticChart()
17241724
{
17251725
if (!_render.IsSizeSet)
17261726
{
1727+
// Force defaults when no DOM measurement is possible
1728+
if (_elementOffset.Width <= 0 || _elementOffset.Height <= 0)
1729+
{
1730+
_svgWidth ??= ChartDefaultWidth.ToString(CultureInfo.InvariantCulture);
1731+
_svgHeight ??= ChartDefaultHeight.ToString(CultureInfo.InvariantCulture);
1732+
}
1733+
17271734
CalculateAvailableSize();
17281735
SetInitialRect();
1736+
1737+
// Border initialization reaches this method before the normal layout pass;
1738+
// process data first so the synchronous prerender sees populated renderers.
1739+
// IsSizeSet keeps this SSR-only initialization from repeating.
1740+
ProcessData();
1741+
Prerender();
17291742
}
17301743
}
1731-
catch
1744+
catch (Exception exception) when (IsDisposed)
17321745
{
1733-
if (!IsDisposed)
1734-
{
1735-
throw;
1736-
}
1746+
System.Diagnostics.Debug.WriteLine($"Static chart initialization failed during disposal: {exception}");
17371747
}
17381748
}
17391749

‎src/Components/Charts/Common/ChartUtils/ChartHelper.cs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,14 @@ internal static string AppendPathElements(SfChart chart, string direction, strin
12151215
/// <returns>An array containing the string representation of X and Y coordinates.</returns>
12161216
internal static string[] AppendTextElements(SfChart chart, string id, double locationX, double locationY, string x = "x", string y = "y")
12171217
{
1218+
if (chart == null && chart.IsStaticServerRendering())
1219+
{
1220+
return new string[]
1221+
{
1222+
locationX.ToString(CultureInfo.InvariantCulture),
1223+
locationY.ToString(CultureInfo.InvariantCulture)
1224+
};
1225+
}
12181226
bool redraw = chart._redraw;
12191227

12201228
if (chart._textAnimationElements.TryGetValue(id, out DynamicTextAnimationOptions? existElement))

0 commit comments

Comments
 (0)