Book Image

Building Blazor WebAssembly Applications with gRPC

By : Václav Pekárek
5 (1)
Book Image

Building Blazor WebAssembly Applications with gRPC

5 (1)
By: Václav Pekárek

Overview of this book

Building Blazor WebAssembly Applications with gRPC will take you to the next level in your web development career. After working through all the essentials of gRPC, Blazor, and source generators, you will be far from a beginner C# developer and would qualify as a developer with intermediate knowledge of the Blazor ecosystem. After a quick primer on the basics of Blazor technology, REST, gRPC, and source generators, you’ll dive straight into building Blazor WASM applications. You’ll learn about everything from two-way bindings and Razor syntax to project setup. The practical emphasis continues throughout the book as you steam through creating data repositories, working with REST, and building and registering gRPC services. The chapters also cover how to manage source generators, C# and debugging best practices, and more. There is no shorter path than this book to solidify your gRPC-enabled web development knowledge. By the end of this book, your knowledge of building Blazor applications with one of the most modern and powerful frameworks around will equip you with a highly sought-after skill set that you can leverage in the best way possible.
Table of Contents (10 chapters)

Understanding page routing in Blazor

Blazor WebAssembly is a SPA. This means that routing is not done on the server but the client. While clicking on the link updates the address bar of the browser, the page itself is not refreshed. Blazor finds a component with a matching route in the @page directive and renders this component (and all child components) as a page.

The Router component takes care of resolving the correct component to render. This component is typically used in the application root component – that is, App.razor. The component is created from two RenderFragments, as follows:

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData"
          DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData"
          Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this
              address.</p>
        </LayoutView>
    </NotFound>
</Router>

In the preceding code, you can see the Router component with the Found and NotFound sections. The Router component looks to the specified AppAssembly and discovers all routable components (components with the @page directive). When navigating from one page to another, the Router component renders the Found section, if there is any routable component with a matched route. Otherwise, the NotFound section is rendered.

Rendering of the component is handled in the RouteView component. The RouteData parameter of the component contains information about the matched component. RouteView uses a layout defined on the component’s @layout directive, or DefaultLayout if the component layout is not specified.

The FocusOnNavigate component is not required. This component sets focus on the element specified by the CSS selector in the Selector parameter. It is useful when building websites compatible with screen readers, or when you want to focus on some user input fields after changing the page.

Navigating between pages

To navigate between pages, the standard HTML anchor is used:

<a href="/contacts">Contacts</a>

As shown in the preceding code, there is no difference between page navigation in standard HTML and Blazor. That is one of Blazor’s advantages over other JavaScript SPA frameworks, which use the custom component to provide SPA navigation. Blazor intercepts any navigation on the same site and tries to find the corresponding components to render.

If you need to manipulate the address from the C# code, you must inject NavigationManager into your code, as follows:

@page "/offers"
@inject NavigationManager NavManager
…
<button @onclick="GoToContacts">Contacts</button>
@code {
  void GoToContacts()
  {
    NavManager.NavigateTo("contacts");
  }
}

The preceding code will change the page to /contacts after the button is clicked.

Page directive

The @page directive can be specified multiple times in the component. It can also contain dynamic parts of the URL:

PageDirective.razor

@page "/author"
@page "/author/{Name}"
<h1>Author @authorName </h1>
@code {
  private string authorName;
[Parameter]
  public string Name { get; set; }
  
  protected override void OnInitialized()
  {
    authorName = Name ?? "Not set";
  }
}

In the preceding code, the first @page directive specifies the URL without any parameters. The second @page directive specifies a parameter with the name. The following URLs are valid for the PageDirective component:

  • /author
  • /author/John
  • /author/123

In the third example, /author/123, you can see that we can pass numbers to the text parameter. It is fine here because any value can be treated like a string. But what if we expect a different type?

Route constraints

Route constraints are used when you need to enforce a specific data type of the route parameter. The route constraints are defined the same way as in C# API endpoints – that is, by adding a semicolon after the parameter name and then specifying the data type:

@page "/author/{Id:int}"

The preceding code will define the URL for the author with Id as an integer.

Not all constraints are supported at the time of writing. The following table shows the supported types:

Constraint type

Example of constraint

Example of valid values

bool

@page "/user/{enabled:bool}

True, true, FALSE

datetime

@page "/user/{createdAt:datetime}

2022-04-23, 2022-01-01 6:18am

decimal

@page "/basket/{price:decimal}

59.99, -999.99

double

@page "/package/{weight:double}

1.15, -39.12

float

@page "/package/{weight:float}

1.234, -1.234

guid

@page "/user/{id:guid}

905E47D7-DA48-4301-8137-B25541438240, {FEB6A90F-3D25-46D1-AD62-64BBD499A0D6}

int

@page "/user/{id:int}

123456, -123456

long

@page "/timer/{ticks:long}

123456789, -123456789

Table 2.2 – Blazor router supported constraints

Component route parameters can also be marked as optional:

@page "/user/{id:int}/{valid:bool?}"

The preceding code specifies the URL to /user with the required Id parameter and optional Valid bool parameter. The optional ? symbol can also be used without type constraints.

Catch-all parameters

Sometimes, you may need to create components for multiple routes. In that case, a catch-all parameter can be used:

Posts/AllPosts.razor

@page "/posts/{*pageRoute}"
@code {
  [Parameter]
  public string? PageRoute { get; set; }
}

The preceding code shows a catch-all parameter named PageRoute. The catch-all route parameter must be of the string type and placed at the end of the route; it is not case-sensitive.

The PageRoute parameter will contain all the matched values from the URL:

  • For the /posts/animals/africa/forest URL, the PageRoute value is animals/africa/forest
  • For the /posts/animals URL, the PageRoute value is animals
  • For the /posts/animals/europa%2Fwater URL, the PageRoute value is animals/europa/water

With that, we know how to create components and how to navigate between them. In the next section, we will look at the project we will be creating in this book. And yes! We will use everything we’ve learned in this chapter up until this point.