English 中文(简体)
How should I handle EditContext & Model in an inner component?
原标题:

I have a number of pages that all need the same address. And address is not only the UI part, but there s a bit of code behind, mainly to call the Azure Map API to get the validated form of the address and location.

So it looks like this:

enter image description here

And the way I have set it up is as follows (I ve eliminated a lot of the inner controls for brevity).

The outer razor file:

<EditForm EditContext="EditContext"
          OnValidSubmit="HandleValidSubmitAsync"
          OnInvalidSubmit="HandleInvalidSubmitAsync"
          Context="EditFormContext">
    <ObjectGraphDataAnnotationsValidator />
    <CustomValidation @ref="_customValidation" />
    <DxFormLayout>
        <DxFormLayoutItem Caption="Unique Id:" Field="@nameof(Model.UniqueId)" ColSpanMd="4">
            <DxTextBox @bind-Text="@Model.UniqueId"
                       ReadOnly="@(ModeState != Mode.Create)"
                       ShowValidationIcon="true" />
        </DxFormLayoutItem>

        <AddressForm @ref="_addressElement"
                     ReadOnly="@(ModeState == Mode.Read)"
                     OnValidated="OnAddressValidated"
                     ValueChanged="@OnAddressChanged"
                     Value="@Model.AddressModel">
        </AddressForm>

        <DxFormLayoutItem Visible="@(ModeState != Mode.Create)" ColSpanMd="4">
            <DxCheckBox @bind-Checked="@Model.Enabled"
                        ReadOnly="@(ModeState == Mode.Read)">Enabled</DxCheckBox>
        </DxFormLayoutItem>
        <DxFormLayoutItem ColSpanMd="12">
            <ValidationSummary />
        </DxFormLayoutItem>
        <DxFormLayoutItem ColSpanMd="12">
            <DxButton Enabled="@SubmitEnabled"
                      Text="@SubmitButtonText"
                      SubmitFormOnClick="true"
                      RenderStyle="@ButtonRenderStyle.Secondary" />
        </DxFormLayoutItem>

    </DxFormLayout>
</EditForm>

And then AddressForm.razor (DxFormLayout s can be nested):

<DxFormLayout >
    <DxFormLayoutItem Caption="Street Address:" Field="@nameof(Value.StreetNumberAndName)" ColSpanMd="12">
        <DxTextBox @bind-Text="@Value!.StreetNumberAndName" 
                   ReadOnly = "@ReadOnly"
                   ShowValidationIcon="true" />
    </DxFormLayoutItem>
</DxFormLayout>

The models are:

public class CampaignPageModel
{
    // ...
    public AddressFormPageModel AddressModel { get; set; }
    // ...
}

public class AddressFormPageModel
{
    public string? StreetNumberAndName { get; set; }
    // ...
}

So a couple of questions about this:

  1. Is this a reasonable way to do this?
  2. For the EditContext and Model in AddressForm.razor.cs, do I need to set EditContext as a [Parameter] in AddressForm that is set by the Outer.razor component? And then get Model in the AddressForm from the passed down EditContext?
  3. Should AddressForm even have a Value property in this case? If so, it would be the EditContext.Model.AddressPageModel property that was passed down - correct?
  4. If I don t have a Value (or do), ok to have a ValueChanged EventCallback? I need this because, if the address is not changed, no need to call the Azure Map API (which causes a pause in the UI).

Update: on reconsideration I think the inner component should have its own Context. And it communicates with the outer component using Value and ValueChanged.

Is there guidance from Microsoft on this?

问题回答

Here s what I came up with.

For my inner AddressForm.razor.cs I did the following:

public partial class AddressForm
{
    /// <summary>
    /// You should <b>not</b> need this. You pass in the Context and the Context.Model is the parent
    /// page model. That has AddressFormPageModel as a property and you want to directly use that
    /// which is direct access (similar to a bind).
    /// If you set this, all bets are off (not tested). If you get this - you re probably doing
    /// something wrong.
    /// </summary>
    [Parameter]
    public AddressFormPageModel? Value { get; set; }

    /// <summary>
    /// Called when any field in the address changes. Including when the user selects an entry from
    /// the AddressSelectionWindow. You should not use this to assign the new Value as the parent
    /// component already has that as Value == ParentModel.AddressFormPageModel. But it is legit to
    /// use this to know that the user has changed the address, thereby requiring calling the map
    /// service to get the valid address and location.
    /// </summary>
    [Parameter]
    public EventCallback<AddressFormPageModel> ValueChanged { get; set; }

    /// <summary>
    /// The EditContext for the form. It is passed from the parent component. Context.Model is the parent
    /// page model. That must have a property that returns AddressFormPageModel. That is the model/value
    /// used in this component.
    /// </summary>
    [Parameter]
    public EditContext? Context { get; set; }

    /// <inheritdoc />
    protected override async Task OnInitializedAsync()
    {
        ArgumentNullException.ThrowIfNull(Context);
        ArgumentNullException.ThrowIfNull(Context.Model as IIncludesAddressModel);

        // we get Value from the passed in Context. The parent Context.Model object must have a
        // property that returns AddressFormPageModel and that is the model for this component.
        Value = ((IIncludesAddressModel)Context.Model).AddressModel;

        Context.OnFieldChanged += OnFieldChanged;

        await base.OnInitializedAsync();
    }
    // ...
}

And then I instantiate it with:

<AddressForm @ref="_addressElement"
             Context="@EditContext"
             ReadOnly="@(ModeState == Mode.Read)"
             OnValidated="OnAddressValidated"
             ValueChanged="@OnAddressChanged"/>

I need the ValueChanged to let me know if I need to hit the map API with the changed address.





相关问题
Blazor custom validation in child component

I have this component that goes into a form like <NumberWithNAAndAmberRanges2 @bind-BindNumber="LocVM.ChartDayStartMinute" NaName="Not Applicable" NaAllowed="true&...

Override/Disable Android Back Button in Blazor Hybrid MAUI

I have a .razor page which takes a lot of time to load. Is it possible to disable/override the hardware Back button in Blazor Hybrid MAUI for a specific .razor page? The primary reason for doing so is ...

Integrating Push Notifications in MAUI .NET 7 Applications

I m developing a cross-platform application using MAUI .NET 7 and I would like to add push notification functionality to my application. I m considering using Firebase Cloud Messaging or Azure ...

Blazor web assembly pass checkbox list values to model

I m new to Blazor. I m working in a web assembly Blazor project. I am trying to create a form that passes values back to a model. I have it working with input text fields and drop downs, but I am ...

热门标签