问题
I have these two components in my Blazor app:
Component1.razor:
<CascadingValue Value=this>
<Component2/>
</CascadingValue>
<p>@DisplayedText</p>
@code {
public string DisplayedText = string.Empty;
}
Component2.razor:
<button @onclick=@(e => { C1.DisplayedText = "testing"; })>Set DisplayedText</button>
@code {
[CascadingParameter]
public Component1 C1 { get; set; }
}
When I click the "Set DisplayedText" button, the text in the p
element in Component1
should change to testing
, but it does not. How can I fix this?
回答1:
@Merlin04, this is an abuse and misuse of the cascading value feature. Ordinarily, a parent component communicates with its child via component parameters.
You can't update a cascaded value from a descendant.
Wrong...
The following code snippet demonstrate a better solution, based on what you do, though it is not optimal because your code and mine as well update a property from a method, when in matter of fact, we should changed the property's value directly, and not through a mediator code (that is a method)
Component2.razor
<button @onclick="@(() => SetDisplayedText.InvokeAsync("testing"))">Set
DisplayedText</button>
@code {
[Parameter]
public EventCallback<string> SetDisplayedText { get; set; }
}
Component1.razor
<Component2 SetDisplayedText="@SetDisplayedText"/>
<p>@DisplayedText</p>
@code {
private string DisplayedText = string.Empty;
public void SetDisplayedText(string newText)
{
DisplayedText = newText;
}
}
Note that calling the StateHasChanged method is not necessary, as this is the bonus you get when using the EventCallback 'delegate'
Hope this helps...
回答2:
Merlin04, the following code snippet demonstrate how you can do it. Note that this is really a very simple sample, but it shows how you should code when communication between distant components is required.
Here's the code, copy and run it, and if you have more questions don't hesitate to ask.
MessageService.cs
public class MessageService
{
private string message;
public string Message
{
get => message;
set
{
if (message != value)
{
message = value;
if (Notify != null)
{
Notify?.Invoke();
}
}
}
}
public event Action Notify;
}
Note: The service is a normal class... It provides services to other objects, and it should be added to the DI container in Startup.ConfigureServices method to make it available to requesting clients. Add this: to the ConfigureServices method:
services.AddScoped<MessageService>();
Note: As you can see I define an event delegate of the Action type, which is invoked from the property's set accessor, when the user type text into a text box in Component3. Triggering this delegate causes the text entered by Components3 to be displayed in the Index component which is the parent of Component2 (see code below).
Index.razor
@page "/"
@inject MessageService MessageService
@implements IDisposable
<p>I'm the parent of Component2. I've got a message from my grand child:
@MessageService.Message</p>
<Component2 />
@code {
protected override void OnInitialized()
{
MessageService.Notify += OnNotify;
}
public void OnNotify()
{
InvokeAsync(() =>
{
StateHasChanged();
});
}
public void Dispose()
{
MessageService.Notify -= OnNotify;
}
}
Note that we directly bind to the MessageService.Message property, but the StateHasChanged method must be called to refresh the display of the text.
Component2.razor
<h3>Component2: I'm the parent of component three</h3>
<Component3/>
@code {
}
Component3.razor
@inject MessageService MessageService
<p>This is component3. Please type a message to my grand parent</p>
<input placeholder="Type a message to grandpa..." type="text"
@bind="@MessageService.Message" @bind:event="oninput" />
Note that in Component3 we bind the MessageService.Message to a text box, and the binding occurs each time you press a key board( input event versus change event).
That is all, hope this helps, and don't hesitate to ask any question.
回答3:
See enet's answer instead of this
You can't update a cascaded value from a descendant.
Instead, make a method in the parent (Component1) to set the value:
public void SetDisplayedText(string newText) {
DisplayedText = newText;
StateHasChanged();
}
Then, you can call that in the descendant:
<button @onclick=@(e => { C1.SetDisplayedText("testing"); })>Set DisplayedText</button>
来源:https://stackoverflow.com/questions/61377193/blazor-component-needs-to-modify-value-of-parent-component