© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
T. LitvinaviciusExploring Blazorhttps://doi.org/10.1007/978-1-4842-8768-2_4

4. Specifics of Different Types of Blazor

Taurius Litvinavicius1  
(1)
Kaunas, Lithuania
 

Different types of Blazor, also known as hosting models , are in general similar, but there are a few differences that must be remembered to avoid malfunctions in your projects. In some cases, certain things simply will not work on one of the types, or the behavior of a feature might change depending on the Blazor type.

In this chapter, you’ll learn the following:
  • Overview of default Visual Studio templates

  • Handling API access in Blazor

Default Template Overview

You will find some interesting differences between the client-side (WebAssembly) and server-side templates. You will also learn how to customize things when needed.

Blazor Server-Side Template

For the Blazor server-side version, you have two template options at the moment. One is called “Blazor server app ,” which includes some example code that you will have to remove once you start your project; the other is “Blazor server app empty ,” which is the one you should choose for this chapter (see Figure 4-1). The Blazor server version essentially runs all its logic on your servers and sends rendered pages to be displayed in a browser. The input and output interactions are done via an active WebSocket .

A screeshot lists a menu of connected services, dependencies, properties, wwwroot, pages, app settings, main layout, and program.

Figure 4-1

File contents of the “Blazor server app empty ” template

In the Blazor server version, you get several default files (shown in Figure 4-1), which, for the most part, should not be modified. Listing 4-1 shows the default contents of the Program.cs file.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
Listing 4-1

Program.cs Default Contents

In Program.cs , the two Blazor-related services shown in Listing 4-2 must be added; if these statements are removed, it will not work.
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
Listing 4-2

Program.cs Default Contents

After that, the Blazor navigation must be mapped; otherwise, your application will not work. However, the fallback page can be modified if you want to use another file name for _Host.cshtml. But notice that the fallback page is a Razor (.cshtml) file, not a Blazor file (.razor). Navigation in general is handled by default in App.razor, which should not be modified except for the “not found” display.

MainLayout.razor (Listing 4-3) contains a declaration of where all the pages will be rendered. The @body can be wrapped in a div or other container instead.
@inherits LayoutComponentBase
<main> @Body </main>
Listing 4-3

Default Contents of MainLayout.razor

Finally, the _Host.cshtml file is where your Blazor project is rendered. This is where you can declare your JavaScript files and your CSS styles.

Blazor Client-Side (WebAssembly) Template

The two main differences between the client- and server-side hosting models are the contents of Program.cs and the lack of a _Host.cshtml file in the client-side Blazor version (Figure 4-2).

A screenshot lists a menu of connected services, properties, and pages with index dot razor, imports, app, and main layout.

Figure 4-2

File contents of the “Blazor WebAssembly app empty ” template

Now, let’s take a look at the default code for Program.cs (Listing 4-4).
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
Listing 4-4

Default Code for Program.cs in the “Blazor webassembly app Empty” Template

The Program.cs file first declares into what HTML tag everything will be rendered and sets up the head. You may notice that this is basically the equivalent of what the _Host.cshtml file handles in the Blazor server. None of this should be modified, as it may cause issues or cause the program to stop working. What you can modify is your base address if you are using HttpClient injections to connect to your API.

To declare your JavaScript and CSS files in the client-side Blazor version, you will need to go to index.html (found in the wwwroot folder) and do it there.

Injection

Dependency injection is mainly important for the server-side Blazor projects. When injected, methods from that class can be accessed in any page or component. To understand this better, Figure 4-3 shows an example of a very basic Blazor server application (Listing 4-3).

A screenshot lists a menu of connected services, dependencies, properties, wwwroot, pages, and services. Whereas program dot cs is selected.

Figure 4-3

File contents for the example project

In Listing 4-5, we have one service class (Calculators) that will be injected in Index.razor.
public class Calculators
    {
        public async Task<double> Calculate(double a, double b)
        {
            return a + b;
        }
    }
Listing 4-5

Index.razor

The class itself contains only one method, which adds two double values.

To inject a class, you need to use the @inject directive (Listing 4-6).
@page "/"
@inject Services.Calculators calculators
<p><button @onclick="(async() => { result = await calculators.Calculate(5,5); })">Calculate</button></p>
<p>@result</p>
@code {
    double result;
}
Listing 4-6

. Use of Inject in a Blazor Page

However, if you ran this right now and executed the method, it would throw a major exception .
builder.Services.AddSingleton< Services.Calculators>();

To make this work, you need to declare this service in Program.cs.

Static Values

Static values must be used with caution in server-side projects. Although they can be beneficial to hold global variable data, it is important to remember that these values will be used between sessions. To demonstrate this problem, Listing 4-7 is a basic example.
public class StaticValues
    {
        public static string StaticValue;
    }
Server side Blazor project contains one class with one static string called StaticValue.
<p><input @bind-value="StaticValues.StaticValue" @bind-value:event="oninput" /></p>
<p>@StaticValues.StaticValue</p>
Listing 4-7

StaticValues.cs Class in Blazor Server Project

In Index.razor (Listing 4-7), we have an input that binds straight to the static variable, and the value is displayed (Figure 4-4).

The text reads test.

Figure 4-4

Result of the example page

If the word test is inserted, the result in the browser is as shown in Figure 4-4. However, if you open another browser and paste the URL for the running application, you will see the same thing without having to insert anything into the input.

Calling APIs

The client-side Blazor project will likely require accessing one or more APIs at some point as the logic of it runs directly on the browser. For this you can use HttpClient in a more traditional manner, or if you use JSON, you can access that directly. Listing 4-8 shows how to do it.
public class ExampleController : Controller
    {
        [Route("testget")]
        public string TestGET()
        {
            return "test result";
        }
        [Route("testpost")]
        public string TestPOST([FromBody]DataToSend data)
        {
            return data.val1 + data.val2;
        }
        public  class DataToSend
        {
            public string val1 { get; set; }
            public bool val2 { get; set; }
        }
    }
Listing 4-8

API Controller Example

For the example controller , we have two basic routes: one for the GET and one for the POST Listing 4-9. You may also notice the data model class; an exact match will have to be provided in the Blazor project as well. Alternatively, if you have more of these model classes, you may use a class library to store them.
@page "/"
@inject HttpClient http
<p><button @onclick="@RequestData" >Request data</button></p>
<p>Result: @result</p>
<p><button @onclick="@SendData" >Send data</button></p>
@code {
    string result;
    DataToSend datatosend = new DataToSend();
    async Task RequestData()
    {
        result = await http.GetFromJsonAsync<string>("testget");
    }
    async Task SendData()
    {
        datatosend.val2 = true;
        result = await (await http.PostAsJsonAsync<DataToSend>("/testget",datatosend)).Content.ReadFromJsonAsync<string>();
    }
    class DataToSend
    {
        public string val1 { get; set; }
        public bool val2 { get; set; }
    }
}
Listing 4-9

HttpClient in the Blazor Page

To access the JSON-based API , you use either one of two methods: PostAsJsonAsync or GetFromJsonAsync. For PostAsJsonAsync, you will need to supply the class object that you are sending, and the API must have a class in the same structure to be able to receive the data. The response of that is an HTTP response, which contains content and a few other things. To read JSON content, you will need to use ReadFromJsonAsync, which gives you the same result as FetFromJsonAsync .

Adding the API Controller

The server-side Blazor project runs in a similar way to the .NET web API , which means that you can integrate this capability into your Blazor project.

This will require adding a Controllers folder (and controller classes) and a few things in Program.cs. A good quick way to do it is to simply create an empty API project and copy things that apply to API handling from there. As you can see in Figure 4-5, a Controllers folder has been added and, inside it, a new controller file Listing 4-10.

A screenshot lists a menu of serversideapi with controllers. Some lists of example controllers, pages, shared app settings, and programs.

Figure 4-5

Example project files

app.MapControllers();
Listing 4-10

Method Execution from Program.cs

For this to work , you need to add MapControllers in the Program.cs file.
public class ExampleController : Controller
    {
        [Route("/testroute")]
        public async Task<string> TestRoute()
        {
            return "test";
        }
    }

The controller itself will look and work the same as it would in a regular .NET API project .

Blazor Hosted

When creating a new Blazor project in Visual Studio, you get a few options, and one of them is Blazor hosted . This type simply adds an API project to a sort of shared assembly with your Blazor client project, as shown in Figure 4-6.

A screenshot lists a menu of basicfofmhosted dot client, server, and shared.

Figure 4-6

Projects contained in a Blazor hosted assembly

The Client one is your Blazor client project, the Server one is your API project to which you can add controllers, and finally Shared is a class library that can host shared logic and/or data models. You will later see a basic example done with this and other types of Blazor.

Basic Form Example for Two Types of Blazor

This will be a basic example of a user registration form (Figure 4-7) in the three Blazor types: server, client, and with hosted arrangements.

A screenshot of a form page includes the register options with full name, email, password, and a submit button.

Figure 4-7

Basic form example view

You will see the differences between the types and read explanations on why one is better or worse than the other.

We’ll now look at the Blazor server version with a simple one-project arrangement (Figure 4-8).

A screenshot lists a menu of basicformserver, pages, and services. Some lists are user data, imports, apps, main layouts, and programs.

Figure 4-8

Blazor server project files

The form itself will go into the default Index.razor file , and the logic will go in UserData.cs (Listing 4-11).
public class UserData
    {
        public async Task<bool> InsertNewUser(User newuser)
        {
            //insert into DB
            return true;
        }
        public class User
        {
            public string FullName { get; set; }
            public string Email { get; set; }
            public string Password { get; set; }
        }
    }
Listing 4-11

UserData class contents

The service class simply contains the method, which when executed would insert the data into the database. There is also a data model class for the form data.

The interface part (Listing 4-12) uses the data model and binds the inputs. Then on the click of the button, it executes the method to insert data into the database. The UI itself will be the same as in the other Blazor types.
@page "/"
@inject Services.UserData userdata
<p>Register</p>
<p>Full name</p>
<p><input @bind="NewUser.FullName" /></p>
<p>Email</p>
<p><input @bind="NewUser.Email" /></p>
<p>Password</p>
<p><input @bind="NewUser.Password" /></p>
<p><button @onclick="@Submit" >Submit</button></p>
@code {
    Services.UserData.User NewUser = new Services.UserData.User();
    async Task Submit(){
     bool result =  await  userdata.InsertNewUser(NewUser);
    }
}
Listing 4-12

UserData.cs

For the client-side part, you will also need an API project, which contains one controller (Listing 4-13) with a class model for data.
public class UsersController : Controller
    {
        [Route("adduser")]
        public async Task<bool> AddUser([FromBody]User user)
        {
            // insert into DB
        }
        public class User
        {
            public string FullName { get; set; }
            public string Email { get; set; }
            public string Password { get; set; }
        }
    }
Listing 4-13

UsersController.cs

@page "/"
@inject HttpClient http
<p>Register</p>
<p>Full name</p>
<p><input @bind="NewUser.FullName" /></p>
<p>Email</p>
<p><input @bind="NewUser.Email" /></p>
<p>Password</p>
<p><input @bind="NewUser.Password" /></p>
<p><button @onclick="@Submit">Submit</button></p>
@code {
    User NewUser = new  User();
    async Task Submit()
    {
       bool result = await  (await http.PostAsJsonAsync<User>("/adduser",NewUser)).Content.ReadFromJsonAsync<bool>();
    }
    public class User
    {
        public string FullName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
    }
}
Listing 4-14

Index.razor

Instead of injecting and executing a method like you would in the server-side version, here you need to make an API call (Listing 4-14). You will also need to set up the base address for HttpClient in Program.cs.

The Blazor hosted option is basically the same as on the client side. We have a client-side Blazor and .NET API, but in this case we also have a class library project (Figure 4-9).

A screenshot lists a menu of basicformhosted dot client, server, and shared. Some lists are properties, pages, and controllers with connected services.

Figure 4-9

Projects and project files

The difference in the controller is only that the data model comes from a class library (Listing 4-15).
public class UsersController : Controller
    {
        [Route("adduser")]
        public async Task AddUser([FromBody] Shared.User user)
        {
            // insert into DB
        }
    }
Listing 4-15

UsersController.cs

However, you can achieve a similar result without having this hosted arrangement. You can simply have a class library that you use on both projects: client-side Blazor and .NET API.

The Blazor server part (Listing 4-16) is pretty much the same as in client Blazor, except that the data model class is in a library. The difference from the server-side version is that we make an API call instead of executing a method directly.
@page "/"
@inject HttpClient http
<p>Register</p>
<p>Full name</p>
<p><input @bind="NewUser.FullName" /></p>
<p>Email</p>
<p><input @bind="NewUser.Email" /></p>
<p>Password</p>
<p><input @bind="NewUser.Password" /></p>
<p><button @onclick="@Submit">Submit</button></p>
@code {
    Shared.User NewUser = new  Shared.User();
    async Task Submit()
    {
       bool result = await  (await http.PostAsJsonAsync<Shared.User>("/adduser",NewUser)).Content.ReadFromJsonAsync<bool>();
    }
}
Listing 4-16

Index.razor

Multiple Select Example

This example shows how a multiple select can be made using only basic buttons and C# code in Blazor. The system is quite simple; we have four select options, and whichever option is selected is highlighted in blue with the number displayed on top (Figure 4-10).

A screenshot depicts the four options. From top to bottom, it is option 1, option 2, option 3, and option 4.

Figure 4-10

Custom-made multiselect

For the values (Listing 4-17), we have a basic dictionary, which contains the number of the selection, and a Boolean value , which determines if it is selected or not. In addition, we also have a method that simply outputs a string for the background color statement in CSS. For a more complicated styling change, you could use CSS classes instead.
@code  {
    Dictionary<int, bool> Selections = new Dictionary<int, bool>()
        {
            { 1, true},
             { 2, false},
              { 3, false},
               { 4, false}
        };
    string GenerateColorForSelection(bool is_selected)
    {
        if (is_selected)
        {
            return "background-color:blue;";
        }else
        {
            return "background-color:white;";
        }
    }
}
Listing 4-17

Code Part of the Blazor File

In the HTML part (see Listing 4-18), we have four buttons representing four selections. When a button is clicked, the Boolean value for that specific selection changes. Then the method to generate the background color is used to set the color according to the selection status. Then, the foreach loop goes through the dictionary and displays the values that are selected.
<p>Selected:
    @foreach (var item in Selections.Where(opt => opt.Value == true))
    {
        @(item.Key + "; ")
    }
    </p>
<p><button style="@GenerateColorForSelection(Selections[1])" @onclick="@(() => { Selections[1] = Selections[1] == false ? true :  false;  })">Option 1</button></p>
<p><button style="@GenerateColorForSelection(Selections[2])" @onclick="@(() => { Selections[2] = Selections[2] == false ? true :  false;  })">Option 2</button></p>
<p><button style="@GenerateColorForSelection(Selections[3])" @onclick="@(() => { Selections[3] = Selections[3] == false ? true :  false;  })">Option 3</button></p>
<p><button style="@GenerateColorForSelection(Selections[4])" @onclick="@(() => { Selections[4] = Selections[4] == false ? true :  false;  })">Option 4</button></p>
Listing 4-18

UI Part of the Blazor File

You can reuse this, but it must be in a component, and you must use parameters with custom events (see Chapter 3). This will also work on any of the Blazor types.

Summary

In this chapter you saw useful features for different Blazor types and the differences between them. But in the end, the choice of Blazor type will always depend on your specific use case. In the following chapter, you will learn about several useful features that can be applied to all Blazor types: accessing JavaScript , using local storage, and more.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.57.3