blazor input type text - oninput custom event, validation message not run on click out

Posted 3 years ago by xicoJosue
Edited 3 years ago
0

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" />
  • 0

    I don't see how you're getting the Value for

    Value = Funcoes.GetValue<T>(args.Value.ToString());

    However, do you see any error messages in your browser console? My first guess is there's a JavaScript error occurring that's causing the functionality to break.

    Posted 3 years ago by selliott
  • 0

    Here:

     public static T ChangeType<T>(object value)
            {
                try
                {
                    var t = typeof(T);
    
                    if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        if (value == null)
                            return default(T);
    
                        t = Nullable.GetUnderlyingType(t);
                    }
    
                    return (T)Convert.ChangeType(value, t);
                }
                catch
                {
                    return default;
                }
            }
    
            public static T GetValue<T>(String value)
            {
                return ChangeType<T>(value);
            }
    Posted 3 years ago by xicoJosue
Someone is typing...

Post a Reply

You must be logged in to add a new post.
Number of online users: 0
An error has occurred. This application may no longer respond until reloaded. Reload 🗙