Skip to content

Commit a392d63

Browse files
dimodiyordan-mitev
andauthored
kb(Form): Add KB for FluentValidation exception and revamp existing example (#3017)
* kb(Form): Add KB for FluentValidation exception and revamp existing example * Update knowledge-base/inputs-validation-child-component.md Co-authored-by: Yordan <[email protected]> * Update knowledge-base/inputs-validation-child-component.md Co-authored-by: Yordan <[email protected]> --------- Co-authored-by: Yordan <[email protected]>
1 parent e78f9b3 commit a392d63

File tree

4 files changed

+353
-137
lines changed

4 files changed

+353
-137
lines changed

components/form/validation.md

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ You can use the built-in `DataAnnotationsValidator` that comes with the Blazor f
133133
</FormValidation>
134134
</TelerikForm>
135135
136-
137136
@code {
138137
public Person person { get; set; } = new Person();
139138
@@ -158,8 +157,7 @@ You can use the <a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/fo
158157

159158
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.
160159

161-
<div class="skip-repl"></div>
162-
````RAZOR
160+
````RAZOR.skip-repl
163161
@using System.Dynamic
164162
@using System.ComponentModel.DataAnnotations
165163
@@ -228,50 +226,122 @@ When using a model with nested objects and fields, specify their `Field` setting
228226

229227
### Fluent Validation
230228

231-
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.
229+
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.
232230

233-
>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>.
231+
The example below:
234232

233+
* 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).
234+
* 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).
235235

236236
>caption Using FluentValidation
237237
238-
<div class="skip-repl"></div>
239-
````RAZOR
240-
@using Microsoft.AspNetCore.Components.Forms
241-
@using FluentValidation
238+
````RAZOR Home.razor
242239
@using Blazored.FluentValidation
240+
@using FluentValidation
243241
244-
<div class="mt-4" style="margin: 0 auto;">
245-
<TelerikForm EditContext="@EditContext">
246-
<FormValidation>
247-
<FluentValidationValidator Validator="@Validator"></FluentValidationValidator>
248-
</FormValidation>
249-
</TelerikForm>
250-
</div>
242+
<TelerikForm Model="@PersonToEdit"
243+
OnValidSubmit="@OnFormValidSubmit">
244+
<FormValidation>
245+
<FluentValidationValidator Validator="@PersonFluentValidator" />
246+
<TelerikValidationSummary />
247+
</FormValidation>
248+
<FormItems>
249+
<FormItem Field="@nameof(Person.Id)" Enabled="false" LabelText="ID" />
250+
<FormItem Field="@nameof(Person.FirstName)" LabelText="First Name" />
251+
<FormItem Field="@nameof(Person.MiddleName)">
252+
<Template>
253+
<label for="person-middlename" class="k-label k-form-label">Middle Name (two-way binding)</label>
254+
<div class="k-form-field-wrap">
255+
<TextBox @bind-Value="@PersonToEdit.MiddleName"
256+
Id="person-middlename" />
257+
<TelerikValidationMessage For="@( () => PersonToEdit.MiddleName )" />
258+
</div>
259+
</Template>
260+
</FormItem>
261+
<FormItem Field="@nameof(Person.LastName)">
262+
<Template>
263+
<label for="person-lastname" class="k-label k-form-label">Last Name (one-way binding with explicit ValueExpression)</label>
264+
<div class="k-form-field-wrap">
265+
<TextBox Value="@PersonToEdit.LastName"
266+
ValueChanged="@LastNameChanged"
267+
ValueExpression="@( () => PersonToEdit.LastName )"
268+
Id="person-lastname" />
269+
<TelerikValidationMessage For="@( () => PersonToEdit.LastName )" />
270+
</div>
271+
</Template>
272+
</FormItem>
273+
</FormItems>
274+
</TelerikForm>
275+
276+
<p style="color:var(--kendo-color-success)"><strong>@FormSubmitResult</strong></p>
251277
252278
@code {
253-
public EditContext EditContext {get; set; }
254-
public Customer MyModel { get; set; } = new Customer();
255-
public CustomerValidator Validator { get; set; } = new CustomerValidator();
279+
private Person PersonToEdit { get; set; } = new();
280+
281+
public PersonValidator PersonFluentValidator { get; set; } = new();
256282
257-
protected override void OnInitialized()
283+
private string FormSubmitResult { get; set; } = string.Empty;
284+
285+
private void LastNameChanged(string newLastName)
258286
{
259-
EditContext = new EditContext(MyModel);
260-
base.OnInitialized();
287+
PersonToEdit.LastName = newLastName;
261288
}
262289
263-
public class Customer
290+
private void OnFormValidSubmit()
264291
{
265-
public string FirstName { get; set; }
266-
public string LastName { get; set; }
292+
FormSubmitResult = $"Form Submit Success at {DateTime.Now.ToString("HH:mm:ss")}";
293+
}
294+
295+
public class PersonValidator : AbstractValidator<Person>
296+
{
297+
public PersonValidator()
298+
{
299+
RuleFor(customer => customer.FirstName).NotEmpty().MinimumLength(2).MaximumLength(60);
300+
RuleFor(customer => customer.MiddleName).NotEmpty().MaximumLength(60);
301+
RuleFor(customer => customer.LastName).NotEmpty().MinimumLength(2).MaximumLength(60);
302+
}
303+
}
304+
305+
public class Person
306+
{
307+
public int Id { get; set; }
308+
309+
public string FirstName { get; set; } = string.Empty;
310+
311+
public string MiddleName { get; set; } = string.Empty;
312+
313+
public string LastName { get; set; } = string.Empty;
267314
}
315+
}
316+
````
317+
````RAZOR TextBox.razor
318+
@using System.Linq.Expressions
319+
320+
<TelerikTextBox Value="@Value"
321+
ValueChanged="@ValueChanged"
322+
ValueExpression="@ValueExpression"
323+
Id="@Id" />
324+
325+
@code {
326+
[Parameter]
327+
public string Value { get; set; } = string.Empty;
328+
329+
[Parameter]
330+
public EventCallback<string> ValueChanged { get; set; }
331+
332+
[Parameter]
333+
public Expression<System.Func<string>>? ValueExpression { get; set; }
268334
269-
public class CustomerValidator : AbstractValidator<Customer>
335+
[Parameter]
336+
public string Id { get; set; } = string.Empty;
337+
338+
private async Task TextBoxValueChanged(string newValue)
270339
{
271-
public CustomerValidator()
340+
Value = newValue;
341+
342+
if (ValueChanged.HasDelegate)
272343
{
273-
RuleFor(customer => customer.FirstName).NotEmpty().MaximumLength(50);
274-
RuleFor(customer => customer.LastName).NotEmpty().MaximumLength(50);
344+
await ValueChanged.InvokeAsync(newValue);
275345
}
276346
}
277347
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
title: FluentValidation Exception Cannot Validate Instances of Type 'ComponentName'
3+
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.
4+
type: troubleshooting
5+
page_title: FluentValidation Exception Cannot Validate Instances of Type 'ComponentName'. This Validator Can only Validate Instances of Type 'ModelClassName'
6+
slug: form-kb-fluent-validation-cannot-validate-instances-of-type
7+
tags: telerik, blazor, form, validation
8+
ticketid: 1689449, 1644783, 1670949, 1672711, 1668735, 1595169, 1539619
9+
res_type: kb
10+
---
11+
12+
## Environment
13+
14+
<table>
15+
<tbody>
16+
<tr>
17+
<td>Product</td>
18+
<td>
19+
Form for Blazor, <br />
20+
Grid for Blazor
21+
</td>
22+
</tr>
23+
</tbody>
24+
</table>
25+
26+
## Description
27+
28+
The KB article deals with the following scenarios that throw a runtime exception. It explains what causes the error and how to fix it.
29+
30+
* 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.
31+
* 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>`.
32+
33+
In both cases the exception message that occurs on value edit in the custom component is:
34+
35+
`Cannot validate instances of type 'ComponentName'. This validator can only validate instances of type 'ModelClassName'`
36+
37+
In general, the exception may occur when using `FluentValidation` with inputs that are wrapped in custom child components.
38+
39+
## Cause
40+
41+
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.
42+
43+
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.
44+
45+
## Solution
46+
47+
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:
48+
* `Foo`
49+
* `FooChanged`
50+
* `FooExpression`
51+
1. Pass the validation expression from the parent to the child component. There are two possible options:
52+
* Use two-way binding for the value parameter (`@bind-Foo`) in the parent component.
53+
* Pass the expression explicitly by setting `FooExpression` in the parent component.
54+
55+
For a full runnable example, refer to [Form Fluent Validation](slug:form-validation#fluent-validation).
56+
57+
>caption Using ValueExpression for validation in child components
58+
59+
```RAZOR TextBox.razor
60+
@using System.Linq.Expressions
61+
62+
<TelerikTextBox Value="@Value"
63+
ValueChanged="@ValueChanged"
64+
ValueExpression="@ValueExpression" />
65+
66+
@code {
67+
[Parameter]
68+
public string Value { get; set; } = string.Empty;
69+
70+
[Parameter]
71+
public EventCallback<string> ValueChanged { get; set; }
72+
73+
[Parameter]
74+
public Expression<System.Func<string>>? ValueExpression { get; set; }
75+
76+
private async Task TextBoxValueChanged(string newValue)
77+
{
78+
Value = newValue;
79+
80+
if (ValueChanged.HasDelegate)
81+
{
82+
await ValueChanged.InvokeAsync(newValue);
83+
}
84+
}
85+
}
86+
````
87+
````RAZOR Home.razor
88+
<TextBox @bind-Value="@PersonToEdit.FirstName" />
89+
90+
or
91+
92+
<TextBox Value="@PersonToEdit.FirstName"
93+
ValueChanged="@FirstNameChanged"
94+
ValueExpression="@( () => PersonToEdit.FirstName )" />
95+
96+
@code {
97+
private Person PersonToEdit { get; set; } = new();
98+
99+
private void FirstNameChanged(string newValue)
100+
{
101+
PersonToEdit.FirstName = newValue;
102+
}
103+
104+
public class Person
105+
{
106+
public string FirstName { get; set; } = string.Empty;
107+
}
108+
}
109+
````
110+
111+
## See Also
112+
113+
* [Form Validation](slug:form-validation#fluent-validation)
114+
* [Form Item Templates](slug:form-formitems-template)

0 commit comments

Comments
 (0)