Grid and Fluent Validator show tooltip
I am attempting to use Fluent validator with DataGrid in Editmode Normal. I want to show the tooltips for each error returned from validation and its only somewhat working. In the photo I simply click Add and then Update, I expect a tooltip for Type, Name, Code, and Hierarchy Name to show up but only the last one (Hierarchy Name) shows.
Once I fill Hierarchy Name, then this happens
Along with other strange behaviour
Fluent Validator for Model: ----------------------------------------------------------------------------------------------------
public FluentValidation.Results.ValidationResult ValidationResults()
{
var validator = new FluentValidator();
return validator.Validate(this);
}
public class FluentValidator : AbstractValidator<CostCenter>
{
public FluentValidator()
{
RuleFor(x => x.CostCenterType).NotEmpty().WithMessage("Type Required");
RuleFor(x => x.Code).NotEmpty().WithMessage("Code Required");
RuleFor(x => x.Code).MaximumLength(PayableCodeBase.MAX_CODE_LENGTH).WithMessage($"Code must be less than {PayableCodeBase.MAX_CODE_LENGTH} characters");
RuleFor(x => x.Name).NotEmpty().WithMessage("Name Required");
RuleFor(x => x.Name).MaximumLength(PayableCodeBase.MAX_NAME_LENGTH).WithMessage($"Name must be less than {PayableCodeBase.MAX_NAME_LENGTH} characters");
RuleFor(x => x.HierarchyDetail).NotNull().SetValidator(new HierarchyDetailNameValidator());
}
}
DataGrid Code: ----------------------------------------------------------------------------------------------------
<SfGrid @ref="_sfGrid" Height="100%" DataSource="_costCenters" EnableVirtualization="true" EnableVirtualMaskRow="true" AllowSorting="true" AllowMultiSorting="true" AllowFiltering="true" Toolbar="_toolbaritems" >
<GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" ShowDeleteConfirmDialog="true" AllowEditOnDblClick="true" Mode="Syncfusion.Blazor.Grids.EditMode.Normal">
<Validator>
@{
<Ep.WebApp.Components.GridFluentValidator Context="(ValidatorTemplateContext)context" TValidator="CostCenter.FluentValidator"></Ep.WebApp.Components.GridFluentValidator>
}
</Validator>
</GridEditSettings>
<GridEvents TValue="CostCenter" OnActionBegin="OnActionBeginHandlerAsync" OnActionComplete="OnActionCompleteHandlerAsync" OnActionFailure="OnActionFailureHandler" OnToolbarClick="ToolbarClickHandler"></GridEvents>
<GridFilterSettings Type="Syncfusion.Blazor.Grids.FilterType.FilterBar"></GridFilterSettings>
<GridColumns>
<GridColumn Field="@nameof(CostCenter.Id)" HeaderText="Guid" Visible="false" IsPrimaryKey="true"></GridColumn>
<GridColumn Field="@nameof(CostCenter.CostCenterType)" HeaderText="Type">
<EditTemplate>
<SfDropDownList ID="CostCenterType" AllowFiltering="true" TValue="CostCenter.CostCenterTypes?" TItem="string" DataSource="@_costCenterTypes" @bind-Value="(context as CostCenter).CostCenterType"></SfDropDownList>
</EditTemplate>
<FilterTemplate>
<SfDropDownList ShowClearButton="true" TValue="string" TItem="string" DataSource="@_costCenterTypes" Value="@((string)(context as PredicateModel).Value)">
<DropDownListEvents TItem="string" ValueChange="FilterCostCenterType" TValue="string"></DropDownListEvents>
</SfDropDownList>
</FilterTemplate>
</GridColumn>
<GridColumn Field="@nameof(CostCenter.Code)" HeaderText="Code" FilterSettings="@(new FilterSettings{ Operator = Operator.Contains })"></GridColumn>
<GridColumn Field="@nameof(CostCenter.Name)" HeaderText="Name" FilterSettings="@(new FilterSettings{ Operator = Operator.Contains })"></GridColumn>
<GridColumn Field="HierarchyDetail.Name" HeaderText="Hierarchy Name" FilterSettings="@(new FilterSettings{ Operator = Operator.Contains })">
<EditTemplate>
@{
<SfDropDownList ID="HierarchyDetail___Name" AllowFiltering="true" TValue="string" TItem="string" DataSource="_hierarchyDetailNames" @bind-Value="@((context as CostCenter).HierarchyDetail.Name)">
</SfDropDownList>
}
</EditTemplate>
</GridColumn>
...
</SfGrid>
Fluent Validator and DataGrid middleman (pulled from here: https://www.syncfusion.com/faq/blazor/forms-and-validation/how-do-i-enable-validation-without-using-the-dataannotationvalidator ) : ------------------------------------------------------------------------------------------------
public class GridFluentValidator<TValidator> : ComponentBase where TValidator : IValidator, new()
{
private readonly static char[] _separators = new[] { '.', '[' };
private TValidator _validator;
[Parameter]
public ValidatorTemplateContext Context { get; set; }
[CascadingParameter] private EditContext _editContext { get; set; }
protected override void OnInitialized()
{
_validator = new TValidator();
var messages = new ValidationMessageStore(_editContext);
// Revalidate when any field changes, or if the entire form requests validation
// (e.g., on submit)
_editContext.OnFieldChanged += (sender, eventArgs)
=> ValidateModel((EditContext)sender, messages);
_editContext.OnValidationRequested += (sender, eventArgs)
=> ValidateModel((EditContext)sender, messages);
}
private void ValidateModel(EditContext editContext, ValidationMessageStore messages)
{
var model = new ValidationContext<object>(editContext.Model);
var validationResult = _validator.Validate(model);
messages.Clear();
foreach (var error in validationResult.Errors)
{
var fieldIdentifier = ToFieldIdentifier(editContext, error.PropertyName);
messages.Add(fieldIdentifier, error.ErrorMessage);
Context.ShowValidationMessage(error.PropertyName.Replace(".", "___"), false, error.ErrorMessage);
}
_editContext.NotifyValidationStateChanged();
}
private static FieldIdentifier ToFieldIdentifier(EditContext editContext, string propertyPath)
{
// This method parses property paths like 'SomeProp.MyCollection[123].ChildProp'
// and returns a FieldIdentifier which is an (instance, propName) pair. For example,
// it would return the pair (SomeProp.MyCollection[123], "ChildProp"). It traverses
// as far into the propertyPath as it can go until it finds any null instance.
var obj = editContext.Model;
while (true)
{
var nextTokenEnd = propertyPath.IndexOfAny(_separators);
if (nextTokenEnd < 0)
{
return new FieldIdentifier(obj, propertyPath);
}
var nextToken = propertyPath.Substring(0, nextTokenEnd);
propertyPath = propertyPath.Substring(nextTokenEnd + 1);
object newObj;
if (nextToken.EndsWith("]"))
{
// It's an indexer
// This code assumes C# conventions (one indexer named Item with one param)
nextToken = nextToken.Substring(0, nextToken.Length - 1);
var prop = obj.GetType().GetProperty("Item");
var indexerType = prop.GetIndexParameters()[0].ParameterType;
var indexerValue = Convert.ChangeType(nextToken, indexerType);
newObj = prop.GetValue(obj, new object[] { indexerValue });
}
else
{
// It's a regular property
var prop = obj.GetType().GetProperty(nextToken);
if (prop == null)
{
throw new InvalidOperationException($"Could not find property named {nextToken} on object of type {obj.GetType().FullName}.");
}
newObj = prop.GetValue(obj);
}
if (newObj == null)
{
// This is as far as we can go
return new FieldIdentifier(obj, nextToken);
}
obj = newObj;
}
}
}
Hi Glenn ,
Before proceeding with
the reporting problem, we require some additional clarification from your end.
Please share the below details to proceed further at our end.
- Share with us the NuGet version you have used.
- How should the Hierarchy field validator (new HierarchyDetailNameValidator) be managed on your end?
- Please provide us with a simple, reproducible sample of the issue.
- If possible, kindly share with us your attempts to reproduce the reported issue on the attached sample.
Above-requested details will be very helpful in validating the reported query at our end and providing a solution as early as possible.
Regards,
Prathap S
Attachment: DatagridSample_70203691.zip
Thank you for your reply Prathap,
I took your attached sample and made a few changes, although I don't have those little bubble popups as shown in the original post, I have a working sample now. The below image shows the functionality I was able to achieve which I was then able to take and apply to my original code.
Thank you,
Glenn
Attachment: BlazorApp1_4df3e794.zip
Thanks for the update. We are happy to hear that the issue has been resolved on your end.
We are closing the thread now.
- 3 Replies
- 2 Participants
- Marked answer
-
GS Glenn Storch
- Oct 2, 2023 05:43 PM UTC
- Nov 6, 2023 02:47 AM UTC