Search code examples
blazor-server-sidedevexpress-blazor

Custom Blazor Component not reflecting binding properly


I have a big DxFormLayout with many DxFormLayoutGroups and each DxFormLayoutGroup has many DxFormlauoutItems. I'm wondering if it is possible to sompress the razor code by componentizing a few DxFormlauoutItems. Here is my code:

Page:

<EditForm Model="@_model" Context="editFormContext" OnValidSubmit="@HandleValidSubmit">
    <DataAnnotationsValidator />

    <DxFormLayout CssClass="dxFormLayoutHeaderStyle">       
        <DxFormLayoutGroup Caption="Options" ColSpanMd="12">
            <DxFormLayoutItem Caption="" ColSpanMd="12">
                @*The below is repeated multiple times*@
                <Template>
                    <DxStackLayout ItemSpacing="10px">
                        <Items>
                            <DxStackLayoutItem Length="95%">
                                <Template>
                                    <DxTextBox ReadOnly="true" @bind-Text="@_model.TooltipTextForNegotiationsDoneBySupplyManagement"
                                               title="@_model.TooltipTextForNegotiationsDoneBySupplyManagement" SizeMode="SizeMode.Medium"/>
                                </Template>
                            </DxStackLayoutItem>
                            <DxStackLayoutItem Length="5%">
                                <Template>
                                    <div class="stacklayout-item">
                                        <DxCheckBox CheckType="CheckType.Switch" style="width: 100%" @bind-Checked="@_model.IsNegotiationsDoneBySupplyManagement"
                                                    Alignment="CheckBoxContentAlignment.Center" title="@_model.TooltipTextForNegotiationsDoneBySupplyManagement"/>
                                    </div>
                                </Template>
                            </DxStackLayoutItem>
                        </Items>
                    </DxStackLayout>
                </Template >
                @*The above is repeated multiple times*@
            </DxFormLayoutItem >
        </DxFormLayoutGroup>            
    </DxFormLayout>
</EditForm>

@*The below become a component with parameters and bindings*@

<EditForm Model="@_model" Context="editFormContext" OnValidSubmit="@HandleValidSubmit">
    <DataAnnotationsValidator />

    <DxFormLayout CssClass="dxFormLayoutHeaderStyle">       
        <DxFormLayoutItem Caption="" ColSpanMd="12">
            <Template>
                <BudgetReleaseRequestDecisionPoint DecisionText="@_model.TooltipTextForNegotiationsDoneBySupplyManagement"
                                                   DecisionResult="@_model.IsNegotiationsDoneBySupplyManagement" />
            </Template >
        </DxFormLayoutItem >            
    </DxFormLayout>
</EditForm>

Component:

<style>
    .stacklayout-item {
        text-align: center;
        height: 100%;
        padding-top: 6px;
    }
</style>

<DxStackLayout ItemSpacing="10px">
    <Items>
        <DxStackLayoutItem Length="95%">
            <Template>
                <DxTextBox ReadOnly="true" @bind-Text="@DecisionText" title="@DecisionText" SizeMode="SizeMode.Medium"/>
            </Template>
        </DxStackLayoutItem>
        <DxStackLayoutItem Length="5%">
            <Template>
                <div class="stacklayout-item">
                    <DxCheckBox CheckType="CheckType.Switch" style="width: 100%" @bind-Checked="@DecisionResult"
                                Alignment="CheckBoxContentAlignment.Center" title="@DecisionResult"/>
                </div>
            </Template>
        </DxStackLayoutItem>
    </Items>
</DxStackLayout>

@code {
    [Parameter]
    public string DecisionText
    {
        get => _decisionText;
        set
        {
            if (_decisionText == value) return;

            _decisionText = value;
            DecisionTextChanged.InvokeAsync(value);
        }
    }

    [Parameter]
    public bool DecisionResult
    {
        get => _decisionResult;
        set
        {
            if (_decisionResult == value) return;

            _decisionResult = value;
            DecisionResultChanged.InvokeAsync(value);
        }
    }

    [Parameter]
    public EventCallback<string> DecisionTextChanged { get; set; }
    [Parameter]
    public EventCallback<bool> DecisionResultChanged { get; set; }

    private string _decisionText;
    private bool _decisionResult;
}

Issue:

I made it a razor component but I'm having issue as the model's properties are not getting updated on the main page. I can confirm this by one property: On the page, there is a SpinEdit that get enabled once model.IsNegotiationsDoneBySupplyManagement is set to true. That is not happening anymore once I went to component-mode:

<DxSpinEdit Id="amountSavedAfterNegotiations" @bind-Value="@_model.SavingsAfterNegotiations" Enabled="@_model.IsNegotiationsDoneBySupplyManagement" title="Savings (AED) after negotiations?" />

When I had the original code (without component/top-most code I pasted), togeling this checkbox would toggle the Enabled state of the SpinEdit. After I transferred to component, the Enabled state is not longer sensing changes to the model's property leading me to believe the model's properties on the page are not getting updated.

What is wrong with the way I wired the component?


Solution

  • The missing magic was on the Page where I was calling the components. This is what I was doing:

    <BudgetReleaseRequestDecisionPoint DecisionText="@_model.TooltipTextForNegotiationsDoneBySupplyManagement" DecisionResult="@_model.IsNegotiationsDoneBySupplyManagement" />
    

    This is the correct syntax:

    <BudgetReleaseRequestDecisionPoint DecisionText="@_model.TooltipTextForNegotiationsDoneBySupplyManagement" @bind-DecisionResult="@_model.IsNegotiationsDoneBySupplyManagement" />
    

    I had to change the DecisionResult to @bind-DecisionResult. Now the Page's model is reflecting the changes that occur to its properties with in component.