Open eholman opened 9 months ago
@eholman Thank you for using BlazorBootstrap. To help us better understand the issue you're facing, please share a minimal code sample that reproduces the problem. Additionally, please provide a sample reference for the validation message you're expecting, rather than allowing us to make assumptions.
Thanks for the quick reply! Will get back to you in a bit.
So, don't mind the beauty of the code, it does show the issue. Based on the starter template.
I expect that the (in)validation mark doesn't overlap with the clear button of the component. I assume the most logical way to place the items is the (in)validation mark first, then the clear button of the autocomplete?
Home.razor
@page "/"
@using Blazored.FluentValidation
@using FluentValidation
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
<EditForm EditContext="_context" OnSubmit="@Callback">
<FluentValidationValidator @ref="_validator"/>
<ValidationSummary></ValidationSummary>
<div class="container">
<div class="row">
<div class="col-4 d-flex flex-column justify-content-center">Item</div>
<div class="col-8">
<AutoComplete TItem="AutoCompleteItem" @bind-Value="_viewModel.AutoCompleteValue"
DataProvider="DataProvider"
PropertyName="@nameof(AutoCompleteItem.Name)"
Placeholder="Search..."/>
</div>
</div>
</div>
<button type="submit">Submit</button>
</EditForm>
@code{
public class MyViewModel
{
public string? AutoCompleteValue { get; set; }
}
public class AutoCompleteItem(string name)
{
public string Name { get; set; } = name;
}
MyViewModel _viewModel = new();
private EditContext? _context;
private ValidationMessageStore? _validationMessageStore;
private FluentValidationValidator? _validator;
/// <inheritdoc />
protected override void OnInitialized()
{
_context = new EditContext(_viewModel);
_context.SetFieldCssClassProvider(new ValidatedFieldCssProvider());
_validationMessageStore = new ValidationMessageStore(_context);
base.OnInitialized();
}
private Task<AutoCompleteDataProviderResult<AutoCompleteItem>> DataProvider(AutoCompleteDataProviderRequest<AutoCompleteItem> request)
{
var customers = new List<AutoCompleteItem>
{
new("Item 1"), new("Item 2"), new("Item 3")
};
return Task.FromResult(new AutoCompleteDataProviderResult<AutoCompleteItem> { Data = customers, TotalCount = 10 });
}
public class ValidatedFieldCssProvider : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
if (editContext.IsModified(fieldIdentifier))
{
return isValid ? "is-valid" : "is-invalid";
}
else
{
return isValid ? "" : "is-invalid";
}
}
}
private Task Callback(EditContext obj)
{
_validator!.Validate();
return Task.CompletedTask;
}
public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
/// <inheritdoc />
public MyViewModelValidator()
{
RuleFor(model => model.AutoCompleteValue).NotEmpty();
}
}
}
csproj:
<PackageReference Include="Blazor.Bootstrap" Version="1.10.4" />
<PackageReference Include="Blazored.FluentValidation" Version="2.1.0" />
<PackageReference Include="FluentValidation" Version="11.8.1" />
@eholman I'll take a look at your sample code. I have a question: Did you check the AutoComplete validation on our demos website? Please let me know if this information is helpful.
Screenshot:
Link: https://demos.blazorbootstrap.com/autocomplete#validations
Code:
@using System.ComponentModel.DataAnnotations
<style>
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid red;
}
.validation-message {
color: red;
}
</style>
<EditForm EditContext="@_editContext" OnValidSubmit="HandleOnValidSubmit">
<DataAnnotationsValidator />
<div class="form-group row mb-2">
<label class="col-md-2 col-form-label">Customer:</label>
<div class="col-md-10">
<AutoComplete @bind-Value="customerAddress.CustomerName"
TItem="Customer2"
DataProvider="CustomersDataProvider"
PropertyName="CustomerName"
Placeholder="Search a customer..."
OnChanged="(Customer2 customer) => OnAutoCompleteChanged(customer)" />
<ValidationMessage For="@(() => customerAddress.CustomerName)" />
</div>
</div>
<div class="form-group row mb-3">
<label class="col-md-2 col-form-label">Address:</label>
<div class="col-md-10">
<InputText class="form-control" @bind-Value="customerAddress.Address" />
<ValidationMessage For="@(() => customerAddress.Address)" />
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<button type="submit" class="btn btn-success float-right">Submit</button>
</div>
</div>
</EditForm>
@code {
private CustomerAddress customerAddress = new();
private EditContext _editContext;
[Inject] ICustomerService _customerService { get; set; }
protected override void OnInitialized()
{
_editContext = new EditContext(customerAddress);
base.OnInitialized();
}
public void HandleOnValidSubmit()
{
Console.WriteLine($"Customer name is {customerAddress.CustomerName} and address is {customerAddress.Address}");
}
private async Task<AutoCompleteDataProviderResult<Customer2>> CustomersDataProvider(AutoCompleteDataProviderRequest<Customer2> request)
{
var customers = await _customerService.GetCustomersAsync(request.Filter, request.CancellationToken); // API call
return await Task.FromResult(new AutoCompleteDataProviderResult<Customer2> { Data = customers, TotalCount = customers.Count() });
}
private void OnAutoCompleteChanged(Customer2 customer)
{
// TODO: handle your own logic
// NOTE: do null check
Console.WriteLine($"'{customer?.CustomerName}' selected.");
}
public class CustomerAddress
{
[Required]
public string CustomerName { get; set; }
[Required]
public string Address { get; set; }
}
}
Thanks; wasn't aware of the example in the docs. It looks good though!
But, this is an existing project, which uses the default Bootstrap validation style implementation via with the FieldCssClassProvider
.
When using validation on an autocomplete component there is an overlap of validated and clean elements:
Besides, is there an option to get the validation message displayed? Bootstrap requires that the message is in the same parent element to get its style applied.