I am currently using Blazor Server Side ( 3.1.5 )
I have a control that dynamically builds some UI via RenderTreeBuilder, and so far this works great. However I have problem. Based on a parameter of this component the result is either empty or a list of entries...
Switching from a "filled" state of the component to an "empty" state does not generate a render batch, and thus the client side is never updated.
( but the control is correctly updated, BuildRenderTree is called )
public sealed class CommandParametersEditor : ComponentBase
{
[Inject]
private IEnumerable<IValueEditorProvider> ValueEditorProviders { get; set; }
private Dictionary<string, IValueEditor> valueEditors;
private Dictionary<string, Option<object>> values;
private ICommandDefinition oldCommandDefinition;
[Parameter]
public ICommandDefinition CommandDefinition { get; set; }
[Parameter]
public IReadOnlyDictionary<string, Option<object>> Values
{
get => values;
set {}
}
[Parameter]
public EventCallback<IReadOnlyDictionary<string,Option<object>>> ValuesChanged { get; set; }
protected override async Task OnParametersSetAsync()
{
if ((CommandDefinition != null && CommandDefinition != oldCommandDefinition)
|| (CommandDefinition != null && oldCommandDefinition == null))
{
values = new Dictionary<string, Option<object>>();
valueEditors = new Dictionary<string, IValueEditor>();
// Find editors
foreach (var parameterDefinition in CommandDefinition.Parameters)
{
var valueEditorProvider =
ValueEditorProviders.FirstOrDefault(v =>
v.CanHandle(parameterDefinition.Type));
if (valueEditorProvider == null)
{
throw new NotSupportedException($"Parameter type {parameterDefinition.Type.Id} " +
$"has no supporting editor implementation");
}
values.Add(parameterDefinition.Id,parameterDefinition.GetSampleValue());
valueEditors.Add(parameterDefinition.Id,
await valueEditorProvider.Create(values[parameterDefinition.Id]));
await ValuesChanged.InvokeAsync(values);
oldCommandDefinition = CommandDefinition;
}
}
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (CommandDefinition == null)
{
return;
}
builder.OpenComponent<RadzenFieldset>(0);
builder.AddAttribute(1,"Text","Parameters");
if (CommandDefinition.Parameters.Any())
{
foreach (var parameterDefinition in CommandDefinition.Parameters)
{
var editor = valueEditors[parameterDefinition.Id];
var fragment = CreateEditorFragment(parameterDefinition, editor);
builder.AddAttribute(2, "ChildContent", fragment);
}
}
else
{
builder.OpenElement(3,"h4");
builder.AddContent(4,"No parameters");
builder.CloseElement();
}
builder.CloseComponent();
}
private RenderFragment CreateEditorFragment(IParameterDefinition parameterDefinition,IValueEditor valueEditor)
{
return builder =>
{
builder.OpenElement(0,"div");
builder.AddAttribute(1,"class","row");
// Label
builder.OpenElement(2,"div");
builder.AddAttribute(3,"class","col-md-4 align-items-center d-flex");
builder.OpenComponent<RadzenLabel>(4);
builder.AddAttribute(5,"Text",parameterDefinition.Id);
builder.CloseComponent();
builder.CloseElement();
// Editor
builder.OpenElement(6,"div");
builder.AddAttribute(7,"class","col-md-8");
var parameterId = parameterDefinition.Id;
var changeCallback = EventCallback.Factory.Create<Option<object>>(
this,
async v =>
{
values[parameterId] = v;
await ValuesChanged.InvokeAsync(values);
});
valueEditor.ValueChanged = changeCallback;
builder.AddContent(8,valueEditor.CreateRenderFragment());
builder.CloseElement();
builder.CloseElement();
};
}
}
If a command was bound that has some parameters, or switching between those, everything works ok, But if i had a command that had parameters and the new one does not have them, the "No Parameters" Header is never rendered, instead the old content remains ...