Skip to content

kb(Form): Add KB for FluentValidation exception and revamp existing example #3017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 99 additions & 29 deletions components/form/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ You can use the built-in `DataAnnotationsValidator` that comes with the Blazor f
</FormValidation>
</TelerikForm>


@code {
public Person person { get; set; } = new Person();

Expand All @@ -158,8 +157,7 @@ You can use the <a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/fo

When using a model with nested objects and fields, specify their `Field` settings as a dot-separate string, do *not* use the `nameof` operator, it does not return the full name of the model.

<div class="skip-repl"></div>
````RAZOR
````RAZOR.skip-repl
@using System.Dynamic
@using System.ComponentModel.DataAnnotations

Expand Down Expand Up @@ -228,50 +226,122 @@ When using a model with nested objects and fields, specify their `Field` setting

### Fluent Validation

You can use third-party validation libraries that integrate with the standard `EditContext` such as <a href="https://fluentvalidation.net/" target="_blank">FluentValidation</a> together with the Telerik Form for Blazor.
You can use third-party validation libraries that integrate with the standard `EditContext` such as [FluentValidation](https://fluentvalidation.net/) together with the Telerik Form for Blazor.

>note Such third party tools are not included with the Telerik UI for Blazor package. Your project must reference their NuGet packages explicitly. The code snippet below will not run unless you install the an appropriate package first. You can find some in <a href="https://docs.fluentvalidation.net/en/latest/blazor.html" target="_blank">their official documentation</a>.
The example below:

* Requires the [`Blazored.FluentValidation` NuGet package](https://www.nuget.org/packages/Blazored.FluentValidation). Also refer to the [FluentValidation documentation](https://docs.fluentvalidation.net/en/latest/blazor.html).
* Shows how to pass `ValueExpression` from the parent component to custom child components in a [Form item template](slug:form-formitems-template) or a [Grid editor template](slug:grid-templates-editor). If the `ValueExpression` is not passed correctly, the app will throw [exception similar to: `Cannot validate instances of type 'ComponentName'. This validator can only validate instances of type 'ModelClassName'`](slug:form-kb-fluent-validation-cannot-validate-instances-of-type).

>caption Using FluentValidation

<div class="skip-repl"></div>
````RAZOR
@using Microsoft.AspNetCore.Components.Forms
@using FluentValidation
````RAZOR Home.razor
@using Blazored.FluentValidation
@using FluentValidation

<div class="mt-4" style="margin: 0 auto;">
<TelerikForm EditContext="@EditContext">
<FormValidation>
<FluentValidationValidator Validator="@Validator"></FluentValidationValidator>
</FormValidation>
</TelerikForm>
</div>
<TelerikForm Model="@PersonToEdit"
OnValidSubmit="@OnFormValidSubmit">
<FormValidation>
<FluentValidationValidator Validator="@PersonFluentValidator" />
<TelerikValidationSummary />
</FormValidation>
<FormItems>
<FormItem Field="@nameof(Person.Id)" Enabled="false" LabelText="ID" />
<FormItem Field="@nameof(Person.FirstName)" LabelText="First Name" />
<FormItem Field="@nameof(Person.MiddleName)">
<Template>
<label for="person-middlename" class="k-label k-form-label">Middle Name (two-way binding)</label>
<div class="k-form-field-wrap">
<TextBox @bind-Value="@PersonToEdit.MiddleName"
Id="person-middlename" />
<TelerikValidationMessage For="@( () => PersonToEdit.MiddleName )" />
</div>
</Template>
</FormItem>
<FormItem Field="@nameof(Person.LastName)">
<Template>
<label for="person-lastname" class="k-label k-form-label">Last Name (one-way binding with explicit ValueExpression)</label>
<div class="k-form-field-wrap">
<TextBox Value="@PersonToEdit.LastName"
ValueChanged="@LastNameChanged"
ValueExpression="@( () => PersonToEdit.LastName )"
Id="person-lastname" />
<TelerikValidationMessage For="@( () => PersonToEdit.LastName )" />
</div>
</Template>
</FormItem>
</FormItems>
</TelerikForm>

<p style="color:var(--kendo-color-success)"><strong>@FormSubmitResult</strong></p>

@code {
public EditContext EditContext {get; set; }
public Customer MyModel { get; set; } = new Customer();
public CustomerValidator Validator { get; set; } = new CustomerValidator();
private Person PersonToEdit { get; set; } = new();

public PersonValidator PersonFluentValidator { get; set; } = new();

protected override void OnInitialized()
private string FormSubmitResult { get; set; } = string.Empty;

private void LastNameChanged(string newLastName)
{
EditContext = new EditContext(MyModel);
base.OnInitialized();
PersonToEdit.LastName = newLastName;
}

public class Customer
private void OnFormValidSubmit()
{
public string FirstName { get; set; }
public string LastName { get; set; }
FormSubmitResult = $"Form Submit Success at {DateTime.Now.ToString("HH:mm:ss")}";
}

public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(customer => customer.FirstName).NotEmpty().MinimumLength(2).MaximumLength(60);
RuleFor(customer => customer.MiddleName).NotEmpty().MaximumLength(60);
RuleFor(customer => customer.LastName).NotEmpty().MinimumLength(2).MaximumLength(60);
}
}

public class Person
{
public int Id { get; set; }

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

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

public string LastName { get; set; } = string.Empty;
}
}
````
````RAZOR TextBox.razor
@using System.Linq.Expressions

<TelerikTextBox Value="@Value"
ValueChanged="@ValueChanged"
ValueExpression="@ValueExpression"
Id="@Id" />

@code {
[Parameter]
public string Value { get; set; } = string.Empty;

[Parameter]
public EventCallback<string> ValueChanged { get; set; }

[Parameter]
public Expression<System.Func<string>>? ValueExpression { get; set; }

public class CustomerValidator : AbstractValidator<Customer>
[Parameter]
public string Id { get; set; } = string.Empty;

private async Task TextBoxValueChanged(string newValue)
{
public CustomerValidator()
Value = newValue;

if (ValueChanged.HasDelegate)
{
RuleFor(customer => customer.FirstName).NotEmpty().MaximumLength(50);
RuleFor(customer => customer.LastName).NotEmpty().MaximumLength(50);
await ValueChanged.InvokeAsync(newValue);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: FluentValidation Exception Cannot Validate Instances of Type 'ComponentName'
description: Learn how to troubleshoot and resolve runtime exceptions Cannot Validate Instances of Type 'ComponentName'. This Validator Can only Validate Instances of Type 'ModelClassName' in Blazor apps.
type: troubleshooting
page_title: FluentValidation Exception Cannot Validate Instances of Type 'ComponentName'. This Validator Can only Validate Instances of Type 'ModelClassName'
slug: form-kb-fluent-validation-cannot-validate-instances-of-type
tags: telerik, blazor, form, validation
ticketid: 1689449, 1644783, 1670949, 1672711, 1668735, 1595169, 1539619
res_type: kb
---

## Environment

<table>
<tbody>
<tr>
<td>Product</td>
<td>
Form for Blazor, <br />
Grid for Blazor
</td>
</tr>
</tbody>
</table>

## Description

The KB article deals with the following scenarios that throw a runtime exception. It explains what causes the error and how to fix it.

* A Telerik Form is using a `FluentValidation` validator. A custom input component in a [`FormItem` `Template`](slug:form-formitems-template) crashes the page on value edit.
* An Telerik Grid with inline, incell or popup `EditMode` is using `<FluentValidationValidator>` for validation. One of the Grid columns has an `<EditorTemplate>` with a custom component that wraps a `<TelerikTextBox>`.

In both cases the exception message that occurs on value edit in the custom component is:

`Cannot validate instances of type 'ComponentName'. This validator can only validate instances of type 'ModelClassName'`

In general, the exception may occur when using `FluentValidation` with inputs that are wrapped in custom child components.

## Cause

The exception occurs, because the custom child component that holds the input is not receiving the correct `ValueExpression` from the parent component that holds the edit form.

The issue is not related to or caused by Telerik UI for Blazor. The same behavior can occur with a standard Blazor `EditForm` and `InputText` components.

## Solution

1. Define a public parameter of type `Expression<System.Func<T>>`, which receives the correct expression from the parent component. `T` is the value type, which the custom child component is editing. The parameter name must be consistent with the other two related parameter names that deal the two-way value binding:
* `Foo`
* `FooChanged`
* `FooExpression`
1. Pass the validation expression from the parent to the child component. There are two possible options:
* Use two-way binding for the value parameter (`@bind-Foo`) in the parent component.
* Pass the expression explicitly by setting `FooExpression` in the parent component.

For a full runnable example, refer to [Form Fluent Validation](slug:form-validation#fluent-validation).

>caption Using ValueExpression for validation in child components

```RAZOR TextBox.razor
@using System.Linq.Expressions

<TelerikTextBox Value="@Value"
ValueChanged="@ValueChanged"
ValueExpression="@ValueExpression" />

@code {
[Parameter]
public string Value { get; set; } = string.Empty;

[Parameter]
public EventCallback<string> ValueChanged { get; set; }

[Parameter]
public Expression<System.Func<string>>? ValueExpression { get; set; }

private async Task TextBoxValueChanged(string newValue)
{
Value = newValue;

if (ValueChanged.HasDelegate)
{
await ValueChanged.InvokeAsync(newValue);
}
}
}
````
````RAZOR Home.razor
<TextBox @bind-Value="@PersonToEdit.FirstName" />

or

<TextBox Value="@PersonToEdit.FirstName"
ValueChanged="@FirstNameChanged"
ValueExpression="@( () => PersonToEdit.FirstName )" />

@code {
private Person PersonToEdit { get; set; } = new();

private void FirstNameChanged(string newValue)
{
PersonToEdit.FirstName = newValue;
}

public class Person
{
public string FirstName { get; set; } = string.Empty;
}
}
````

## See Also

* [Form Validation](slug:form-validation#fluent-validation)
* [Form Item Templates](slug:form-formitems-template)
Loading