Blazor Events and Data Bindings

 In this article, we will see Blazor Events and Data Bindings.

When we start working with Blazor, we work with events as well like button clicks, dropdown select changes, text-box changes, mouse events and so many others, this is known as Event Handing.

 For example, the following code shows a button having an on-click event.

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

So, when this button is clicked then it calls the IncrementCount C# function. Inside this function, increment count logic is written.

Events in Blazor

Any HTML element like a button, dropdown or select, input, etc can be added to an event by adding an attribute to it. This attribute name is the@on which is followed by event name. In of case button it is@onclickevent,

   private void IncrementCount()
    {
        currentCount++;
    }

The value assigned to this attribute is the method (called as Handler Method) that will be invoked when the event is triggered. In case of button – @onclick="IncrementCount", “IncrementCount” is the method name.

The EventArgs Classes and their Events

Additional information about the event can be provided to the Handler Method by using the EventArgs class. For example, see the below-updated code of the button:

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount(ChangeEventArgs e)
    {
        currentCount++;
    }
}

The handler method now has a parameter of ChangeEventArgs class which is inherited from the EventArgs class, which contains information about the textbox event.

ClassEvents
ChangeEventArgs     onchange, oninput
KeyboardEventArgsonkeydown, onkeypress, onkeyup
ProgressEventArgsonabort, onload, onloadend, onloadstart, onprogress, ontimeout
ClipboardEventArgsoncopy, oncut, onpaste
MouseEventArgsonclick, oncontextmenu, ondblclick, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onmousewheel, onwheel

Behind the screen, this works in this way, the Blazor JavaScript code receives the event that is triggered and forwards it to the server over the persistent HTTP connection (SignalR). Then the handler method is invoked (on the server), and the state of the component is updated (on the server).

Then the server sends back the changes to the content to the JavaScript code, and this JavaScript code updates the content displayed by the browser. All this works without any page reload and instantly.

Input type text “onchange” event

Go to the counter razor component and add the following code to it.

@page "/counter"

<h3 >onchange text box change </h3>

<h4 style="color:red">@userName</h4>

<div class="form-group">
    <label>Enter User Name:</label>
    <input type="text" class="form-control" @onchange="userNameChanged" />
</div>

@code {
    public string userName { get; set; }

    private void userNameChanged(ChangeEventArgs e)
    {
        userName = e.Value as string;
    }
}



The above code has an input type text tag which has an onchange event of Blazor. It calls a handler method called userName.

The ChangeEventArgs parameter supplies information about the event to the hander. You can see I have grabbed the value of the text box as e.Value as string, and set the userName property to this value.

userName = e.Value as string;

The value of the userName property is shown inside the “h4” tag:

@userName




Single Handler for Events from Multiple Elements

To avoid code duplication, you can use a Single Hander method for receive events from multiple elements. See the below code that has 2 text boxes, one for receiving first name of the person and other for last name. There is only one hander method for 2 onchange events of text boxes.

@page "/counter"

<h3 class="bg-secondary text-white">@FullNameValue</h3>

<div class="form-group">
    <label>Full Name:</label>
    <input type="text" class="form-control" @onchange="@(e => UserFullName(e, null))" />
    <input type="text" class="form-control" @onchange="@(e => UserFullName(null, e))" />
</div>

@code {
    public string FullNameValue { get; set; }

    private void UserFullName(ChangeEventArgs e1, ChangeEventArgs e2)
    {
        if (e1 is not null)
            FullNameValue = e1.Value as string;
        if (e2 is not null)
            FullNameValue = FullNameValue + " " + e2.Value as string;
    }
}

Here I have used Lambda function of C# to invoke the UserFullName hander for the onchange events and passing to them 2 parameters – ChangeEventArgs & Null in an interchanging manner.

@onchange="@(e => UserFullName(e, null))"
@onchange="@(e => UserFullName(null, e))"

Then in the hander method, I use the if condition to fetch out the first and last names from the 2 text boxes.

If you run the above code, it will work as shown below:



If you don’t need to use the EventArgs object, then you can omit the parameter from the lambda function, as shown below:

<input type="text" class="form-control" @onchange="@(() => ShowMessage("Hello"))" />
Events without Hander method

You can also process events without using hander method, this is done by using Lambda function. See the below code which shows a hello message on the button’s click event. The button onclick event has lambda function which does the event handling without a hander method.

<h3 class="bg-secondary text-white">@Message</h3>
 
<div class="form-group">
    <label>Message:</label>
    <button class="form-control" @onclick="@(() => Message="Hello")">Show Message</button>
</div>
 
@code {
    private string Message { get; set; }
}
Select “onchange” event

The onchange event works well with the HTML Select tag to. Now place the select tag with “onchange” event as – @onchange="Option". See the below code:

@page "/counter"

<h3 class="bg-secondary text-white">@MyOption</h3>

<div class="form-group">
    <label>Users:</label>
    <select class="form-control" @onchange="Option">
        <option>Blazor</option>
        <option>DotNet</option>
        <option>Office</option>
    </select>
</div>

@code {
    public string MyOption { get; set; }

    private void Option(ChangeEventArgs e)
    {
        MyOption = e.Value as string;
    }
}

So, whenever an option is selected, then the handler method called “Option” is called. This handler method gets the selected value through the ChangeEventArgs type parameter. It updates the MyOption property value to the selected value, and then the MyOption property value is shown inside an h3 tag.

See the below video which shows it’s working.



Checkbox “onchange” event

The onchange event can also be applied to checkbox. The only thing to note here is that the checkbox’s value is of type bool so you need to caste it on the hander method. See the below code:

@page "/counter"

<h3 class="bg-secondary text-white">@AgreeValue</h3>

<div class="form-group">
    <label>Agree to terms:</label>
    <input type="checkbox" @onchange="Agree" />
</div>

@code {
    public bool AgreeValue { get; set; }

    private void Agree(ChangeEventArgs e)
    {
        AgreeValue = Convert.ToBoolean(e.Value);
    }
}

It works as shown in the given video:



Radio Button “onchange” event

Radio buttons can be used with onchange event to find out the user’s selection. Note that here only 1 selection out of multiple options can be made. The below code illustrates this thing. Note that the name of the radio buttons should be made the same as here I have given them the same name “gender”.

@page "/counter"

<h3 class="bg-secondary text-white">@GenderValue</h3>

<div class="form-group">
    <label>Select Gender:</label><br />
    <input type="radio" name="Gender" value="Male" @onchange="Gender" />  Male
    <br/>
    <input type="radio" name="Gender" value="Female" @onchange="Gender" />  Female
    <br />
    <input type="radio" name="Gender" value="Others" @onchange="Gender" />  Others
</div>

@code {
    public string GenderValue { get; set; }

    private void Gender(ChangeEventArgs e)
    {
        GenderValue = e.Value as string;
    }
}



 



Preventing Default Events (preventDefault) & Event Propagation (stopPropagation)

Suppose a button is kept inside an HTML form tag. Then on clicking the button, the form gets submitted. This is called the default behaviour of the event for the button.

Now suppose you add onclick event to the button which does some work (like increasing the counter by 1). You will notice that in this case, when you click the button, the form will get submitted but the button’s onclick event will not be executed. I have shown this problem in the below code:

@page "/counter"

<h3 class="bg-secondary text-white">@currentCount</h3>
<form action="/counter" method="get">
    <div class="form-group">
        <label>Counter:</label>
        <button class="form-control" @onclick="IncrementCount">Counter</button>
    </div>
</form>

@code {
    private int currentCount { get; set; }

    private void IncrementCount(MouseEventArgs e)
    {
        currentCount++;
    }
}


On clicking the button, the counter will now be increased, because the default behaviour of the buttons causes the problem and submits the form instead. This is shown in the below video:



“preventDefault” Parameter

The solution to this problem is by adding the preventDefault parameter with the onclick event and setting its value to “true”. The preventDefault parameter specifies whether to cancel the default action that belongs to the onclick event and takes a bool value. So update the code by adding one more onclick event with preventDefault parameter to the button – @onclick:preventDefault="true".

@page "/counter"

<h3 class="bg-secondary text-white">@currentCount</h3>
<form action="/counter" method="get">
    <div class="form-group">
        <label>Counter:</label>
        <button class="form-control" @onclick="IncrementCount" @onclick:preventDefault="true">Counter</button>
    </div>
</form>

@code {
    private int currentCount { get; set; }

    private void IncrementCount(MouseEventArgs e)
    {
        currentCount++;
    }
}


This will solve the problem and now the button click will start increasing the counter value. See the below video:



Now let us see another problem arising due to Event propagation. Suppose I have a div tag that contains a button. Both the div and button have onclick events. So, when I click the button then both the onclick events get’s executed. That is, first the onclick event of the button is executed and then the onclick event of the div gets executed. This is unexpected behaviour and happens due to the propagation of events from the child towards the parents.

I have created this problem in the below code:

@page "/counter"

<h3 class="bg-secondary text-white">@Message1</h3>
<h3 class="bg-secondary text-white">@Message2</h3>
<div class="form-group" @onclick="@(() => Message1="DotNet Office")">
    Show Message:
    <button class="form-control"@onclick="@(() => Message2="Blazor Article")">Show Message</button>
</div>

@code {
    private string Message1 { get; set; }
    private string Message2 { get; set; }
}

There are 2 properties called “Message1” & “Message2”. The div’s onclick event updates the “Message1” property to DotNet Office while the onclick event of the button updates the “Message2” property to Blazor Article. Now when you click the button then you will receive both DotNet Office & Blazor Article messages. This is because the div’s onclick event also gets called as the button is the child of this div.

I have shown this in the below video:





Note that event propagation happens from child till to root element of the DOM tree.
“stopPropagation” Parameter

The solution of this problem is by using stopPropagation parameter with the onclick event and set it to “true”. The stopPropagation parameter specifies whether to stop the event propagation of the onclick event, and you need to set it to true.

So add one more onclick event to the button tag and set stopPropagation to true – @onclick:stopPropagation="true".

@page "/counter"

<h3 class="bg-secondary text-white">@Message1</h3>
<h3 class="bg-secondary text-white">@Message2</h3>
<div class="form-group" @onclick="@(() => Message1="DotNet Office")">
    Show Message:
    <button class="form-control" @onclick="@(() => Message2="Blazor Article")" @onclick:stopPropagation="true">Show Message</button>
</div>

@code {
    private string Message1 { get; set; }
    private string Message2 { get; set; }
}

Now run the code and see output like below



Blazor Data Bindings

You can bind HTML controls with C# value using the Data Bindings feature of Blazor. You can simple bind the value attribute of the HTML control/tag with the C# property and this will show the C# property’s value inside the control. For example, in the below code, I am binding an input-type text tag to a C# property called Name.

<input class="form-control" value="@Name" />

@code {
    public string Name { get; set; } = "Blazor";
}

In the same way, you can use the value attribute in other HTML controls like select tag, radio button, etc. Let us take an example of the select control.

In the below given code, there is a select tag that let you select 3 frameworks given in it’s options. Note, its value attribute is bind to the Frameworks property. This property is defined in the code block.

There is a div tag which is given below the select control, and it shows the “ClassName” property’s value inside it. This code is given below.

@page "/counter"

<div class="form-group">
    <label>Class Name:</label>
    <select class="form-control" value="@ClassName">
        <option value="Select">Select Class</option>
        <option>Blazor</option>
        <option>Angular</option>
        <option>ASP.NET Core</option>
    </select>
</div>

<div class="p-2 mb-2">Selected class is : @ClassName</div>

@code {
    private string ClassName { get; set; } = "Blazor";
}

Now run the code and you will notice that the select control is bind with the “Blazor” value from the very start and the div will be showing the className’s value which is “Blazor”.

Now change the select value to Blazor, and you will notice that the div value does not change i.e. it remains the same as Blazor.

See the below video which shows these things.



The explanation of this thing is that here I have created only One Way Binding in Blazor. This means:

  • 1. Select control only got bind with the property value at the start.
  • 2. Select control is not able to change the value of this C# property.

Now I will update the code to create Two Way Binding in Blazor. I will only add onchange event to the select control and it’s hander method will update the C# property to the value of the selected option of the select control.

@page "/counter"

<div class="form-group">
    <label>Class Name:</label>
    <select class="form-control" value="@ClassName" @onchange="UpdateClassName">
        <option value="Select">Select Class</option>
        <option>Blazor</option>
        <option>Angular</option>
        <option>ASP.NET Core</option>
    </select>
</div>

<div class="p-2 mb-2">Selected class is : @ClassName</div>

@code {
    private string ClassName { get; set; } = "Blazor";

    private void UpdateClassName(ChangeEventArgs e)
    {
        ClassName = e.Value as string;
    }
}

Notice I added @onchange="UpdateClassName" to the select control and the hander method UpdateClassName inside the code block. Now run your app in the browser and notice the change in the selected option of the select control will automatically update the value of the ClassName property, and so the div now shows the updated value (which was not the case previously). This is Two Way Binding in Blazor.

See the below video which explains this scenario.



“@bind” attribute

The @bind attribute can be applied to an HTML control and it directly provides Two Way Binding for the control. The @bind attribute specifies the property that will be updated when the change event of the control is triggered and that will also update the value attribute of the control.

So now you do not need a handler method or a lambda function to update the property.

Now make changes in the above code. Remove the value attribute and the onchange event from the select control and instead place @bind attribute and assign it to the className property – @bind="ClassName". Also, comment out or remove the hander method as it is not needed anymore. Check the given code below:

@page "/counter"

<div class="form-group">
    <label>Class Name:</label>
    <select class="form-control" @bind="ClassName">
        <option value="Select">Select Class</option>
        <option>Blazor</option>
        <option>Angular</option>
        <option>ASP.NET Core</option>
    </select>
</div>

<div class="p-2 mb-2">Selected class is : @ClassName</div>

@code {
    private string ClassName { get; set; } = "Blazor";

    //private void UpdateClassName(ChangeEventArgs e)
    //{
    //    ClassName = e.Value as string;
    //}
}

Run and see output.



@bind and @onchange cannot be used together

Blazor prevents using @bind and @onchange attributes together. Now Suppose you come up with a situation where there is @bind attribute on a select element and you also need to do some other task on the onchange event. Then you should use this approach where you call a property with @onbind attribute and on the property set code, you do your task. See the below code which shows this thing.

@page "/counter"
@inject IJSRuntime runtime;

<div class="form-group">
    <label>Class Name:</label>
    <select class="form-control" @bind="ChangeClassName">
        <option value="Select">Select Class</option>
        <option>Blazor</option>
        <option>Angular</option>
        <option>ASP.NET Core</option>
        <option>NotLive</option>
    </select>
</div>

<div class="p-2 mb-2">Selected class is : @ClassName</div>

@code {
    private string ClassName { get; set; } = "ASP.NET Core";
    public string ChangeClassName
    {
        get { return ClassName; }
        set
        {
            ClassName = value;
            if (ClassName == "NotLive")
            {
              runtime.InvokeVoidAsync("alert", "Class is not live");
            }
        }
    }
}


This approach will come in handy in certain areas.



Changing the Default Binding Event – “@bind-event”

By default, the onchange event is used in bindings. This means when you apply @bind attribute to an input-type text element then the bindings will work only when the input element loses focus.

In the below code there is an “input type text” element which is bind to the UserName property using @bind attribute. One div element, which is placed below it, shows the userName’s property value.

@page "/counter"

<input type="text" class="form-control" @bind="UserName" />
<div class="p-2 mb-2">User Name is: @UserName</div>

@code {
    private string UserName { get; set; }
}

Once you run this code and write a userName on the input element. You will notice the username’s value shown by the div is not instantaneous. It gets updated only when the text box loses its focus i.e. when you press the tab key or click somewhere outside the text box.

This is because the onchange event is used by @bind attribute by default. This is the normal behaviour of onchange event which fires when the control loses focus. In below video I have shown this thing.



You can use the attribute called @bind:event="oninput" to change the binding event to oninput.

The updated code now becomes:

@page "/counter"

<input type="text" class="form-control" @bind="UserName" @bind:event="oninput" />
<div class="p-2 mb-2">User Name is: @UserName</div>

@code {
    private string UserName { get; set; }
}

Run it and check the input value is displayed instantaneously.



Showing DateTime with Culture specific and Formatted values in Blazor

The values of the DateTime types can be shown in a culture-specific manner. They can also be formatted according to you your needs.

In the below code, I am using @bind:format attribute to show the DOB variables value in yyyy-MM-dd type. It will show DOB as – 2022-10-31.

@page "/counter"

<input type="text" class="form-control" @bind="DOB" @bind:format="yyyy-MM-dd" />

@code {
    private DateTime DOB = new DateTime(2022, 10, 31);
}

In the next code, I am specifying the culture through @bind:culture attribute. There are 2 text boxes that show sales time in en-GB and fr-FR cultures.

@page "/counter"
@using System.Globalization

<input type="text" class="form-control" @bind="SaleTime" @bind:format="MMM-dd"  @bind:culture="CultureGB" />
<input type="text" class="form-control" @bind="SaleTime" @bind:format="MMM-dd" @bind:culture="CultureFR" />

@code {
    public DateTime SaleTime { get; set; } = DateTime.Parse("2022/10/31 10:00");
    public CultureInfo CultureFR { get; set; } = CultureInfo.GetCultureInfo("fr-FR");
    public CultureInfo CultureGB { get; set; } = CultureInfo.GetCultureInfo("en-gb");
}

The image shows their values formatted to be culture-specific.






---------------------------