Pass Validation context to child component?

I'm trying to figure out how it would be possible to notify a child component in the Data Form that validation is happening and to apply the validation styling to that control. 

Take for example this custom component called TestComp.razor

<SfComboBox TValue="int?" TItem="Model" Placeholder="Model" @bind-Value="@ModelId" DataSource="@_models">
    <ComboBoxFieldSettings Value="Id" Text="Name"></ComboBoxFieldSettings>
    <ComboBoxEvents TValue="int?" TItem="Model" OnValueSelect="OnValueSelectHandler"></ComboBoxEvents>
</SfComboBox>

@code {
    private List<Model> _models = [];
    [Parameter] public int? ModelId { get; set; }
    [Parameter] public EventCallback<int?> ModelIdChanged { get; set; }

    protected override void OnInitialized()
    {
        _models.Add(new Model { Id = 1, Name = "Model 1" });
        _models.Add(new Model { Id = 2, Name = "Model 2" });
        _models.Add(new Model { Id = 3, Name = "Model 3" });
    }

    private async Task OnValueSelectHandler(SelectEventArgs<Model> args)
    {
        if (args.ItemData is not null) ModelId = args.ItemData.Id;

        await ModelIdChanged.InvokeAsync(ModelId);
    }

    private class Model
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
    }
}

I want this to be a separate component on not a field directly on the form because this control along with fetching the data is used in several different places in the application and it seems silly to have to duplicate the same code over again instead of making it its own component.

And there is a page which contains the Form and the child component:

@page "/Test"
@using System.ComponentModel.DataAnnotations
@attribute [StreamRendering]
@rendermode @(new InteractiveServerRenderMode(false))

<SfDataForm Model="@_asset">
    <FormValidator>
        <DataAnnotationsValidator></DataAnnotationsValidator>
    </FormValidator>
    <FormItems>
        <FormItem Field="@nameof(Asset.SerialNumber)" LabelText="Serial Number"></FormItem>
        <FormItem Field="@nameof(Asset.ModelId)">
            <Template>
                <TestComp @bind-ModelId="@_asset.ModelId"></TestComp>
            </Template>
        </FormItem>
    </FormItems>
</SfDataForm>

@code {

    private Asset _asset = new();

    private class Asset
    {
        [Required]
        public int? ModelId { get; set; }

        [Required]
        public string SerialNumber { get; set; } = string.Empty;
    }
}



The way it is working right now, the data annotation validation message is appearing below the custom component on submit but the combo box is not being highlighted in red like other controls. Is there a way to trigger this within the child component?


4 Replies

JS Joe Sperlak February 23, 2024 08:23 AM UTC

For reference here is the result of the code in my post. I want the combo box control in the child component to be outlined in red on the 
SF_DataForm_Validation.png


KP Kokila Poovendran Syncfusion Team March 19, 2024 10:11 AM UTC

Hi Joe Sperlak,


Thank you for bringing this concern to our attention. After investigating the validation issue you encountered, it appears that the @bind-value in the child component was directly linked to the parent component. Consequently, validation was restricted to the model property value, and the error class wasn't associated with the component due to the absence of a ValueExpression.


To address this, we suggest configuring the parent component with essential parameters, including ValueExpression. Additionally, instead of relying solely on the OnValueSelect event to refresh the parent component's value, you can manage this within the value property's getter and setter methods.


Below is an example code demonstrating these adjustments:


[CustomTextBox.razor]

<SfComboBox TValue="int?" TItem="Model" Placeholder="Model" Value="@ModelId" ValueChanged="ModelIdChanged" DataSource="@_models" ValueExpression="@ModelIdExpression">

    <ComboBoxFieldSettings Value="Id" Text="Name"></ComboBoxFieldSettings>

 

</SfComboBox>

 

@code {

    private List<Model> _models = new List<Model>();

    private int? _value { get; set; }

    [Parameter]

    public int? ModelId

    {

        get => _value;

        set

        {

            if (!EqualityComparer<int?>.Default.Equals(value, _value))

            {

                _value = value;

                ModelIdChanged.InvokeAsync(value);

            }

        }

    }

 

    [Parameter] public EventCallback<int?> ModelIdChanged { get; set; }

 

   [Parameter]

    public Expression<Func<int?>> ModelIdExpression { get; set; }

 

  

    protected override void OnInitialized()

    {

        _models.Add(new Model { Id = 1, Name = "Model 1" });

        _models.Add(new Model { Id = 2, Name = "Model 2" });

        _models.Add(new Model { Id = 3, Name = "Model 3" });

    }

 

    private class Model

    {

        public int Id { get; set; }

        public string Name { get; set; } = string.Empty;

    }

}


With these adjustments, the validation should now function as expected, and the error styling should be correctly applied to the child component.


If you have any further questions or need additional assistance, please feel free to reach out.


Attachment: BlazorApp1_e604c9d3.zip


MG MohammadRaoof Garousi replied to Kokila Poovendran September 24, 2024 08:48 AM UTC

hello  Kokila Poovendran

I had the same problem and your solution solved my problem.

But I need to access its valuechange from outside when I use my component.

do u have any solution?



PK Priyanka Karthikeyan Syncfusion Team October 8, 2024 01:54 PM UTC

Hi Joe Sperlak,

 

Thank you for the update and your continued patience. To access the ValueChanged event of your SfComboBox (inside the CustomTextbox.razor component) from outside the component, you can pass an event callback (EventCallback) from the parent component and invoke it when the value changes. Here's how you can implement this:

 

CustomTextBox.razor

 

 

<SfComboBox TValue="int?" TItem="Model" ValueExpression="ModelIdExpression" Placeholder="Model" Value="@ModelId" ValueChanged="OnModelIdChanged" DataSource="@_models">
<ComboBoxFieldSettings Value="Id" Text="Name"></ComboBoxFieldSettings>
</SfComboBox>

 

@code {

 

// External event callback for ValueChanged
    [Parameter]
    public EventCallback<int?> ModelIdChanged { get; set; }

 

// External event callback to notify outside component about value change
    [Parameter]
    public EventCallback<int?> ExternalValueChanged { get; set; }

 

    private async Task OnModelIdChanged(int? value)
    {
// Update the ModelId value
        ModelId = value;

 

// Trigger the external event when the value changes
        if (ExternalValueChanged.HasDelegate)
        {
            await ExternalValueChanged.InvokeAsync(value);
        }
    }
}

 

 

 

Index.razor

 

 

<SfDataForm Model="@_asset">
<FormValidator>
<DataAnnotationsValidator></DataAnnotationsValidator>
</FormValidator>
<FormItems>
<FormItem Field="@nameof(Asset.SerialNumber)" LabelText="Serial Number"></FormItem>
<FormItem Field="@nameof(Asset.ModelId)">
<Template>
<BlazorApp1.Components.CustomTextbox @bind-ModelId="@_asset.ModelId" ExternalValueChanged="HandleModelIdChange"></BlazorApp1.Components.CustomTextbox>
</Template>
</FormItem>
</FormItems>
</SfDataForm>

 

@code {

 

    // Handler for external value changes
private Task HandleModelIdChange(int? newModelId)
    {
        Console.WriteLine($"External ModelId changed to: {newModelId}");
        // You can perform any additional logic here if needed
        return Task.CompletedTask;
    }
}

 

 

 

Regards,

Priyanka K



Attachment: BlazorApp1_3f52bcec.zip

Loader.
Up arrow icon