Blazor is an experimental web framework that is quickly evolving. Therefore, it is possible that some of the content on this blog post is already outdated.

Data Binding in Blazor

As you might already know, most modern web frameworks (Angular, React, Vue) offer different forms of communication between a data source and the UI. This communication is known as data binding. Let’s look at the different ways you can achieve data binding with Blazor.

One-Way Data Binding

One-way data binding is also known as interpolation in other frameworks. It’s all about moving data in one direction from the “Model” to HTML elements. In Blazor’s case, the “Model” refers to variables. You just need to add the @ symbol to your variable name (for example, @yourVariable) and you’re done! Take a look at the example below to see one-way data binding in action.

Example

In the following example, an orderd list is created by iterating over a List of strings containing the top ten automobile companies of 2018.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@page "/top-ten"

<h3>Top 10 Automobile Companies in the World</h3>

<ol>
    @foreach (var company in autoCompanies)
    {
        <li>@company</li>
    }
</ol>

@functions{
    List<string> autoCompanies { get; set; } = new List<string>
    {
        "Toyota Motor Corporation", "Volkswagen Group",
        "Daimler AG", "BMW", "Honda", "General Motors",
        "Tesla Inc.", "Ford", "Nissan", "Fiat Chrysler Automobiles"
    };
}

Output

Two-Way Data Binding

In Blazor, two-way data binding is achieved with the bind attribute. You specify the variable you want to bind inside the bind attribute (for example, <input bind="@yourVariable" />). Blazor will automatically detect changes on your HTML element and will update the variable accordingly and viceversa.

NOTE

Blazor uses the onchange event attribute when the bind attribute is used on input elements.

For example, <input bind="@yourVariable" /> is equivalent to:

1
<input value="@yourVariable" onchange="@((UIChangeEventArgs e) => yourVariable = e.Value.ToString())" />

Therefore, if you want Blazor to update your variable on every keystroke, you must use the following alternative:

1
<input bind-value-oninput="@yourVariable" />

This is because, unlike onchange, oninput fires for every keystroke. Now, let’s look at an example of two-way data binding.

Example

In this example, three different HTML elements (<input type="text" />, <select>, and <input type="checkbox" />) use the bind attribute to achieve two-way data binding.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@page "/company-form"

<h2>Automobile Company Form</h2>
<hr />

<label for="name">Your name:</label>
<input type="text" name="name" bind-value-oninput="@name" />

<br />

<label for="favoriteCompany">Select your favorite company:</label>
<select name="favoriteCompany" bind="@favoriteCompany">
    @foreach (var company in autoCompanies)
    {
        <option value="@company">@company</option>
    }
</select>

<br />

<label for="promoEmails">Receive promotional emails?</label>
<input type="checkbox" name="promoEmails" bind="@sendPromoEmails" />

<br /><br />

<h3>Your Information</h3>
<hr />

<p>Your name: <b>@name</b></p>
<p>Favorite company: <b>@favoriteCompany</b></p>
<p>Send me promotional emails: <b>@sendPromoEmails.ToString()</b></p>

@functions{
    string name { get; set; } = string.Empty;
    bool sendPromoEmails { get; set; } = false;
    string favoriteCompany { get; set; } = "BMW";
    List<string> autoCompanies { get; set; } = new List<string>
    {
        "Toyota Motor Corporation", "Volkswagen Group",
        "Daimler AG", "BMW", "Honda", "General Motors",
        "Tesla Inc.", "Ford", "Nissan", "Fiat Chrysler Automobiles"
    };
}

Output

Event Binding

With Blazor you can take advantage of event-driven programming by using event binding. This is achieved with HTML event attributes like, onclick, onchange, and onkeypress. You must specify the method you want to call on the event attribute (for example, onclick="@YourMethod"). If you need to pass arguments to your method, you must use a Lambda expression (for example, onclick="@(() => YourMethod(someArgument, anotherArgument))"). For some events, you can access event-specific arguments supported by Blazor.

 Blazor supports the following event arguments:
  • UIEventArgs
  • UIChangeEventArgs
  • UIKeyboardEventArgs
  • UIMouseEventArgs

Let’s take a look at an example where the onclick event attribute is used to call a method with and without event arguments. You’ll see that it works even if we don’t pass the event argument explicitly.

Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@page "/mouse-coordinates"

<h1>Mouse pointer location</h1>

<p>Mouse coordinates (X, Y): @clientX, @clientY</p>

<!--Without arguments-->
<button class="btn btn-primary" onclick="@ShowMouseLocation">Without event argument</button>

<!--Lambda expression with one argument-->
<button class="btn btn-primary ml-5" onclick="@(e => ShowMouseLocation(e))">With event argument</button>

@functions {
    long clientX = 0;
    long clientY = 0;

    void ShowMouseLocation(UIMouseEventArgs eventArgs)
    {
        clientX = eventArgs.ClientX;
        clientY = eventArgs.ClientY;
    }
}

Output

Parent-child event callback

Sometimes you’ll want to execute something in a parent component when an event is triggered in its child component. To do that, we need to pass a callback function as a component parameter and call it from the child component when the desired event is triggered. Let’s look at an example to get a better understanding.

Example

In the parent component, you will see that two methods, IncrementCount and ResetCount, are being passed as component parameters to a child component.

Parent Component
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@page "/parent-child"

<h1>Parent Component</h1>

<p>Child event triggered: @childEventTriggerCount time@(childEventTriggerCount != 1 ? "s" : "")</p>

<ChildComponent Increment="@IncrementCount" Reset="@ResetCount"></ChildComponent>

@functions{
    int childEventTriggerCount = 0;

    void IncrementCount()
    {
        childEventTriggerCount++;
        this.StateHasChanged();
    }

    void ResetCount()
    {
        childEventTriggerCount = 0;
        this.StateHasChanged();
    }
}

In the child component we are calling the two parent methods provided via component parameters: Increment and Reset when their respective onclick events are triggered.

Child Component
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<h2>Child Component</h2>

<button class="btn btn-primary" onclick="@CallParentIncrementMethod">Increment</button>

<button class="btn btn-secondary" onclick="@CallParentResetMethod">Reset</button>

@functions {
    [Parameter]
    Action Increment { get; set; }

    [Parameter]
    Action Reset { get; set; }

    void CallParentIncrementMethod()
    {
        Increment?.Invoke();
    }

    void CallParentResetMethod()
    {
        Reset?.Invoke();
    }
}

Output

Manually Trigger UI Refresh

There are some scenarios, like in the parent-child example above, where you’ll need to trigger a manual UI refresh for your components since Blazor only re-renders a component automatically if an explicit user action is made (for example, a button is clicked, a bound input field’s value changes). But fear not because the BlazorComponent.StateHasChanged method will let you trigger a manual UI refresh when Blazor doesn’t do it automatically.

NOTE

Blazor only re-renders the component that triggers a bound event. Therefore, a parent-child event callback is one scenario where a manual UI refresh is required.

Take Away

Blazor provides one-way and two-way data binding similar to other modern web frameworks, such as Angular and Vue. You can also do event-driven programming by taking advantage of Event Bindings along with Parent-child callbacks. Blazor is still a work in progress with noticeable limitations but future versions are sure to bring exciting new features!

You can get all the examples shown on this post from our GitHub page: nativoplus/BlazorWorkshop