Ivan-Mark Debono
Ivan-Mark Debono

Reputation: 16280

How to set event callback automatically in inherited component?

Currently I have:

<InputText @oninput="@SomeHandler" />

Now I need to inherit from InputText using a class-only component and I want to the set event handler automatically in this new component, so then I would use the new component like this:

<CustomInputText />

How can I set the event handler in the custom component?

Upvotes: 1

Views: 1341

Answers (1)

enet
enet

Reputation: 45586

<InputText @oninput="@SomeHandler" />

The @oninput is a compiler attribute directive intended to be used with Html tags, not with components. InputText is a component tag, not Html tag. In the past, such application was ignored or raised compilation error; currently, however, compiler attribute directives may be added by the compiler silently, but often may cause subtle issues that may be hard to discern. You should avoid this by all means.

If you want to subclass InputText and use it like this: <CustomInputText />,

you simply have to derive from the InputText, like this:

public partial class CustomInputText : InputText
{
   // Add here the internal work...
}

and use it on the EditForm (Note: it must be embedded in EditForm component) , like this:

<EditForm>
   <CustomInputText />
</EditForm>

I would like to set òninput=SomeHandler (or an equivalent) directly in the ctor or OnAfterRender of the custom component

It is important to understand that though a component is a C# class, it is a special case of a class, and you shouldn't try to use it as ordinary C# class. As for instance, you shouldn't instantiate a component like this:

CustomInputText MyCustomInputText = new CustomInputText();

And then call its methods and properties as if it was an ordinary C# class object. You should instantiate it as a component element like this:

<CustomInputText @bind-Value="myValue" /> 

This is because the rendering engine of the component model do the instantiation correctly... Now, if you instantiate your component as though it was an ordinary C# class object, you'll skip the handling of the rendering engine, which in the best cases render your component useless. I'm telling you this because you should forget the ides of constructor. No constructor...

Blazor has life cycles methods which you should override when your component is being initialized.

Below is a code sample describing how to subclass the InputText component...copy test and learn:

NumericInputText.razor.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;


 public partial class NumericInputText : InputText
    {
        protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.OpenElement(0, "input");
            __builder.AddAttribute(1, "type", "number");
            __builder.AddMultipleAttributes(2, AdditionalAttributes);
            __builder.AddAttribute(3, "class", CssClass);
            __builder.AddAttribute(4, "value", CurrentValueAsString);
            __builder.AddEventPreventDefaultAttribute(5, "onkeypress", true);
            __builder.AddAttribute(6, "onblur", Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.FocusEventArgs>(this, OnBlurEventHandler));
            __builder.AddAttribute(7, "onkeydown", Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.KeyboardEventArgs>(this, OnKeyDown));
            __builder.AddAttribute(8, "oninput", Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.ChangeEventArgs>(this, OnInput));
            __builder.CloseElement();
        }

        protected override void OnInitialized()
        {
            CurrentValueAsString = "0";
        }

        private void OnInput(ChangeEventArgs args)
        {
         
            if (args.Value.ToString() == "")
            {
                CurrentValueAsString = "0";
            }
            else
            {
                CurrentValueAsString = args.Value.ToString();

            }
        }

        private void OnKeyDown(KeyboardEventArgs e)
        {
            if (e.Key == "Backspace" && CurrentValueAsString.Length == 1)
            {
                CurrentValueAsString = "";
            }


            if (e.Key == "+")
            {
                var value = int.Parse(CurrentValueAsString);
                value++;
                CurrentValueAsString = value.ToString();
            }
            else if (e.Key == "-")
            {
                var value = int.Parse(CurrentValueAsString);
                value--;
                CurrentValueAsString = value.ToString();
            }


            if (Regex.IsMatch(e.Key, "[0-9]"))
            {
                CurrentValueAsString += int.Parse($"{e.Key}").ToString();

            }

        }

        protected void OnBlurEventHandler(Microsoft.AspNetCore.Components.Web.FocusEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(CurrentValueAsString))
            {
                CurrentValueAsString = "0";
            }

        }

    }  

Usage

<EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
    <div class="alert @StatusClass">@StatusMessage</div>
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="form-group">
        <label for="name">Name: </label>
        <NumericInputText Id="name" Class="form-control" @bind-Value="@Model.NumberSelection" />
        <ValidationMessage For="@(() => Model.NumberSelection)" />
    </div>


    <button type="submit">Ok</button>

</EditForm>

@code {

    private string StatusMessage;
    private string StatusClass;

    private Comment Model = new Comment();

    protected void HandleValidSubmit()
    {
        StatusClass = "alert-info";
        StatusMessage = DateTime.Now + " Handle valid submit";
    }

    protected void HandleInvalidSubmit()
    {
        StatusClass = "alert-danger";
        StatusMessage = DateTime.Now + " Handle invalid submit";
    }


    public class Comment
    {
        public string NumberSelection { get; set; }

    }

}

Upvotes: 1

Related Questions