0
votes

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 ...

1

1 Answers

0
votes

My first thoughts are

1: When you change the value of CommandDefinition can it ever become null? Just to be sure, put a check in if (CommandDefinition == null && oldCommandDefinition != null) throw ......

2: When you change your CommandDefinition state in the parent, do you alter the state of the existing Command by doing something like commendDefinition.Parameters.Clear(); or do you replace it with a new instance? Because your code only checks for a new instance.