Hello,
Can someone help with this?
I have a Custom Input Text based on Chris Sainty (https://chrissainty.com/building-custom-input-components-for-blazor-using-inputbase/)
I need to fill a combobox(input select) when add values to the input text.
So i add an event @oninput="@OnInputChange" and the change values works, but, with this event the ValidationMessage stop showing. if I remove @oninput="@OnInputChange" from the input type text, the validation messages show again.
I notice this because i have other text fields on the form that use the custom input type text and the error message stop showing.
//CustomValidationMessage
@using System.Linq.Expressions
@typeparam TValue
@implements IDisposable
@foreach (var message in EditContext.GetValidationMessages(_fieldIdentifier))
{
<div class="@Class">
@message
</div>
}
@code {
[CascadingParameter] private EditContext EditContext { get; set; }
[Parameter] public Expression<Func<TValue>> For { get; set; }
[Parameter] public string Class { get; set; }
private FieldIdentifier _fieldIdentifier;
protected override void OnInitialized()
{
_fieldIdentifier = FieldIdentifier.Create(For);
EditContext.OnValidationStateChanged += HandleValidationStateChanged;
}
private void HandleValidationStateChanged(object o, ValidationStateChangedEventArgs args) => StateHasChanged();
public void Dispose()
{
EditContext.OnValidationStateChanged -= HandleValidationStateChanged;
}
}
public class CustomInputBase<T> : InputBase<T>
{
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
{
try
{
if (typeof(T) == typeof(string))
{
result = (T)(object)value;
validationErrorMessage = null;
return true;
}
else if (typeof(T) == typeof(int))
{
int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue);
result = (T)(object)parsedValue;
validationErrorMessage = null;
return true;
}
else if (typeof(T) == typeof(Int16))
{
Int16.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue);
result = (T)(object)parsedValue;
validationErrorMessage = null;
return true;
}
else if (typeof(T) == typeof(Guid))
{
Guid.TryParse(value, out var parsedValue);
result = (T)(object)parsedValue;
validationErrorMessage = null;
return true;
}
else if (typeof(T) == typeof(DateTime))
{
DateTime.TryParse(value, out var parsedValue);
result = (T)(object)parsedValue;
validationErrorMessage = null;
return true;
}
else if (typeof(T).IsEnum)
{
// There's no non-generic Enum.TryParse (https://github.com/dotnet/corefx/issues/692)
try
{
result = (T)Enum.Parse(typeof(T), value);
validationErrorMessage = null;
return true;
}
catch (ArgumentException)
{
result = default;
validationErrorMessage = $"The {FieldIdentifier.FieldName} field is not valid.";
return false;
}
}
else
{
try
{
result = (T)(object)Helpers.Funcoes.ChangeType<T>(value);
validationErrorMessage = null;
return true;
}
catch (Exception ex)
{
result = default;
validationErrorMessage = $"The {FieldIdentifier.FieldName} field is not valid.";
return false;
}
}
}
catch (Exception ex)
{
//throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(T)}'.");
result = default;
validationErrorMessage = $"{GetType()} does not support the type '{typeof(T)}'.";
return false;
}
}
protected string fixClassNames(string inputClassNames)
{
//NOTE: Notice the space in front of the class name, this is to ensure we get
// the suffix to our existing form-control class set from the mark up and NOT
// half of an invalid tag. We could use a reg-ex but that might be a bit
// too slow for the UI renedering to stay smooth.
// The invalid string shall always be fixed up, as we can never get it until the
// element has chacked at least once by an attempted submit.
string result = inputClassNames.Replace(" invalid", " is-invalid");
// The valid tag is on by default, and to keep consistancy with BS4 we only want
// it to appear either when our field is modified, or we've tried a submit
if (inputClassNames.Contains("modified"))
{
result = result.Replace(" valid", " is-valid");
}
return result;
//FG - TODO: Isto ainda pode ser alterado NOUTRA FASE para passar como parametro o nome da nova CSS class
}
@using System.Linq.Expressions
@using System.Globalization;
@typeparam T
@inherits CustomInputBase<T>
@inject IJSRuntime JSRuntime
<div class="form-control-wrapper">
@if (!string.IsNullOrWhiteSpace(Label))
{
<label for="@Id">@Label</label> @*class="form-control-label"*@
}
<input class="@cssFormControl @fixClassNames(CssClass)" id="@Id" @bind="@CurrentValueAsString" @bind:event="@Event"
placeholder="@PlaceHolder" aria-label="@AriaLabel" readonly="@ReadOnly" @ref="inputElement" @oninput="@OnInputChange"/>
@if (ValidationFor != null)
{
<div class="form-control-validation">
<CustomValidationMessage For="@ValidationFor" Class="@ValidationMessageCssClass" />
</div>
}
</div>
@code {
[Parameter] public string Id { get; set; }
[Parameter] public string Label { get; set; } = "";
[Parameter] public Expression<Func<T>> ValidationFor { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public bool ShowDefaultOption { get; set; } = true;
[Parameter] public string PlaceHolder { get; set; } = "";
[Parameter] public string AriaLabel { get; set; } = "";
[Parameter] public string ValidationMessageCssClass { get; set; } = "validation-message";
[Parameter] public bool ReadOnly { get; set; } = false;
[Parameter] public string Event { get; set; } = "onchange";
private string cssFormControl = "form-control";
private string ReadOnlyAttribute => ReadOnly ? "readonly" : "";
private ElementReference inputElement;
public ValueTask FocusAsync()
{
return JSRuntime.FocusAsync(inputElement);
}
//public async Task OnInputHandler(KeyboardEventArgs args)
//{
// await TextChanged.InvokeAsync(args.Key.ToString());
//}
[Parameter]
public EventCallback<string> ValueChanging { get; set; }
private async Task OnInputChange(ChangeEventArgs args)
{
Value = Funcoes.GetValue<T>(args.Value.ToString());
await ValueChanging.InvokeAsync(Value.ToString());
}
}
Usage:
<CtmInputText Label="Código Postal" AriaLabel="Código Postal" PlaceHolder="Formato:0000-000"
Id="txtCodigoPostal" @bind-Value="CodigoPostalSelecionado.Codigo_Postal" ValueChanging="OnEscrevendoCodigoPostal"
ReadOnly="false" />