5

I try to capture text changes of InputText in Blazor (ServerSide) and then call a async method to check if input is correct coupon code or not.

HTML:

<EditForm Model="@Basket" OnValidSubmit="@CommitBasket">
    Gutscheincode
    <InputText class="coupon-input checkout-control pristine untouched" 
            @bind-Value="CouponCode"
            @onchange="CouponCodeChanged">
    </InputText>
    @CouponText
</EditForm>`

But The CouponCodeChanged isn't raised - OnKeyUp is not useful because the Text Value isn't changed at that state...

here is C# Method:

public async Task CouponCodeChanged(ChangeEventArgs args)
{
    using var client = ClientFactory.CreateClient();
    var result = await ApiCalls.GetCouponCodeData(client, AppState.BaseUrl, 2, CouponCode);
    CouponText = result== null ? "NOT FOUND" : $"exists:{result.Exists} amount:{result.Amount}";
}

Has anyone a hint or idea how to solve that in a convinient way?

Thx!

1

5 Answers 5

3

InputText component doesn't have a onchange event but ValueChanged.
However, if you want to subscribe to this event you need to pass a ValueExpression and it's not easy. The easiest I found to subscribe to this event is to override the InputText component.

CouponComponent.razor

@inherits InputText
<InputText class="coupon-input checkout-control pristine untouched" 
                   ValueExpression="ValueExpression" Value="@Value" ValueChanged="OnValueChanged" />
@code {
  [Parameter]
  public EventCallback<string> CouponValueChanged { get; set; }

  private async Task OnValueChanged(string value)
  {
     await ValueChanged.InvokeAsync(value);
     using var client = ClientFactory.CreateClient();
     var result = await ApiCalls.GetCouponCodeData(client, AppState.BaseUrl, 2, CouponCode);
     var coupon = result == null ? "NOT FOUND" : $"exists:{result.Exists} amount:{result.Amount}"
     await CouponValueChanged.InvokeAsync(coupon);
  }
}

Usage

<EditForm Model="@Basket" OnValidSubmit="@CommitBasket">
    Gutscheincode
    <CouponComponent @bind-Value="CouponCode"
        @CouponValueChanged="CouponCodeChanged">
    </InputText>
    @CouponText
</EditForm>

@code {
   private void CouponCodeChanged(string coupon)
   {
      CouponText = coupon;
      StateHasChanged();
   }
}
Sign up to request clarification or add additional context in comments.

Comments

3

This is working for me...

@using System.Linq.Expressions 

   <p>@Coupon Text: @CouponText</p>



    <EditForm Model="@basket" OnValidSubmit="@CommitBasket">
        Gutscheincode

            <InputText class="coupon-input checkout-control pristine untouched" Value="@basket.CouponCode" ValueChanged="@CouponCodeChanged" ValueExpression="@(() => basket.CouponCode)" />

    </EditForm>

    @code {
        private Basket basket = new Basket();
        private string CouponText;

        private string _value { get; set; }
        [Parameter]
        public string Value
        {
            get { return _value; }
            set
            {
                if (_value != value)
                {
                    _value = value;
                    if (ValueChanged.HasDelegate)
                    {
                        ValueChanged.InvokeAsync(value);
                    }
                }


            }
        }

        [Parameter] public EventCallback<string> ValueChanged { get; set; }
        [Parameter] public Expression<Func<string>> ValueExpression { get; set; }


        private void CouponCodeChanged(string CouponCode)
        {
            basket.CouponCode = CouponCode;
            CouponText =  basket.CouponCode;
        }

        private void CommitBasket()
        { }

        public class Basket
        {
            public Basket() { }
            public string CouponCode { get; set; } = "12345";
            public string CouponName { get; set; } = "Coupon Name";

        }
    }

You can also do it with the KeyUp event:

<InputText Id="CouponCode" Class="form-control" @bind-Value="@basket.CouponCode" @onkeyup='@(e => KeyUp(e, "CouponCode"))'></InputText>


@code {
    private EditContext EditContext;
    private Basket basket = new Basket();

     protected override void OnInitialized()
    {
        EditContext = new EditContext(basket);
        base.OnInitialized();
    }

    void KeyUp(KeyboardEventArgs e, string memberName)
    {
        var property = basket.GetType().GetProperty(memberName);
        var value = property.GetValue(basket);

        // Requires some tweaks to prevent unwanted stuff...
        property.SetValue(basket, value + e.Key);

        var id = EditContext.Field(memberName);


        EditContext.NotifyFieldChanged(id);
        property = basket.GetType().GetProperty(memberName);

        CouponText = property.GetValue(basket).ToString();

    }
}

Comments

2

Try this:

@onchange="(ChangeEventArgs _event) => CouponCodeChanged(_event.Value.ToString())"

Update method:

public async Task CouponCodeChanged(string coupon)
...
..., AppState.BaseUrl, 2, coupon);
...

Comments

1

I have created a set of Blazor components. One of which is Debounced inputs with multiple input types and much more features. Blazor.Components.Debounce.Input is available on NuGet.

You can try it out with the demo app.

Note: currently it is in Preview. Final version is coming with .NET 5. release

Comments

1

With .Net 7, we can use the :after directive, which allows you to call asynchronous methods after the bound property is (synchronously) updated.

The Forms and Components documentation contains some useful stuff, however, it doesn't provide a full answer to your problem.

Firstly, create a CustomInputText component, inheriting from InputText, bound to the oninput event, which will fire every time a key is pressed.

@inherits InputText

<input @attributes="AdditionalAttributes" 
       class="@CssClass" 
       @bind="CurrentValueAsString" 
       @bind:event="oninput" />

Now, use that component, binding your value to a property (this will still be synchronous, but once the key is pressed, rather than after losing focus), but also use the :after directive to then call your asynchronous method after the property has been updated:

  <CustomInputText @bind-Value="CouponCode" @bind-Value:after="DoAsyncStuff" />

@code { 
  [Parameter]
  public string? CouponCode { get; set; }

    private async Task DoAsyncStuff()
    {
        await Foo(this.CouponCode);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.