Search code examples
c#blazorblazor-editform

Blazor pass ValidationMessage to extended InputText component


I have an ExtendedInputText component which inherits from InputText

@inherits InputText

<div class="flex">
    <label class="w-1/2">
        @Label 
        @if(Required){
            <span class="text-red-500 ml-1">*</span>
        }
    </label>
    <InputText
        class="flex-1 border border-gray-200 bg-white p-2 rounded"
        placeholder="@Label"
        Value="@Value"
        ValueChanged="@ValueChanged"
        ValueExpression="@ValueExpression"
        Required="@Required"
    />
    
</div>

@code
{

    [Parameter]
    public bool Required { get; set; }

    [Parameter]
    public string Label { get; set; }
}

I intend on using it to replace this

<EditForm Model="Command" OnValidSubmit="OnValidSubmit">

  <FluentValidationValidator />
  <ValidationSummary />

  <div class="">
    <label>Title <span class="text-red-500">*</span></label>
    <InputText id="Title" @bind-Value="Command.Title" />
    <ValidationMessage For="@(() => Command.Title)" />
  </div>

  <button type="submit" class="p-2 bg-positive-500 text-white rounded">Create</button>

</EditForm>

with this

<EditForm Model="Command" OnValidSubmit="OnValidSubmit">

  <FluentValidationValidator />
  <ValidationSummary />

  <ExtendedInputText Label="Title" Required="true" @bind-Value="Command.Title"/>

  <button type="submit" class="p-2 bg-positive-500 text-white rounded">Create</button>

</EditForm>

How would I go about also passing <ValidationMessage For="@(() => Command.Title)" /> to the ExtendedInputText component and rendering it from within?


Solution

  • With the help of Nicola and Shaun, this is the solution that worked for me.

    @inherits InputText
    
    <div class="flex">
        <label class="w-1/2 text-right font-semibold mr-1 py-2">
            @Label
            @if (Required)
            {
                <span class="text-red-500 ml-1">*</span>
            }
        </label>
        <div class="flex-1">
            <InputText class="w-full border border-gray-200 bg-white p-2 rounded"
                        placeholder="@Label"
                        Value="@Value"
                        ValueChanged="@ValueChanged"
                        ValueExpression="@ValueExpression"
                        Required="@Required"/>
            @ValidationFragment
        </div>
    </div>
    
    @code
    {
    
        [Parameter]
        public bool Required { get; set; }
    
        [Parameter]
        public string Label { get; set; }
    
        private RenderFragment ValidationFragment => (builder) =>
        {
            var messages = EditContext.GetValidationMessages(FieldIdentifier).ToList();
            if(messages is not null && messages.Count > 0)
            {
                builder.OpenElement(310, "div");
                builder.AddAttribute(320, "class", "text-red-500 p-2 w-full");
                builder.OpenComponent<ValidationMessage<string>>(330);
                builder.AddAttribute(340, "For", ValueExpression);
                builder.CloseComponent();
                builder.CloseElement();
            }
    
        };
    
    }
    

    They key part was the private RenderFragment ValidationFragment which is built programatically to display associated errors stored in the cascading EditContext