Replies: 1 comment 1 reply
|
There seems to be a few misconceptions how Blazor works here. Let's start with the second failing test public async Task Set(string? value)
{
+ Value = value; // You have to set your parameter as well
await ValueChanged.InvokeAsync(value);
}If you check how the That is why just invoking the event doesn't cut it. Blazor doesn't do something magically in that sense. Now why is your first The two-way binding is only useful if you want to bubble up the value to the parent again. Here an example: Parent.razor <p>@text</p>
<Child Text="@text"></Child>
<button @onclick="() => StateHasChanged()">Update Parent</button>
@code {
private string text = "Test";
}Child.razor: <p>@Text</p>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnParametersSet()
{
Text = "New Content";
StateHasChanged();
}
}The output will always be: Even if you press the button and force Blazor to check everything again. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Given this component:
And these NUnit tests:
The two Bound tests fail (reporting no change to the rendered content -- still showing "test"). What am I missing?
For
BoundValueFromOutside, I'm expecting that some explicit call is needed (since there's no internalStateHasChanged), and I was expecting thatRenderwould pick up the newvalueand render that, but it appears it does not.For
BoundValueFromInside, I'm expecting that there shouldn't need to be any call (sinceEventCallbackshould internally triggerStateHasChanged) but I included an explicitRenderanyway, but it doesn't help.Perhaps notably, the
SetParameterslifecycle method is called again for the second render but still with the old value.If I explicitly
SetParametersAndRenderthen it produces the correct output, but this means repeating the parameter assignments, which shouldn't be necessary when binding.Of interest is that if I add a wrapping component:
With this test:
[Test] public async Task BoundValueFromWrapper() { using var cut = Render<BoundComponent>(@<BoundComponent/>); cut.MarkupMatches("<span></span>"); cut.Instance.Value = "test"; cut.Render(); cut.MarkupMatches("<span>test</span>"); await cut.InvokeAsync(() => cut.FindComponent<BindComponent>().Instance.Set("another")); Assert.That(cut.Instance.Value, Is.EqualTo("another")); cut.MarkupMatches("<span>another</span"); }This variant includes two changes to
Value-- the first is similar toBoundValueFromOutsideand the second is similar toBoundValueFromInside. But now this all passes, without requiring explicit re-render after the internalSet(as expected) -- but it does fail (call on wrong thread) ifSetis called directly rather than invoked (which wasn't entirely expected, but seems reasonable).It does seem odd that a top-level component under test behaves differently from a wrapped component, though.
All reactions