-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dialog: Fixed dialog parameters re-set on each StateHasChanged overwriting current values #1773
Conversation
Here is a small repro from stock sample: Just open any dialog, change the date to anything and click "Update". |
Please create a test case that will fortify this behavior against accidental corruption by future PRs |
While creating the test I've faced some weird behavior.
But. Also if you change the "Color" parameter's type to be, for example, integer, then the bug also doesn't appear. I even changed "AddComponentReferenceCapture" method to also use correct sequence but that doesn't help either. |
it even happens with this:
and
but it doesn't happen with this:
So it doesn't depend on the type of the param but on the value. Why |
Check this out, even defining an own enum and using it causes it https://try.mudblazor.com/snippet/mEQvuAEAgaFzxDOS |
This is the method that is called when adding the attribute. I wonder what it does with Enums. /// <summary>
/// Appends a frame representing a string-valued attribute.
/// The attribute is associated with the most recently added element. If the value is <c>null</c>, or
/// the <see cref="System.Boolean" /> value <c>false</c> and the current element is not a component, the
/// frame will be omitted.
/// </summary>
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="value">The value of the attribute.</param>
public void AddAttribute(int sequence, string name, object? value)
{
// This looks a bit daunting because we need to handle the boxed/object version of all of the
// types that AddAttribute special cases.
if (_lastNonAttributeFrameType == RenderTreeFrameType.Element)
{
if (value == null)
{
// Treat 'null' attribute values for elements as a conditional attribute.
TrackAttributeName(name);
}
else if (value is bool boolValue)
{
if (boolValue)
{
_entries.AppendAttribute(sequence, name, BoxedTrue);
}
else
{
// Don't add anything for false bool value.
TrackAttributeName(name);
}
}
else if (value is IEventCallback callbackValue)
{
if (callbackValue.HasDelegate)
{
_entries.AppendAttribute(sequence, name, callbackValue.UnpackForRenderTree());
}
else
{
TrackAttributeName(name);
}
}
else if (value is MulticastDelegate)
{
_entries.AppendAttribute(sequence, name, value);
}
else
{
// The value is either a string, or should be treated as a string.
_entries.AppendAttribute(sequence, name, value.ToString());
}
}
else if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
// If this is a component, we always want to preserve the original type.
_entries.AppendAttribute(sequence, name, value);
}
else
{
// This is going to throw. Calling it just to get a consistent exception message.
AssertCanAddAttribute();
}
} |
Check this out. Casting the enum value to int works! https://try.mudblazor.com/snippet/QEclOqaKUvbpaaaP |
Well in our case everything from "AddAttribute" actually goes directly here: public void AppendAttribute(int sequence, string attributeName, object? attributeValue)
{
if (_itemsInUse == _items.Length)
{
GrowBuffer(_items.Length * 2);
}
_items[_itemsInUse++] = new RenderTreeFrame
{
SequenceField = sequence,
FrameTypeField = RenderTreeFrameType.Attribute,
AttributeNameField = attributeName,
AttributeValueField = attributeValue,
};
} So the value is stored directly without any conversions. I wonder if the problem lies somewhere inside rendering parts of Blazor framework itself. |
https://github.com/uhfath/TestBlazorParameters A small repro with just the Blazor code. However, adding any number of extra parameters of different types doesn't change anything. |
Sure, it is logical that this will happen in your code. Even commenting out the Enum parameter doesn't change anything, so the Enum parameter is not the cause in your example. In your example calling StateHasChanged re-renders the renderfragment which applies the same parameters again with which it was initialized. I am not sure this is is a real repro because as I have demonstrated it doesn't happen in the tryMB when you pass the enum value as an int: https://try.mudblazor.com/snippet/QEclOqaKUvbpaaaP |
Change this: public partial class Index : ComponentBase
{
private string title = "test";
private RenderFragment renderFragment => new RenderFragment(builder =>
{
var i = 0;
builder.OpenComponent(i++, typeof(SurveyPrompt));
builder.AddAttribute(i++, nameof(SurveyPrompt.Title), title);
//builder.AddAttribute(i++, nameof(SurveyPrompt.TestEnum), TestEnum.Error);
builder.AddAttribute(i++, nameof(SurveyPrompt.OnTest), (Action<string>)OnTest);
builder.CloseComponent();
});
private void OnTest(string title)
{
this.title = title;
StateHasChanged();
}
} and [Parameter]
public Action<string> OnTest { get; set; }
private async void OnTestClicked()
{
Console.WriteLine("Child pre: {0}", Title);
OnTest(Title);
Console.WriteLine("Child post: {0}", Title);
} |
I've done a bit more digging and this is what came up (taken from MudBlazor test). This is a diff of a case when test is passed but shouldn't (one "TestValue" parameter): And this is when we use two parameters and the test fails: Notice the latter is completely identical as it should (the parameter is restored after StateHasChanged). The left part is before StateHasChanged. I'm not entirely sure what is this attribute for and why it's values are missing or showing up. If this attribute is used as a reference to elements then perhaps in the first case the framework couldn't find the exact elements to diff with, hence no parameter was changed. |
I don't know where we stand now. Do you think this is a bug in MudDialog or in the Blazor framework or is it a bug on your side application side? |
I actually believe this is a bug in MudBlazor, since parameters are overwritten every time. |
Small refactor
Added a test and made some refactoring. |
I have encountered a similar issue which seems very related to this. |
Looks good. Thank you very much. |
When using parameters for dialog like this:
They get re-set every time a StateHasChanged is called for the components, which use these parameters.
This PR adds a AreParametersRendered property for a DialogReference to restrict parameters setting only for the first time when the component is created.