Live Chat Icon For mobile
Live Chat Icon

Blazor FAQ - General

Find answers for the most frequently asked questions
Expand All Collapse All

Instead of using the DataAnnotationValidator in Blazor, you can use FluentValidation to implement form validation. FluentValidation is a common validation library for.NET Core that offers a few advantages over the built-in DataAnnotations validation library, such as a larger set of rules, simpler setup, and extensibility.

To use fluent validation in a Blazor application:

  1. Create a Blazor application using the link.

  2. Install the “FluentValidation” package using the NuGet package manager.

  3. Create a new folder named FluentValidation in the main application and add the following *.cs files – Employee, EmployeeValidator, FluentValidationValidator to it as shown below.
    Fluent Validation

  4. Add a model Employee class name in the Employee.cs file.

      namespace {AppName}.FluentValidation
    {
        public class Employee
        {
            public string Name { get; set; }
            public string Organization { get; set; }
        }
    }


  5. To write a model validator, you must create a EmployeeValidator class that is inherited from the AbstractValidator<Employee> and then add all the validation rules for the respective model to the constructor.

  6.    using FluentValidation;
     
    namespace {AppName}.FluentValidation
    {
        public class EmployeeValidator : AbstractValidator<Employee>
        {
            public EmployeeValidator()
            {
                RuleFor(p => p.Name).NotEmpty().WithMessage("You must enter a valid name.");
                RuleFor(p => p.Name).MaximumLength(20).WithMessage("The name cannot be more than 20 characters long.");
                RuleFor(p => p.Organization).NotEmpty().WithMessage("You must enter a valid organization name.");
            }
        }
    }

     

  7. In FluentValidationValidator.cs, create a new validator component named FluentValidationValidator to replace DataAnnonationsValidator, and add the code below to it.
     
    The FluentValidationValidator receives an EditContext as a cascading parameter and hooks into the EditContext’s OnFieldChanged and OnValidationRequested events to know when something is happening in the UI. It can add or remove validation messages from a ValidationMessageStore at any time.

    using System;
    using FluentValidation;
    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Components.Forms;
     
    namespace {AppName}.FluentValidation
    {
        public class FluentValidationValidator<TValidator> : ComponentBase where TValidator : IValidator, new()
        {
            private readonly static char[] separators = new[] { '.', '[' };
            private TValidator validator;
     
            [CascadingParameter]
            private EditContext EditContext { get; set; }
     
            protected override void OnInitialized()
            {
                validator = new TValidator();
                var messages = new ValidationMessageStore(EditContext);
     
                /* Re-validate when any field changes or when the entire form requests validation.*/
                EditContext.OnFieldChanged += (sender, eventArgs)
                    => ValidateModel((EditContext)sender, messages);
     
                EditContext.OnValidationRequested += (sender, eventArgs)
                    => ValidateModel((EditContext)sender, messages);
            }
     
            private void ValidateModel(EditContext editContext, ValidationMessageStore messages)
            {
                var context = new ValidationContext<object>(editContext.Model);
                var validationResult = validator.Validate(context);
                messages.Clear();
                foreach (var error in validationResult.Errors)
                {
                    var fieldIdentifier = ToFieldIdentifier(editContext, error.PropertyName);
                    messages.Add(fieldIdentifier, error.ErrorMessage);
                }
                editContext.NotifyValidationStateChanged();
            }
     
            private static FieldIdentifier ToFieldIdentifier(EditContext editContext, string propertyPath)
            {
                var obj = editContext.Model;
     
                while (true)
                {
                    var nextTokenEnd = propertyPath.IndexOfAny(separators);
                    if (nextTokenEnd < 0)
                    {
                        return new FieldIdentifier(obj, propertyPath);
                    }
     
                    var nextToken = propertyPath.Substring(0, nextTokenEnd);
                    propertyPath = propertyPath.Substring(nextTokenEnd + 1);
     
                    object newObj;
                    if (nextToken.EndsWith("]"))
                    {
                        nextToken = nextToken.Substring(0, nextToken.Length - 1);
                        var prop = obj.GetType().GetProperty("Item");
                        var indexerType = prop.GetIndexParameters()[0].ParameterType;
                        var indexerValue = Convert.ChangeType(nextToken, indexerType);
                        newObj = prop.GetValue(obj, new object[] { indexerValue });
                    }
                    else
                    {
                        var prop = obj.GetType().GetProperty(nextToken);
                        if (prop == null)
                        {
                            throw new InvalidOperationException($"Could not find property named {nextToken} in object of type {obj.GetType().FullName}.");
                        }
                        newObj = prop.GetValue(obj);
                    }
     
                    if (newObj == null)
                    {
                        return new FieldIdentifier(obj, nextToken);
                    }
     
                    obj = newObj;
                }
            }
        }
    }

  8. Add the following code to the Index.razor page to perform fluent validation using the FluentValidationValidator component and its model validator (EmployeeValidator) in the EditForm component.

      @page "/"
     
    @using {AppName}.FluentValidation;
     
    <EditForm Model="employee" OnValidSubmit="SubmitForm">
        <FluentValidationValidator TValidator="EmployeeValidator" />
        <ValidationSummary />
     
        <div class="form-group">
            <label for="name">Name:</label>
            <InputText @bind-Value="employee.Name" class="form-control" id="name" />
        </div>
     
        <div class="form-group">
            <label for="age">Organization:</label>
            <InputText @bind-Value="employee.Organization" class="form-control" />
        </div>
     
        <button type="submit" class="btn btn-primary">Submit</button>
     
    </EditForm>
     
    @code {
        Employee employee { get; set; } = new Employee();
     
        public void SubmitForm()
        {
     
        }
    }

    Fluent Validator Output


    View Sample in GitHub

Permalink

The DisplayName property, which is set for built-in form components, help maintain an alternate name. When the component’s input value is incorrect due to form validation, this alternate name is displayed in the error message.

The DisplayName property is supported by the following built-in components.

  • InputCheckbox
  • InputDate
  • InputNumber
  • InputRadioGroup
  • InputSelect
  • InputText
  • InputTextArea

Please refer to the code example below.

[Index.razor]

@page "/"

@using System.ComponentModel.DataAnnotations;

<EditForm Model="@details">
    <DataAnnotationsValidator />

    <div>
        <strong>Name:</strong>
        <InputText @bind-Value="details.Name" />
        <ValidationMessage For="@(() => details.Name)" />
    </div>
    <br />

    <div>
        <strong>Age:</strong>
        <InputNumber @bind-Value="details.Age" DisplayName="Details: Age"></InputNumber>
        <ValidationMessage For="@(() => details.Age)" />
    </div>
    <br />

    <div>
        <strong>Birth Date:</strong>
        <InputDate @bind-Value="details.BirthDate" DisplayName="Details: Birthday" />
        <ValidationMessage For="@(() => details.BirthDate)" />
    </div>
    <br />

    <div>
        <strong>Address:</strong>
        <InputTextArea @bind-Value="details.Address" />
        <ValidationMessage For="@(() => details.Address)" />
    </div>
    <br />

    <div>
        <strong>Classification:</strong>
        <InputSelect @bind-Value="details.Classification">
            <option value="">Select classification...</option>
            <option value="Sedan">Sedan</option>
            <option value="SUV">SUV</option>
            <option value="MUV">MUV</option>
        </InputSelect>
        <ValidationMessage For="@(() => details.Classification)" />
    </div>
    <br />

    <div>
        <strong>Vehicle Name: @details.VehicleName</strong>
        <InputRadioGroup @bind-Value="details.VehicleName">
            @foreach (var option in VehicleNames)
            {
                <br /><InputRadio Value="option" /> @option
            }
        </InputRadioGroup>
        <ValidationMessage For="@(() => details.VehicleName)" />
    </div>
    <br />

    <div>
        <strong>Loan Required:</strong>
        <InputCheckbox @bind-Value="details.IsLoanRequired" />
        <ValidationMessage For="@(() => details.IsLoanRequired)" />
    </div>
    <br />

    <button type="submit">Submit</button>

</EditForm>

@code{

    public Details details = new Details();
    public List<string> VehicleNames = new List<string> { "Ciaz", "Vitara Brezza", "Ertiga" };

    public class Details
    {
        [Required, Display(Name = "Details: Name")]
        public string Name { get; set; }

        [Required]
        public int Age { get; set; }

        [Required]
        public DateTime BirthDate { get; set; }

        [Required, Display(Name = "Details: Address")]
        public string Address { get; set; }

        [Required, Display(Name = "Details: Classification")]
        public string Classification { get; set; }

        [Required, Display(Name = "Details: Vehicle Name")]
        public string VehicleName { get; set; }

        [Required, Display(Name = "Details: Loan Required")]
        public bool IsLoanRequired { get; set; }
    }
}

View Sample in GitHub

Permalink

By default, the System.ComponentModel.Annotations library provides different validation attributes. However, to add a new or custom validation, users can create a custom validation attribute and apply it to their model class. To create an attribute:

  1. Create a Blazor Server Application with latest .NET support.

  2. Install the System.ComponentModel.Annotations NuGet package to perform custom validation in Created project.

  3. Create a new folder named Models in the main application and add the following *.cs files –EmployeeDetails, OrganizationValidationAttribute to it as shown below.

  4. Models

  5. Add a model EmployeeDetails class name in the EmployeeDetails.csfile.
  6.  using System.ComponentModel.DataAnnotations;
    namespace BlazorServerApp.Models
    {
    public class EmployeeDetails
    {
    [Required]
    public string? Name { get; set; }
    [Required]
    public string? Organization { get; set; }
    }
    }
  7. Create a new OrganizationValidationAttribute.cs file and then the OrganizationValidationAttribute class, which is derived from ValidationAttribute. The ValidationAttribute from System.ComponentModel.Annotations acts as the base class for validation.
  8. [OrganizationValidationAttribute.cs]
     using System.ComponentModel.DataAnnotations; 
    namespace BlazorServerApp.Models
    {
    public class OrganizationValidationAttribute : ValidationAttribute
    {
    public string? ValidOrganizationName { get; set; }
    protected override ValidationResult? IsValid ( object? value, ValidationContext? validationContext )
    {
    string fieldValue = value!.ToString()!.ToLower();
    if (fieldValue.Equals(ValidOrganizationName?.ToLower()))
    return null;
    return new ValidationResult(ErrorMessage, new[] { validationContext?.MemberName }.Cast());
    }
    }
    }
    The ValidOrganizationName property can be set from the model EmployeeDetails class. Following that, override the IsValid method, which takes two parameters, value and validationContext. The value parameter contains the value that the user entered in the Organization field. The validationContext parameter is used to explain the context in which validation is performed. If the value is equal to the ValidOrganizationName, no errors will be thrown; otherwise, an error message returns.
  9. Add the following custom validation attribute to the Organization property.
  10.  using System.ComponentModel.DataAnnotations;
    namespace BlazorServerApp.Models
    {
    public class EmployeeDetails
    {
    [Required]
    public string? Name { get; set; }
    //Here OrganizationValidation is a Customvalidation Attribute
    [Required]
    [OrganizationValidation(ErrorMessage = "Invalid customer log-in.", ValidOrganizationName = "Syncfusion")]
    public string? Organization { get; set; }
    }
    }
  11. Moving back to the Blazor Server application, add the following code to the Index.razor page to render a form with validation settings.
  12. [Index.razor]
     @page "/" 
    @using BlazorServerApp.Models
    <EditForm Model="_product" OnValidSubmit="Submit" style="width:600px;">
    <DataAnnotationsValidator />
    <ValidationSummary/>
    <div class="form-group row">
    <label for="name" class="col-md-2 col-form-label">Name:
    <label for="name" class="col-md-2 col-form-label">Name:
    <div class="col-md-10">
    <InputText id="name" class="form-control" @bind-Value="_product.Name" />
    <ValidationMessage For="@(() => _product.Name)" />
    </div>
    </div>
    <div class="form-group row">
    <label for="organization" class="col-md-2 col-form-label">Organization:
    <div class="col-md-10">
    <InputText id="supplier" class="form-control" @bind-Value="_product.Organization" />
    <ValidationMessage For="@(() => _product.Organization)" />
    </div>
    </div>
    <div class="row">
    <div class="col-md-12 text-right">
    <button type="submit" class="btn btn-success">Submit</button>
    </div>
    </div>
    </EditForm>
    @code{
    private EmployeeDetails _product = new EmployeeDetails();
    public void Submit () =>
    Console.WriteLine($"{_product.Name}, {_product.Organization}");
    }
    The following outputs will be seen after submitting the form.
    Form Validation
    Custom Form Validation

 View Sample in GitHub

Permalink

The built-in DataAnnotationsValidator allows you to validate form input using data annotations,  but it only validates top-level properties bound to the form and not child or complex type properties. To validate the nested complex  model, replace the DataAnnotationsValidator with the ObjectGraphDataAnnotationsValidator, which validates the entire object, including child and complex type properties in the form.

  1. Install the following package via Package Manager Console in order to use the ObjectGraphDataAnnotationsValidator.

    PM> Install-Package Microsoft.AspNetCore.Components.DataAnnotations.Validation -Version 3.2.0-rc1.20223.4

  2. Create a new Folder named StudentDetails in the main application and add the class named Student.cs to the StudentDetails folder. In Student.cs file, create two model classes -Student and PersonalDetails. Here, add ValidateComplexType belonging to the ObjectGraphDataAnnotationsValidator above the complex type declaration.

    using System.ComponentModel.DataAnnotations;
    namespace Validating_Complex_Model_in_Blazor.StudentDetails
    {
    public class Student
    {
    public Student ()
    {
    Details = new PersonalDetails();
    }

    [Required]
    public string Name { get; set; }
    [Required]
    public string Department { get; set; }
    [ValidateComplexType]
    public PersonalDetails Details { get; set; }
    }

    public class PersonalDetails
    {
    [Required]
    public int Age { get; set; }

    [Required]
    public string? Address { get; set; }
    }
    }

  3. Create an Index.razor page with the EditForm and Input components shown in the code sample below. Set the Model property of the EditForm component to the Student class instance. Additionally, the ObjectGraphDataAnnotationsValidator and ValidationSummary tags are used for validation and the display of validation summary, respectively.

     @page "/"
    @using Validating_Complex_Model_in_Blazor.StudentDetails
    <EditForm Model="@_student" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
        <ObjectGraphDataAnnotationsValidator />
        <ValidationSummary />
        <div class="form-group">
            <label for="name">Name: </label>
            <InputText Id="name" Class="form-control" @bind-Value="@_student.Name"></InputText>
            <ValidationMessage For="@(() => _student.Name)" />
        </div>
        <div class="form-group">
            <label for="department">Department: </label>
            <InputText Id="department" Class="form-control" @bind-Value="@_student.Department"></InputText>
            <ValidationMessage For="@(() => _student.Department)" />
        </div>
        <div class="form-group">
            <label for="address">Address: </label>
            <InputTextArea Id="address" Class="form-control" @bind-Value="@_student.Details.Address"></InputTextArea>
            <ValidationMessage For="@(() => _student.Details.Address)" />
        </div>
        <button type="submit">Submit</button>
    </EditForm>
     
    @code
    {
        public string StatusMessage;
        public Student _student = new Student();
     
        public void HandleValidSubmit()
        {
            StatusMessage = "It's a valid submission.";
        }
     
        public void HandleInvalidSubmit()
        {
            StatusMessage = "It's an invalid submission. Please see the error message(s) listed below.";
        }
    }

      Nested Form Output

View Sample in GitHub

Permalink

The EditContext of the form stores information about the editing process, such as which fields have been changed and which validation messages are currently displayed. To manually pass the EditContext to the EditForm, follow the steps below.

  1. Create a new Customer.cs file and model Customer class.

    public class Customer
    {
        [Required]
        public string Name { get; set; }
     
        [Required]
        [StringLength(4, ErrorMessage = "The organization code must be less than 5 characters in length.")]
        public string OrganizationCode { get; set; }
    }

  2. Insert an EditForm component into the Index.razor page. Then, make instances of the Customer class and the EditContext, and assign the Customer class instance to the EditContext instance. Finally, bind the EditContext instance to the EditContext property in the EditForm component.

      @page "/"
     
    <EditForm EditContext="@editContext">
        <DataAnnotationsValidator />
        <ValidationSummary />
     
    </EditForm>
     
    @code {
        private Customer _customer = new Customer();
        private EditContext editContext;
     
        protected override void OnInitialized()
        {
            editContext = new(_customer);
        }
    }

  3. As the EditContext stores all the form information, it can be used to validate the form components. Add the Razor code as given below with input  texts and a button. When a form is submitted, the Validate() method is invoked to perform validation.

      @page "/"
     
    <EditForm EditContext="@editContext">
        <DataAnnotationsValidator />
        <ValidationSummary />
        <div class="form-group">
            <label for="name">Name:</>
            <InputText id="name" @bind-Value="_customer.Name" />
            <ValidationMessage For="="@(() => _customer.Name)" />
        </div>
            <div class="form-group">
            <label for="organizationCode">Organization Code:</>
            <InputText id="organizationCode" class="form-control" @bind-Value="_customer.OrganizationCode"/>
            <ValidationMessage For="="@(() => _customer.OrganizationCode)" />
            </div>
        <div style="margin-top: 10px;">
            <button type="submit" class="btn btn-primary" @onclick="Submit">Submit</button>
            </div>
    </EditForm>
    EditContext Output



View Sample in GitHub

Permalink

To add custom CSS class names to input components despite bootstrap and other CSS framework styling, follow the steps below.

  1. In the wwroot/css/site.css stylesheet, include the following styles.

     .validField {
        outline: 2px dotted green;
    }
     
    .invalidField {
        outline: 2px dotted red;
    }

  2. Create a new Customer.cs and model Customer class.

     using System.ComponentModel.DataAnnotations;
     
    namespace CustomCSS_Validation_InputComponent
    {
        public class Customer
        {
            [Required]
            public string Name { get; set; }
     
            [Required]
            [StringLength(4, ErrorMessage = "The organization code must be less than 5 characters in length.")]
            public string OrganizationCode { get; set; }
        }
    }

  3. Create a new CustomFieldCssClassProvider.cs and then the CustomFieldCssClassProvider class.Inherit the CustomFieldCssClassProvider class from FieldCssClassProvider and override the GetFieldCssClass method, which helps to check for validation messages with the EditContext and returns the validField” or “invalidField” class name to the appropriate field.

       using Microsoft.AspNetCore.Components.Forms;
    using System.Linq;
     
    namespace FormBootstrapCSS.Data
    {
        public class CustomFieldCssClassProvider : FieldCssClassProvider
        {
            public override string GetFieldCssClass(EditContext editContext,
            in FieldIdentifier fieldIdentifier)
            {
                var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
     
                return isValid ? "validField" : "invalidField";
            }
        }
    }

     

  4. Add the form design code as well as the code for input components validation to the Index.razor page. Here, the SetFieldCssClassProvider method in the EditContext class is used to set the custom CSS name to the appropriate field based on the validation result returned by the CustomFieldCssClassProvider class, whose instance is passed into the method.

    @page "/"
     
    <EditForm EditContext="@editContext" OnValidSubmit="@Submit">
        <DataAnnotationsValidator />
        <ValidationSummary />
        <div class="mt-2 col-md-12">
            Name: <InputText id="name" @bind-Value="_customer.Name" />
        </div>
        <div class="mt-2 col-md-12">
            Organization Code: <InputText id="organizationCode" @bind-Value="_customer.OrganizationCode" />
        </div>
        <div class="mt-2 col-md-12">
            <button type="submit">Submit</button>
        </div>
    </EditForm>
     
    @code {
        private Customer _customer = new Customer();
        private EditContext editContext;
     
        protected override void OnInitialized()
        {
            editContext = new(_customer);
            editContext.SetFieldCssClassProvider(new CustomFieldCssClassProvider());
        }
     
        private void Submit()
        {
         …….. . .
        }
    }


    Bootstrap css output


    View Sample in GitHub
Permalink

To use .npm packages in a Blazor WebAssembly application, follow these steps:

  1. Create a Blazor WebAssembly application in Visual Studio 2019 using this link.

  2. Right-click on the application and create a new folder named npm_packages.
    NPM Demo

  3. Open the npm_packages folder directory via command prompt and run the following command to initialize NPM in the application.

    npm init -y


    Once the command is executed, it will create a new package.json file in the npm_packages directory.

  4. Continue in the command prompt by executing the following command to install a JavaScript bundler named webpack and its CLI as development dependencies.

    npm install webpack webpack-cli –save-dev

  5. Finally, install the required .npm package. In this demo, we installed Syncfusion Maps by running the following command.

    npm install @syncfusion/ej2-maps

  6. Within the npm_packages folder, create a new folder called src, and then a JavaScript file called index.js.
    npm packages

  7. Include the build script for the package.json file, as shown in the following, which uses webpack to bundle the JavaScript file. This script tells webpack to use the index.js file in the src folder as the source file. The output directory of the bundled file is then set to be created under the js folder in the wwwroot folder directory as index.bundle.js.

      {
      "name": "npm_packages",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "build": "webpack ./src/index.js --output-path ../wwwroot/js --output-filename index.bundle.js --mode=development"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^5.58.1",
        "webpack-cli": "^4.9.0"
      },
      "dependencies": {
        "@syncfusion/ej2-maps": "^19.3.44"
      }
    }

  8. Navigate to the Index.razor page and insert a div tag with the id set as element to display the Syncfusion Maps component.
    [Index.razor]  

    @page "/"
     
    <h1>NPM Packages Demo</h1>
     
    <div id="element"></div>
     

  9. In the index.js file, import and declare the Syncfusion Maps component. Finally, the component is appended to the div tag with the id set as element in the Index.razor page.
    [index.js]

     import { Maps } from '@syncfusion/ej2-maps';
     
    const map = new Maps({
        layers: [
            {
                layerType: "OSM"
            }
        ]
    });
    map.appendTo('#element');

     

  10. Modify the index.html file with the following code to help Blazor inject a script after it starts.
    Note: This step is primarily for including the Syncfusion Maps component; if you’re using something else, you can skip it.
     
    [index.html]  

      <!DOCTYPE html>
    <html>
     
    <head>
        <title>NPM_Demo</title>
        <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
        <link href="css/app.css" rel="stylesheet" />
    </head>
     
    <body>
        ………………..
        <script src="_framework/blazor.webassembly.js" autostart="false"></script>
        <script>
            Blazor.start().then(function () {
                var customScript = document.createElement('script');
                customScript.setAttribute('src', 'js/index.bundle.js');
                document.head.appendChild(customScript);
            });
        </script>
    </body>
     
    </html>
     

  11. Add a prebuild step to the *.csproj file that will run the .npm commands whenever you compile or build the application.
     [.csproj]

     <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
     
                 ………………………..
                <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
                            <Exec Command="npm install" WorkingDirectory="npm_packages" />
                            <Exec Command="npm run build" WorkingDirectory=" npm_packages " />
                </Target>
    </Project>

    Run the sample to see the demonstration.
    NPM Packages demo output

Permalink

CORS stands for cross-origin resource sharing, and it is an HTTP header-based mechanism that allows the server to tell the browser whether to accept a request from a domain, port, or scheme other than its own. It is based on a process in which the browser sends a “preflight” request to the server, and the server responds with response headers indicating whether the request can proceed or not. The browser will either succeed or fail the request based on what these headers say. The browser sends headers that indicate the HTTP method and headers that will be used in the actual request in the preflight request.

To enable CORS in your Blazor Server application, follow these steps:

  1. Create a new Blazor Server application with latest .Net support.

  2. After creating the application, open the Program.cs file. In this file, you will find the code where the AddCors method is called on the builder.Services object to register the CORS service. Within the AddCors method, a named CORS policy called “NewPolicy” is defined using the options.AddPolicy method. If necessary, you can further configure the CORS policy by providing additional options through the builder parameter in the AddPolicy method.
    [Program.cs]

    ...
    builder.Services.AddCors(options =>
    {
    options.AddPolicy("NewPolicy", builder =>
    builder.AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader());
    });
    ...
    app.UseRouting();
    app.UseCors("NewPolicy");
    app.UseAuthorization();

Note: Call the UseCors method between the UseRouting and UseAuthorization methods.

Permalink

Bundling is the process of combining multiple files into a single file. Minifying is the process of removing unnecessary data such as comments and extra spaces, as well as converting a large variable name into a smaller name without affecting its functionalities.

To bundle and minify the CSS and JS files in a Blazor application, follow these steps:

  1. Install the BuildBundlerMinifier NuGet package and reference it in the .csproj file, as shown.

    [.csproj]

        <Project Sdk="Microsoft.NET.Sdk.Web">
      ……………
     
      <ItemGroup>
        <PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
        ……………
      </ItemGroup>
     
    </Project>

  2. Create a new .json file named bundleconfig.json and add the following code to bundle and minify the CSS and JS files.

    [bundleconfig.json]

      [
      {
        "outputFileName": "wwwroot/css/bundle.min.css",
        "inputFiles": [
          "wwwroot/css/app.css",
          "wwwroot/css/site.css"
        ]
      },
      {
        "outputFileName": "wwwroot/scripts/bundle.min.js",
        "inputFiles": [
          "wwwroot/scripts/core.js",
          "wwwroot/scripts/index.js"
        ],
        "minify": {
          "enabled": true,
          "renameLocals": true
        },
        "sourceMap": false
      }
    ]

  3. After you build your application, the bundled and minified files are generated as shown in the following image. You can reference these files in your application based on the requirement.

    Output


    View Sample in GitHub

Permalink

The AddDefaultPolicy method allows you to add a new policy to the CORS configuration and make it the application’s default. In the Program.cs file, you can call the AddCors method to add the cross-origin resource sharing services to the service collection. To add a default policy to the configuration, you can call the AddDefaultPolicy within the AddCors method. Since you are using the default policy, there is no need to include the policy name in the UseCors method, as shown. 

[Program.cs] 

…. 

builder.Services.AddCors(options => 
{ 
    options.AddDefaultPolicy("NewPolicy", builder => 
     builder.AllowAnyOrigin() 
                  .AllowAnyMethod() 
                  .AllowAnyHeader()); 
}); 
…. 
app.UseRouting(); 
app.UseCors(); 
app.UseAuthorization(); 

The AddPolicy method allows you to add a custom policy to the CORS configuration and give it a name for identification. In the Program.cs file, to add a user-defined (custom) policy to the configuration, you can call AddPolicy within the AddCors method.

[Program.cs]

builder.Services.AddCors(options => 
{ 
    options.AddPolicy("NewPolicy", builder => 
     builder.WithOrigins("https://localhost:8080") 
                  .AllowAnyHeader() 
                  .WithMethods("GET")); 
}); 

Then, you should call the UseCors method and pass the user-defined (custom) policy name to add the CORS middleware to the application pipeline.

[Progaram.cs]

app.UseRouting(); 
app.UseCors("NewPolicy"); 
app.UseAuthorization(); 

Note: Call the UseCors method between the UseRouting and UseAuthorization methods.

Permalink

SEO is an abbreviation for search-engine optimization, and it refers to the process of optimizing a website for search engines. In simpler words, it refers to the process of improving your website in order to increase its visibility when people use Google, Bing, and other search engines to find what they’re looking for.

The best way to make your Blazor WebAssembly application SEO-friendly is to work on the title, meta description, and H1 tag in the index.html common page or in individual pages based on your requirements and development activities. If you include keywords in the title, meta description, and H1 tag of your Blazor WebAssembly application, the app will appear near the top of search engine results when people search for general information using those keywords.

meta tags

Title: The title must be text-only and appears in the browser’s title bar or in the page’s tab.

meta title

H1 tag: The H1 tag will be displayed as the application’s top-level heading.

h1 tag

Meta description:  The meta description will be displayed as compressed content just below the search-related link.

meta description

Meta keywords: If anyone searches for a keyword mentioned in your Blazor application, it will appear at the top of the search engine results. However, Googlebot no longer considers meta keywords to be SEO-friendly.

meta keywords

Note: Make sure that your title, H1 tag, and meta description are unique. If the description or title is too long, Google will limit the content to a specific range. Follow these guidelines to avoid unwanted content loss:

  • Title: should be between 20 and 70 characters long.
  • Meta description: should be between 100 and 160 characters long.
  • H1: should be between 20 and 70 characters long.
Permalink

SEO is an abbreviation for search-engine optimization, and it refers to the process of optimizing a website for search engines. In simpler words, it refers to the process of improving your website in order to increase its visibility when people use Google, Bing, and other search engines to find what they’re looking for.

The best way to make your Blazor Server application SEO-friendly is to work on the title, meta description, and H1 tag in the _Host.cshtml common page or in individual pages based on your requirements and development activities. If you include keywords in the title, meta description, and H1 tag of your Blazor Server application, the app will appear near the top of search engine results when people search for general information using those keywords.

meta tags

Title: The title must be text-only and appears in the browser’s title bar or in the page’s tab.

meta title

H1 tag: The H1 tag will be displayed as the application’s top-level heading.

h1 tag

Meta description:  The meta description will be displayed as compressed content just below the search-related link.

meta description

Meta keywords: If anyone searches for a keyword mentioned in your Blazor application, the app will appear at the top of the search engine results. However, Googlebot no longer considers meta keywords to be SEO-friendly.

meta keywords

Note: Make sure that your title, H1 tag, and meta description are unique. If the description or title is too long, Google will limit the content to a specific range. Follow these guidelines to avoid unwanted content loss:

  • Title: should be between 20 and 70 characters long.
  • Meta description: should be between 100 and 160 characters long.
  • H1: should be between 20 and 70 characters long.
Permalink

In a Blazor application, you can call JavaScript functions from C# (.NET) methods and vice versa. This is referred to as “JavaScript interoperability” or “JS interop.” The JS interop allows you to integrate JavaScript libraries into your Blazor application, and its primary function is to handle DOM manipulation and browser API calls. 

Please refer to this link to learn more about JS interop in Blazor.

Please refer to this link for calling and implementing JS interop in Blazor.

Permalink

You can change the CSS value in the Blazor application by using the following options.

  • Internal CSS
  • Inline CSS
  • Declare and assign a variable to the respective DOM element.

NOTE: In CSS, the “!important” rule is used to give a property more weightage, that is, it will override all the previous styling rules applied to that element for that specific property.

[Index.razor]


@page "/"

<style>
    .divStyle {
        font-family: 'Courier New';
        color: red !important;
    }
</style>

<div class="divStyle"> This is internal CSS styling. </div>
<div style="font-family: 'Times New Roman'; color: green !important;"> This is inline CSS styling. </div>
<div style="@styleInfo"> This CSS styling is applied by declaring a variable. </div>

@code {
    public string styleInfo = "font-family: 'Calibri'; color: blue !important;";
}
CSSvaluechange

View Sample in GitHub

Permalink

Stored procedures are pieces of reusable code that you can save in a database data dictionary. They help you extract, edit, and remove data from a database without writing the code to do so again and again. It also saves your time, lessens your workload, and increases your productivity.
Follow these steps to use stored procedures in a Blazor application:

  1. Create a new Blazor application and add the Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Design, Microsoft.EntityFrameworkCore.Tools NuGet packages using the NuGet package manager.

  2. Create a class named EmpClass.cs in the Data folder and define the properties that are already in the database table.
    [Empclass.cs]

    public class EmpClass
    {
    [Key]
    public int Empid { get; set; }
    public string? Empname { get; set; }
    public string? Department { get; set; }
    public DateTime joinDate { get; set;}
    }
    [EmpClass table]
    Employee table
    Also Define inserted Employee Details in stored procedure.
    Employee details
    CREATE PROCEDURE [dbo].[InsertEmpdetails]
    @Empname nvarchar(150),
    @Department nvarchar(150),
    @joinDate datetime
    AS
    Insert into EmpClass(Empname,Department,joinDate) values (@Empname,@Department,@Joindate)
    RETURN 0

  3. Add the virtual property for the DbSet from EmpClass to insert records in the database under the Data folder.
    [ApplicationDbContext.cs]

    public class ApplicationDbContext:DbContext 
    {
    public ApplicationDbContext(DbContextOptions options ) : base(options)
    {
    }
    public virtual DbSet InsertRecord { get; set; }
    }

  4. Connect the SQL Server database to the Blazor application by choosing (View –> Server Explorer). Right-click the Data Connections and choose Add connection in Visual Studio. Add your server name and DB name to connect the database to the Blazor application.
    Add server connection
    The database is now connected to the Blazor application.

  5. Add the connection string configurations to the appsetting.json file.
    [appsetting.json]

    {
    "ConnectionStrings": {
    "Myconnection": " "Data Source={{Server_Name}};Initial Catalog={{DataBase_Name}};Integrated Security=True""
    },
    "Logging": {
    ……
    },
    "AllowedHosts": "*"
    }

  6. Create an EmpServices class in the Services folder and define the ExecuteSqlRaw extension method to execute a stored procedure.
    [EmpServices.cs]

    using {Your App Name}.Data;
    using Microsoft.EntityFrameworkCore;
    namespace {Your App Name}.Services
    {
    public class EmpService
    {
    protected readonly ApplicationDbContext? _dbcontext;
    public EmpService ( ApplicationDbContext? _db )
    {
    _dbcontext = _db;
    }
    public EmpClass AddNewRecord ( EmpClass? ec)
    {
    //Here define the ExecuteSqlRaw extension method to execute a stored procedure with InsertEmpdetails
    _dbcontext!.Database.ExecuteSqlRaw("InsertEmpdetails {0},{1},{2}", ec?.Empname!, ec?.Department!, ec!.Joindate);
    _dbcontext.SaveChanges();
    return ec;
    }
    }
    }

  7. Add the DbContext configuration to the Program.cs file.

    builder.Services.AddDbContext(options => options.UseSqlServer("name=ConnectionStrings:Myconnection"));
    builder.Services.AddScoped();

  8. Insert Record Into Database Using Stored Procedure implement in index.razor page

    @page "/"
    @using {Your App Name}.Data
    @using {Your App Name}.Services
    @inherits OwningComponentBase<EmpService>
    <h1>Insert Record Into Database Using Stored Procedure</h1>
    <hr/>
    <EditForm Model="@ec" OnValidSubmit="@Insertdata">
    <table border="1">
    <tr>
    <td>Employee Name</td>
    <td><input type="text" placeholder="Enter Employee Name" @bind="ec.Empname"/></td>
    </tr>
    <tr>
    <td>Department</td>
    <td><input type="text" placeholder="Enter Department" @bind="ec.Department" /></td>
    </tr>
    <tr>
    <td>Joindate</td>
    <td><InputDate @bind-Value="ec.Joindate"></InputDate></td>
    </tr>
    <tr>
    <td>Joindate</td>
    <td><input type="submit" value="Insert"/></td>
    </tr>
    </table>
    </EditForm>
    @code{
    EmpClass ec = new EmpClass();
    void Insertdata ()
    {
    ec.Empid = 0;
    Service.AddNewRecord(ec);
    }
    }

  9. Press Ctrl + F5 to run the application and see the output in the following image.
    final output

View Sample in GitHub

Permalink

The Dispose method is used to avoid memory leaks and allows proper garbage collection. Implement the IDisposable interface in the component and call the Dispose method. If the framework calls the Dispose method, the component is removed from the UI and unmanaged resources can be released.

The following example demonstrates the disposal of Timer in the counter page.

[Counter.Razor]

@page "/counter"
@using System.Timers
@implements IDisposable

<h1>Counter with Timer disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timerObj = new(1000);

    protected override void OnInitialized()
    {
        timerObj.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timerObj.Start();
    }

    private void OnTimerCallback()
    {
        InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        timerObj?.Dispose();
    }
}    

View Sample in GitHub

Permalink

A progressive web application (PWA) is usually a single-page application (SPA) used to create a Blazor WebAssembly application to work offline. It runs in its own app window and independent of network speed.

Follow these steps to create a Blazor WebAssembly PWA application to work offline.

  1. Create a Blazor WebAssembly application with a progressive web application configuration.

  2. Press Ctrl + F5 to run the application. Users have the option of installing the app.

  3. Once the app is installed, the app appears in its own window without an address bar.

  4. To run the application offline:

    1. Publish the app using this documentation – https://learn.microsoft.com/en-us/aspnet/core/blazor/progressive-web-app?view=aspnetcore-7.0&tabs=visual-studio
    2. Deploy the app to a server that supports HTTPS and access the app in a browser at its secure HTTPS address.
    3. Run the deployed application in the browser and open the browser’s dev tools.
    4. Open the network tab in the browser dev tool and set the throttle setting to offline mode.
    5. Refresh the application. It still loads in offline mode, also.
Permalink

To achieve two-way binding with the Select component in Blazor, you can use the @bind directive. Here’s an example of how you can implement two-way binding with the Select component:

Razor file

@page "/" 

<select @bind="selectedOption"> 
    <option value="option1">Option 1</option> 
    <option value="option2">Option 2</option> 
    <option value="option3">Option 3</option> 
</select> 

<p>You selected: @selectedOption</p> 
@code { 
    private string selectedOption = "option1"; 
} 

Refer to this link for more details.

View Sample in GitHub

Permalink

To apply custom CSS style to a Blazor component, create a custom CSS file in your Razor page with the name of razorpage.css (i.e. Index.razor.css) and add your styles.

[Index.razor.css]

h1 {
        color: red;
        font-style: italic;
        text-shadow: 2px 2px 2px gray;
    }

You can also define the custom CSS styles in the <style> section of Razor pages.

[Index.razor]

@page "/"

<h1>Blazor App</h1>

<style>
h1 {
        color: red;
        font-style: italic;
        text-shadow: 2px 2px 2px gray;
    }
</style>
Permalink

To create a custom validation component in Blazor, follow these code steps:

  1. Create a Blazor Server or WebAssembly application and install the System.ComponentModel.Annotations NuGet package using NuGet Package Manager.

  2. Now, create a new custom validation class in the Pages folder and restrict the user to enter only “admin” in the password field.
    [CustomValidatorAttribute.cs]

    using System.ComponentModel.DataAnnotations;
     
    namespace {{Your_App_Name}}.Pages
    {
    public class CustomValidationAttribute : ValidationAttribute
    {
    public string? ValidUserName { get; set; }
    protected override ValidationResult? IsValid ( object? username, ValidationContext validationContext )
    {
    var content = username?.ToString()?.ToLower();
    if (content!.Equals(ValidUserName?.ToLower()))
    {
    return null;
    }
    return new ValidationResult(ErrorMessage, new[] { validationContext.MemberName });
    }
    }
    }
  3. Use the created custom validator attribute in the Razor component. Provide the error message and valid username properties for validation.
    [Index.razor]

    @page "/"
    @using System.ComponentModel.DataAnnotations
     
    <EditForm style="width:470px;" Model="_login" OnValidSubmit="Submit">
        <DataAnnotationsValidator />
        <div class="form-group row">
            <label for="name" class="col-md-2 col-form-label">Name:</label>
            <div class="col-md-10">
                <InputText id="name" class="form-control" @bind-Value="_login.UserName" />
                <ValidationMessage For="@(() => _login.UserName)" />
            </div>
        </div>
     
        <div class="form-group row">
            <label for="supplier" class="col-md-2 col-form-label">Password:</label>
            <div class="col-md-10">
                <InputText id="supplier" class="form-control" @bind-Value="_login.Password" />
                <ValidationMessage For="@(() => _login.Password)" />
            </div>
        </div>
     
        <div class="row">
            <div class="col-md-12 text-right">
                <button type="submit" class="btn btn-success">Submit</button>
            </div>
        </div>
    </EditForm>
     
    @code {
        private Login _login = new Login();
     
        public void Submit()
        {
            Console.WriteLine($"User name is {_login.UserName} and password is {_login.Password}");
        }
     
        public class Login
        {
            [Required]
            [CustomValidation(ErrorMessage = "The entered username is wrong ", ValidUserName = "admin")]
            public string? UserName { get; set; }
            [Required]
            public string? Password { get; set; }
        }
    }

     

  4. Refer to the following output image for the custom validator.

    Refer to this link for more details.


    View Sample in GitHub
Permalink

Using C# conditional preprocessor directives and predefined constants, you can check the Blazor target framework version. For example, if the framework targets .NET 6.0, or latest version the code is compiled only in that specified condition. Follow these steps to check the Blazor target framework version.

[Index.razor]

@page "/" 
<h1>@blazorVersion</h1> 

@code { 
    private string? blazorVersion { get; set; } 
    protected override void OnInitialized () 
    { 
#if NET6_0 
        blazorVersion = "Blazor App version is .NET6.0"; 
#elif NET5_0 
    blazorVersion = "Blazor App version is .NET5.0"; 
#elif NETCOREAPP3_1 
    blazorVersion = "Blazor App version is netcoreapp3.1"; 
#endif 
    } 
} 
Permalink

By calling the StateHasChanged() method in Blazor, you can receive notifications about state changes and trigger component re-rendering to push the changes to the browser using SignalR. An example that illustrates this functionality is the Counter component. In this example, the component automatically pushes incremented count data using the Timer function and updates the UI in the browser by invoking the StateHasChanged() method within the InvokeAsync action. 

[Counter.razor]

@page "/counter"
@using System.Timers

<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;
    private Timer time;

    private void IncrementCount()
    {
        currentCount++;
    }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            time = new Timer();
            //Set the time interval.
            time.Interval = 1000;
            time.Elapsed += OnTimeInterval;
            time.AutoReset = true;
            // Start the timer.
            time.Enabled = true;
        }
        base.OnAfterRender(firstRender);
    }

    Private async void OnTimeInterval(object sender, ElapsedEventArgs e)
    {
        IncrementCount();
        await InvokeAsync(() => StateHasChanged());
    }

    public void Dispose()
    {
        // While navigating to other components, Dispose method will be called and clean up the Timer function.
        time?.Dispose();
    }
}

Refer to this link for more details.

Permalink

Use HttpContext through the IHttpContextAccessor interface to get the user agent details in a Blazor Server application. The following example demonstrates how to use HttpContext to get the user agent and IP address details by default. Extend the AddHttpContextAccessor() method in the Program.cs file. 

[Program.cs]

builder.Services.AddHttpContextAccessor();

[Index.razor]

@page "/"
@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor httpContextAccessor

<p>UserAgent = @UserAgent</p>
<p>IPAddress = @IPAddress</p>

@code {
    public string UserAgent { get; set; }
    public string IPAddress { get; set; }

    protected override void OnInitialized()
    {
        UserAgent = httpContextAccessor.HttpContext.Request.Headers["User-Agent"];
        IPAddress = httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
    }
}

Refer to this link for more details.

Permalink

In a Blazor Server app, the default RenderMode is Server Prerendered. When the component is rendering with the ServerPrerendered render mode, the component is initially rendering statically as part of the page. 

On executing the first time, it is rendered as an MVC component directly when the page is requested and handled by “_Host” which is specified in “_Host.cshtml”. 

<body> 
    <component type="typeof(App)" render-mode="ServerPrerendered" /> 
    ……. 
</body>

Then the resources are loaded with the blazor.server.js script and start rendering a second time. Then the Razor pages are loaded as a Blazor component.
Note: If you are changing the RenderMode to Server, the lifecycle method executes only once.

[Pages/_Host.cshtml] 

<body> 
    <component type="typeof(App)" render-mode="Server" /> 
    ……. 
</body> 

Refer to this link for details. 

Permalink

Use the System.Timers.Timer class to wait until the user has finished typing in the text field. The onkeyup event triggers for each key press and resets the timer until the last timer raises the OnUserFinish event.

[Index.razor]

@page "/"
@using System.Timers;

<input type="text" @bind-value="dataValue" @bind-value:event="oninput" @onkeyup="@OnValueChange" />
<p>@Content</p>

@code {
    private string dataValue { get; set; }
    private string Content { get; set; }
    private Timer timerObj;

    protected override void OnInitialized()
    {
        timerObj = new Timer(1500);
        timerObj.Elapsed += OnUserFinish;
        timerObj.AutoReset = false;
    }

    private void OnValueChange(KeyboardEventArgs e)
    {
        // remove previous one
        timerObj.Stop();
        // new timer
        timerObj.Start();
    }

    private void OnUserFinish(Object source, ElapsedEventArgs e)
    {
        InvokeAsync(() =>
        {
            Content = $"Typed text: {dataValue}";
            StateHasChanged();
        });
    }
}

View Sample in GitHub

Permalink

In the following example, we’ve animated state transitions using CSS in a Blazor app.

When the state is changed, adding and removing the property values for every state transition is animated.

Create a separate reusable component (.razor) in the Pages folder and add the animation.

[Pages/AnimeState.razor]


<div class="container @(IsShown?"spin-in":"fade-out")">
    <span>@(Name??"")</span>
</div>

<style>
    .container{
        display:inline-block;
        width:@width;
        height:@height;
        color:white;
        background-color:blue;
    }

    .fade-out{
       animation: fade @AnimationTime linear forwards;
    }

    .spin-in{
       animation: spin @AnimationTime linear forwards;
    }

    @@keyframes fade {
      0%   {opacity:1;}
      99%  {width:@width; height:@height;}
      100% {width:0px;height:0px; opacity:0;}
    }

    @@keyframes spin {
      0%   {width:0px;height:0px; transform: rotate(0deg);}
      99%  {width:@width; height:@height;}
      100% {width:@width;height:@height;transform: rotate(1440deg);}
    }
</style>

@code {
    string width = "100px";
    string height = "20px";

    [Parameter] public string Name { get; set; }
    [Parameter] public string AnimationTime { get; set; }
    [Parameter] public bool IsShown { get; set; }
}

Add the AnimeState.razor component and define the property value to be animated when the state has been changed.

[Index.razor]

@page "/"

<button class="btn btn-primary" @onclick="OnButtonClick">Switch</button>
<AnimeState Name="Blazor" AnimationTime="2s" IsShown=@isShown />
<span>State transition Animation</span>
@code{
    bool isShown = true;

    private void OnButtonClick() 
    {
        isShown = !isShown;
    }
}

View Sample in GitHub

Permalink

By utilizing browser storage in Blazor, you can effectively save and load data within a component. In the following example, you can see how the counter value is updated with a button click and the count value is stored in the local storage of the browser. When you refresh the page, the saved data will be loaded from the browser’s local storage.

[Index.razor]

@page "/"
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedLocalStorage LocalStorage

@if (isCount)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button class="btn btn-primary" @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

@code {
    private int currentCount;
    private bool isCount;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isCount = true;
            // Get the count value into browser local storage.
            var countValue = await LocalStorage.GetAsync<int>("countValue");
            // Loaded the browser local storage value to currentCount.
            currentCount = countValue.Success ? countValue.Value : 0;
            StateHasChanged();
        }
    }

    private async Task IncrementCount()
    {
        currentCount++;
        // Set the count value in browser local storage.
        await LocalStorage.SetAsync("countValue", currentCount);
    }
}

Refer to this link for more details.

Permalink

IndexedDB is used for the client-side storage of data and files. Follow these steps to use IndexedDB in Blazor WebAssembly:

  1. Create a Blazor WebAssembly app and install the IndexedDB.Blazor NuGet package using the NuGet package manager.

  2. Set up the IIndexedDbFactory configuration service in your Program.cs file and set it as scoped.
    [Program.cs]

    using IndexedDB.Blazor;
    builder.Services.AddScoped();
  3. Now add the properties inside the class to store and process data in the Data folder.
    [IndexDb.cs]

    using IndexedDB.Blazor; 
    using Microsoft.JSInterop;
    using System.ComponentModel.DataAnnotations;
    namespace Blazor_WebAssembly_App.Data
    {
    public class IndexDb : IndexedDb
    {
    public IndexDb ( IJSRuntime jSRuntime, string name, int version ) : base(jSRuntime, name, version) { }
    // These are like tables. Declare as many of them as you want.
    public IndexedSet employee { get; set; }
    }
    public class Employee
    {
    [Key]
    public int Id { get; set; }
    [Required]
    public string? FirstName { get; set; }
    [Required]
    public string? LastName { get; set; }
    }
    }
  4. Add the namespace for the IndexedDB in _Import.razor file.
    [_Import.razor]

    @using IndexedDB.Blazor 
    @using {{Your_App_Name}}.Data
  5. Add the Razor component to add and store the data using IndexedDB in the Index.razor file.
    [_Import.razor]

    @page "/" 
    @inject IIndexedDbFactory DbFactory
    <h1>employee</h1>
    @if (employee != null)
    {
    <table class="table">
    <thead>
    <tr>
    <th>ID</th>
    <th>First name</th>
    <th>Last name</th>
    <th></th>
    </tr>
    </thead>
    <tbody>
    @foreach (var Employee in employee)
    {
    <tr>
    <td>@Employee.Id</td>
    <td>@Employee.FirstName</td>
    <td>@Employee.LastName</td>
    <td>
    <button class="btn btn-danger" @onclick="() => DeleteEmployee(Employee.Id)">Delete</button>
    </td>
    </tr>
    }
    </tbody>
    </table>
    }
    <fieldset>
    <legend>Add new Employee</legend>
    <EditForm Model="@newEmployee" OnValidSubmit="@SaveNewEmployee">
    <InputText @bind-Value="@newEmployee.FirstName" placeholder="First name..." />
    <InputText @bind-Value="@newEmployee.LastName" placeholder="Last name..." />
    <button type="submit">Add</button>
    <p><ValidationSummary /></p>
    <DataAnnotationsValidator />
    </EditForm>
    </fieldset>
    @code {
    List<Employee>? employee;
    protected override async Task OnInitializedAsync ()
    {
    await RefreshEmployeeList();
    }
    Employee newEmployee = new Employee();
    async Task RefreshEmployeeList ()
    {
    using var db = await DbFactory.Create<IndexDb>();
    employee = db.employee.ToList();
    }
    async Task SaveNewEmployee ()
    {
    using var db = await this.DbFactory.Create();
    db.employee.Add(newEmployee);
    await db.SaveChanges();
    // Refresh list and reset the form
    newEmployee = new Employee();
    await RefreshEmployeeList();
    }
    async Task DeleteEmployee ( int id )
    {
    using var db = await this.DbFactory.Create<IndexDb>();
    var employeeToDelete = db.employee.FirstOrDefault(e => e.Id == id);
    if (employeeToDelete != null)
    {
    db.employee.Remove(employeeToDelete);
    await db.SaveChanges();
    await RefreshEmployeeList();
    }
    }
    }

View Sample in GitHub

Permalink

Follow these steps to add a Blazor WebAssembly project to an existing ASP.NET Core application:

  1. Create a ASP.NET Core application and Blazor WebAssembly application separately.

  2. Install the Microsoft.AspNetCore.Components.WebAssembly.Server NuGet package in the ASP.NET Core application and add the Blazor WebAssembly application project reference to the ASP.NET Core application.
    [AspNetCoreApp.csproj]

    <Project Sdk="Microsoft.NET.Sdk.Web">
       <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.0" />
      </ItemGroup>
     
      <ItemGroup>
        <ProjectReference Include="..{Your Blazor App Path}" />
      </ItemGroup>
     
    </Project>

  3. Add the following configurations the Program.cs file in the ASP.NET Core app to include Blazor WebAssembly.
    [Program.cs]

    ....
    if (!app.Environment.IsDevelopment())
    {
    app.UseWebAssemblyDebugging();
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
    }

    app.UseBlazorFrameworkFiles();
    app.MapFallbackToFile("index.html");
    //app.MapRazorPages(); // Remove this line.
    app.Run();
  4. To avoid conflicts during publishing between WebAssembly and ASP.NET Core applications, it is necessary to delete the favicon.ico file. By default, both projects create a favicon.ico file, which can lead to conflicts. To resolve this, remove one of the favicon.ico files from the project.

Refer to this link for more information. 

View Sample in GitHub 

Permalink

Blazor authentication is implemented to determine who a particular user is. Blazor follows the existing ASP.NET Core authentication mechanisms to show a user’s identity.
Follow these steps to implement authentication within Blazor WebAssembly:

  1. Create a Blazor WebAssembly app with individual user account authentication in Visual Studio 2019.

  2. Install the NuGet package named “Microsoft.AspNetCore.Components.WebAssembly.Authentication” using the NuGet package manager.

  3. To support the authenticating service, add the AddOidcAuthentication service configuration to the Program.cs file.
    [Program.cs]

    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    . . .

      builder.Services.AddOidcAuthentication(options =>
          {
            builder.Configuration.Bind("Auth0", options.ProviderOptions);
            options.ProviderOptions.ResponseType = "code";
          });

  4. In wwwroot/appsettings.json file, replace the Authority and Client ID placeholders with the proper values taken from the Auth0 dashboard.
    Note: Use Auth0; it will let you integrate authentication for configuring your application. Refer to this link for how to configure the application.
    [wwwroot/appsetting.json]

    {
      "Auth0": {
        "Authority": "https://<YOUR_AUTH0_DOMAIN>",
        "ClientId": "<YOUR_CLIENT_ID>"
      }
    }

  5. Add the authentication service script reference to the index.html file, which handles the low-level details of the OIDC protocol.
    [index.html]

    <body>
     
        //. . .
     
        <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js">
        </script>
        
    </body>

  6. Add CascadingAuthenticationState and AuthorizeRouteView to display the page matching the specified route only if the user is authorized. Otherwise, it redirects to the Login page.
    [App.razor]

    <CascadingAuthenticationState>
       
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <Authorizing>
                    <p>Loading...</p>
                </Authorizing>
                <NotAuthorized>
                    <p>You're not authorized to reach this page. You need to log in.</p>
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>       
            <p>Sorry, there's nothing at this address.</p>        
        </NotFound>
    </Router>
    </CascadingAuthenticationState>

  7. Create a Razor component to allow the user to log in to be authenticated in the Shared folder.
    [Shared/LoginControl.razor]

    @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
     
    @inject NavigationManager UriHelper
    @inject SignOutSessionStateManager SignOutManager
     
    <AuthorizeView>
        <Authorized>
            Hello, @context.User.Identity.Name!
            <a href="#" @onclick="OnClickEvent">Log out</a>
        </Authorized>
        <NotAuthorized>
            <a href="authentication/login">Log in</a>
        </NotAuthorized>
    </AuthorizeView>
     
    @code{
        private async Task OnClickEvent(MouseEventArgs args)
        {
            await SignOutManager.SetSignOutState();
            UriHelper.NavigateTo("authentication/logout");
        }
    }

  8. Reference the LoginControl page in the MainLayout.razor page.
    [MainLayout.razor]

    <div class="main">
            <div class="top-row px-4 auth">
                <LoginControl />
                <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
            </div>
     
            <div class="content px-4">
                @Body
            </div>
        </div>

  9. Create an Authentication.razor component in the Pages folder to authenticate the user when logging in and out of the app.
    [Pages/Authentication.razor]

    @page "/authentication/{action}"
    @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
    @using Microsoft.Extensions.Configuration
     
    @inject NavigationManager UriHelper
    @inject IConfiguration Config
     
    <RemoteAuthenticatorView Action="@Action">
        <LogOut>
            @{
                var authority = (string)Config["Auth0:Authority"];
                var clientId = (string)Config["Auth0:ClientId"];
     
                UriHelper.NavigateTo($"{authority}/v2/logout?client_id={clientId}");
            }
        </LogOut>
    </RemoteAuthenticatorView>
     
    @code{
        [Parameter]
        public string Action { get; set; }
    }

  10. Now display the authorized content when the app is authorized. If the app is not authorized, display the “Not Authorized” message to the user.
    [Index.razor]

    @page "/"
     
    <AuthorizeView>
        <Authorized>
            <h1>Hello, @context.User.Identity.Name !</h1>
     
            <p>Welcome to your new app.</p>
        </Authorized>
        <NotAuthorized>
            <p>Not Authorized</p>
        </NotAuthorized>
    </AuthorizeView>

  11. Press Ctrl + F5 to run the application and click Log in in the header to authenticate the user.

Refer to this documentation for more details.

View Sample in GitHub 

Permalink

Blazor allows JavaScript isolation in standard JavaScript modules. JavaScript isolation provides the following benefits:

  • JavaScript code is allowed to load only specified components.
  • Imported JavaScript does not affect any global namespace.
  • Library and component consumers are not required to import the related JavaScript.
Follow these steps to implement JavaScript isolation in Blazor:

  1. Create an export JavaScript function in the wwwroot/script folder.
    [isolationScript.js]

    export function jsIsolation(value) {
        console.log(value);
    }

  2. Import the JavaScript function using the IJSRuntime.InvokeAsync method in Blazor and call the JavaScript method on button click event.
    [Index.razor]

    @page "/"
    @inject IJSRuntime JSRuntime
     
    Enter text:<input @bind="content" />
    <button class="btn btn-primary" @onclick="OnClickButton">Click</button>
     
    @code {
        private string content { get; set; }
     
        private async void OnClickButton()
        {
            var jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./script/isolationScript.js"); 
            await jsModule.InvokeVoidAsync("jsIsolation", content);
        }
    }
    The JavaScript code file loads only during a button click event. It will not load again and again for each button click. 

Refer to this documentation for more details.

Permalink

Blazor authentication is implemented to determine who a particular user is. Blazor follows the existing ASP.NET Core authentication mechanisms to show a user’s identity.
Follow these steps to implement authentication with OpenID Connect in Blazor:

  1. Create Blazor application

    Create a Blazor Server app and install a NuGet package named “Microsoft.AspNetCore.Authentication.OpenIdConnect” using the NuGet package manager.

  2. Add OIDC and authentication configuration

    Add OpenID Connect and cookie authentication service configuration to the Blazor Server app in the Startup.cs file.
    [Startup.cs]

    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Authentication.OpenIdConnect;
    using Microsoft.IdentityModel.Tokens;
     
    namespace BlazorServerApp
    {
        public class Startup
        {
          . . .
          . . .
          public void ConfigureServices(IServiceCollection services)
          {
                      . . .
                      . . .
                      services.AddAuthentication(opt =>
                {
                    opt.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    opt.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                }).AddCookie().AddOpenIdConnect("oidc", options =>
                    {
                    options.Authority = "https://demo.identityserver.io/";
                    options.ClientId = "interactive.confidential.short";
                    options.ClientSecret = "secret";
                    options.ResponseType = "code";
                    options.SaveTokens = true;
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.UseTokenLifetime = false;
                    options.Scope.Add("openid");
                    options.Scope.Add("profile");
                    options.TokenValidationParameters = new TokenValidationParameters{ NameClaimType = "name" };
     
                    options.Events = new OpenIdConnectEvents
                    {
                        OnAccessDenied = context =>
                        {
                            context.HandleResponse();
                            context.Response.Redirect("/");
                            return Task.CompletedTask;
                        }
                    };
                });
             }
     
             public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
             {
                 . . .
                 . . .
                                app.UseAuthentication();
                 app.UseAuthorization();
                         }
               }
     }

  3. Add AuthorizeRouteView to the App.razor file

    Add AuthorizeRouteView to display the page matching the specified route only if the user is authorized.
    [App.razor]

    @inject NavigationManager UriHelper
     
    <CascadingAuthenticationState>
        <Router AppAssembly="@typeof(Program).Assembly">
            <Found Context="routeData">
                <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                    <NotAuthorized>
                        @{
                            var returnUrl = UriHelper.ToBaseRelativePath(UriHelper.Uri);
                            UriHelper.NavigateTo($"login?redirectUri={returnUrl}", forceLoad: true);
                        }
                    </NotAuthorized>
                    <Authorizing>
                        Loading...
                    </Authorizing>
                </AuthorizeRouteView>
            </Found>
            <NotFound>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p>Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    </CascadingAuthenticationState>

  4. Add buttons

    Add Log out and Log in buttons to authorize the user in the header section.
    [MainLayout.razor]

    <div class="top-row px-4">
                <AuthorizeView>
                    <Authorized>
                        <form method="get" action="logout">
                            <button type="submit" class="nav-link btn btn-link">Log out</button>
                        </form>
                    </Authorized>
                    <NotAuthorized>
                        <a href="login?redirectUri=/">Log in</a>
                    </NotAuthorized>
                </AuthorizeView>
                <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
            </div>

  5. Redirect requests to log in or log out users

    Create Razor pages for login (Login.cshtml.cs) and logout (Logout.cshtml.cs) redirection requests to IdentityServer for authorization under the Pages folder.
    [Login.cshtml.cs]

    using Microsoft.AspNetCore.Authentication;
     
    namespace BlazorServerApp.Pages
    {
        public class LoginModel : PageModel
        {
            public async Task OnGet(string redirectUri)
            {
                await HttpContext.ChallengeAsync("oidc", new AuthenticationProperties { RedirectUri = redirectUri });
            }
        }
    }
    [Logout.cshtml.cs]
    using Microsoft.AspNetCore.Authentication;
     
    namespace BlazorServerApp.Pages
    {
        public class LogoutModel : PageModel
        {
            public async Task<IActionResult> OnGetAsync()
            {
                await HttpContext.SignOutAsync();
                return Redirect("/");
            }
        }
    }

  6. Show authorized content in Razor component

    Following is the code to display the authorized content when the app is authorized. If the app is not authorized, it displays the “Not Authorized” message to the user.
    [Index.razor]

    @page "/"
     
    <AuthorizeView>
        <Authorized>
            <h1>Hello, @context.User.Identity.Name !</h1>
     
            <p>Welcome to your new app.</p>
        </Authorized>
        <NotAuthorized>
            <p>Not Authorized</p>
        </NotAuthorized>
    </AuthorizeView>

  7. Run the application

    Press Ctrl + F5 to run the application and click Log in in the header to authenticate the user.

    You can download the reference sample on GitHub.

Permalink

Cookies are created by the application and passed to the user’s web browser when the user submits the request. The web browser passes the cookie back to the application to indicate that the user is authenticated. When the user logs out, the cookie is removed.
Follow these steps to set a cookie and read that cookie with authentication in Blazor.

  1. Configure the cookie authentication services in the Startup.cs file.
    [Startup.cs]

    public class Startup
    {
       . . .
       . . .
     
       public void ConfigureServices(IServiceCollection services)
       {
          . . .
          . . .
          services.AddAuthentication("Cookies").AddCookie();
       }
       public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
       {
          . . .
          . . .
          app.UseAuthentication();
          app.UseAuthorization();
     
          app.UseEndpoints(endpoints =>
          {
             endpoints.MapControllers();
             . . .
             . . .
          });
       } 
    }

  2. Now, add a controller page to set the cookie to the browser and redirect the URL.
    [CookieController.cs]

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using System.Collections.Generic;
    using System.Security.Claims;
    using System.Threading.Tasks;
     
    namespace blazorcookie
    {
        [Route("/[controller]")]
        [ApiController]
        public class CookieController : ControllerBase
        {
            [HttpPost]
            public async Task<ActionResult> Login([FromForm] string name)
            {
                ClaimsIdentity claimsIdentity = new ClaimsIdentity(new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, name)
                }, "auth");
                ClaimsPrincipal claims = new ClaimsPrincipal(claimsIdentity);
                await HttpContext.SignInAsync(claims);
                return Redirect("/");
            }
        }
    }

  3. To use the authorized view, configure the CascadingAuthenticationState component in the App.razor file. This will let you check the authentication state inside the Blazor application.
    [ App.razor ]

    <CascadingAuthenticationState>
        <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
            . . .
            . . .
            . . .
        </Router>
    </CascadingAuthenticationState>

  4. Use form action to call the cookie controller to set the cookie and have it read by AuthorizeView user name.
    [Index.razor]

    @page "/"
     
    <AuthorizeView>
        <Authorized>
            <h1>Hello @context.User.Claims.First().Value</h1>
        </Authorized>
    </AuthorizeView>
     
    <form action="cookie" method="post">
        <input type="text" name="name" />
        <input type="submit" />
    </form>

  5. Run the application and submit the form request. You will find an authentication cookie with the scheme “Cookies,” which was specified in the ConfigureServices() method of the Startup class.


    Refer to this blog post for more details and download the sample on GitHub.

Permalink

Blazor Server is a stateful application framework that maintains a connection to the server. Its state will occur in the server memory known as circuit. We can preserve the state in Blazor server-side three ways:

  • Server-side storage
  • URL
  • Browser storage

The following example demonstrates how to preserve the state in Blazor server-side using browser storage.

Install the Blazored.SessionStorage NuGet package in the NuGet package manager to store the session data in Blazor. Add the Blazored SessionStorage configuration to the Blazor Server app.

[Startup.cs]

using Blazored.SessionStorage;
public class Startup
{
        public void ConfigureServices(IServiceCollection services)
    {
       //. . .
       services.AddBlazoredSessionStorage();
    }
}

[Index.razor]

@page "/"
@inject Blazored.SessionStorage.ISessionStorageService sessionStorage

<button class="btn btn-primary" @onclick="Clear">Clear Session</button>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await sessionStorage.SetItemAsync("ID", "2021");
        await sessionStorage.SetItemAsync("Name", "John Smith");
    }
    public async void Clear()
    {
        // This will clear the session data.
        await sessionStorage.ClearAsync();
    }
}

Refer to this documentation for details.

Permalink

The background long-running tasks are cancelled by using the CancellationToken object in Blazor. If an application downloads the data to render in view and then you navigate to another page, the CancellationToken cancels the download by using the Cancel() method. Call the Cancel()  method by using the Dispose() method to cancel the background running task.

In the following example, the background running task in the FetchData.razor component is cancelled when a user navigates to other pages in Blazor.
[FetchData.razor]

@page "/fetchdata" 
<PageTitle>Weather forecast</PageTitle> 
@using BlazorServerApp.Data 
@using System.Threading 
@implements IDisposable 
@using System.Net.Http 
@inject WeatherForecastService ForecastService 

<h1>Weather forecast</h1> 
<p>This component demonstrates fetching data from a service.</p> 
@if (forecasts == null) 
{ 
    <p><em>Loading...</em></p> 
} 
else 
{ 
    <table class="table"> 
        <thead> 
            <tr> 
                <th>Date</th> 
                <th>Temp. (C)</th> 
                <th>Temp. (F)</th> 
                <th>Summary</th> 
            </tr> 
        </thead> 
        <tbody> 
            @foreach (var forecast in forecasts) 
            { 
                <tr> 
                    <td>@forecast.Date.ToShortDateString()</td> 
                    <td>@forecast.TemperatureC</td> 
                    <td>@forecast.TemperatureF</td> 
                    <td>@forecast.Summary</td> 
                </tr> 
            } 
        </tbody> 
    </table> 
} 

@code { 
    private WeatherForecast[]? forecasts; 
    private readonly HttpClient httpClient = new HttpClient(); 
    private CancellationTokenSource? cancellationToken; 
    protected override async Task OnInitializedAsync () 
    { 
        cancellationToken = new CancellationTokenSource(); 
        try 
        { 
            forecasts = await httpClient.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json", cancellationToken.Token); 
        } 
        catch (OperationCanceledException) 
        { 
            // Task was cancelled 
        } 
    } 

    public void Dispose () 
    { 
        cancellationToken?.Cancel(); 
        cancellationToken?.Dispose(); 
    } 
} 

Refer to this documentation for more details.

Permalink

Blazor CSS isolation is a process that occurs at build time. By utilizing CSS isolation, you can restrict the scope of CSS files or styles to specific components within your app, preventing them from affecting other components globally. The CSS selectors are rewritten by Blazor to match markup rendered by the component. These rewritten CSS files are loaded as static assets in the {{Your_App_Name}}.styles.css.
The static file name is referenced inside the tag of the _Host.cshtml file.
[_Host.cshtml] / [index.html]

<head>
    // . . .
    <link href="{{Your_App_Name}}.styles.css" rel="stylesheet" />
</head>
Follow these steps to enable CSS isolation for a Blazor application.

  1. Create a Blazor Server or WebAssembly application.

  2. To enable CSS isolation, create a razor.css file matching the .razor file for the component in the same folder.
    For example, if your component is named “Isolate.razor,” create a file alongside the component named “Isolate.razor.css.” The Isolate.razor.css file is placed in the same folder as the Isolate.razor component.
    [Pages/Isolate.razor]

    @page "/isolate"
     
    <h1>Hello, World</h1>
    <p>Welcome to your new app</p>
    [Pages/Isolate.razor.css]
    h1 {
        color: blue;
        font-style: italic;
        text-shadow: 2px 2px 2px gray;
    }
    The Isolate.razor.css styles work only for the Isolate.razor component. Refer to this documentation for more details.  

    View Sample in GitHub

Permalink

The LocationChanged event handler is triggered when there is a change in the navigation location. In the example below, a JavaScript interop function is used to display an alert to the user whenever the navigation location changes:

@inject NavigationManager UriHelper
@inject IJSRuntime JSRuntime 

<p> When navigating to another page, an alert box will be displayed to indicate the navigation. </p> 

@code { 
    protected override void OnInitialized ()   
    { 
        UriHelper.LocationChanged += DetectNavigation; 
    } 

    private void DetectNavigation ( object sender, LocationChangedEventArgs e ) 
    { 
        JSRuntime.InvokeVoidAsync("alert", "Navigation event is triggered"); 
    } 
} 

Refer to this documentation for more details. 

Permalink

To redirect users to your login page when their session has expired, create a RedirectLogin Razor component and implement the AuthenticationState to check whether sessions are expired or valid.
Follow these steps to redirect to a login page when sessions time out in Blazor:

Create a RedirectLogin component to redirect users to a login page when their session has expired.

[RedirectLogin.razor]

@inject NavigationManager UriHelper
  
 @code {
     [CascadingParameter]
     public Task<AuthenticationState> StateAuthenticate { get; set; }
  
     protected override async Task OnInitializedAsync()
     {
         var authenticationState = await StateAuthenticate;
         if (authenticationState?.User?.Identity is null || !authenticationState.User.Identity.IsAuthenticated)
         {
             var returnUri = UriHelper.ToBaseRelativePath(UriHelper.Uri);
             if (string.IsNullOrWhiteSpace(returnUri))
             {
                 UriHelper.NavigateTo("YourLoginPath", true);
             }
             else
             {
                 UriHelper.NavigateTo($"YourLoginPath?returnUrl={returnUri}", true);
             }
         }
     }
 }

Now, initialize the RedirectLogin component in App.razor to identify whether a session is expired or valid.

[App.razor]

<Router AppAssembly="@typeof(Program).Assembly">
	<Found Context="routeData">
		<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
			<NotAuthorized>
				<RedirectLogin />
			</NotAuthorized>
			// . . .
		</AuthorizeRouteView>
	</Found>
	// . . .
</Router> 

View Sample in GitHub

Permalink

To redirect to the login page when a user is not authenticated in Blazor WebAssembly:

  • Create a login page component.
  • Add the login page component to the NotAuthorized tag.
Follow these steps to redirect to the login page if the user is not authenticated:

  1. Create a login page component for redirection.
    [LoginRedirect.razor]

    @inject NavigationManager UriHelper
    @code {
        protected override void OnInitialized()
        {
            UriHelper.NavigateTo("login");
        }
    }

  2. Now add the LoginRedirect component to the NotAuthorized tag to redirect to the login page if the user is not authorized.
    [App.razor]

    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
            <Found Context="routeData">
                <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                    <NotAuthorized>
                        <LoginRedirect />
                    </NotAuthorized>
                </AuthorizeRouteView>
            </Found>
            <NotFound>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p>Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    Refer to this documentation for more details.

    View Sample in GitHub

Permalink

To convert the date and time to a client’s or user’s time zone in Blazor Server, use the JavaScript function to get the current offset time difference from UTC in minutes using the GetLocalDateTime() method in Blazor using JavaScript Interop. Display the local time by the current offset time difference.

Follow these steps to convert the server time zone to the user’s time zone.

  1. To define the TimeZoneService class and the GetLocalDateTime() method, create a new file named TimeZoneService.cs in your project. Inside the TimeZoneService.cs file, define the TimeZoneService class and implement the GetLocalDateTime() method.

    using Microsoft.JSInterop;
    namespace BlazorServerApp
    {
    public class TimeZoneService
    {
    private readonly IJSRuntime _jsRuntime;
    private TimeSpan? _userOffset;
    public TimeZoneService ( IJSRuntime jsRuntime )
    {
    _jsRuntime = jsRuntime;
    }
    public async ValueTask GetLocalDateTime ( DateTimeOffset dateTime )
    {
    if (_userOffset == null)
    {
    int offsetInMinutes = await _jsRuntime.InvokeAsync("blazorGetTimezoneOffset");
    _userOffset = TimeSpan.FromMinutes(-offsetInMinutes);
    }
    return dateTime.ToOffset(_userOffset.Value);
    }
    }
    }

  2. To register the time zone service, add the following code snippet to the Program.cs file.
    [Program.cs]

    builder.Services.AddScoped();

  3. Create a JavaScript file named BlazorInterop.js within the wwwroot directory and include the provided code snippet in it.
    [BlazorInterop.js]

    function blazorGetTimezoneOffset() {
    return new Date().getTimezoneOffset();
    }

  4. To add a reference to the JavaScript file, open the Pages/_Layout.cshtml or _Host.cshtml file, and include the following line of code:
    [_Layout.cshtml/_Host.cshtml]

    <script src="~/BlazorInterop.js">

  5. Finally, you can utilize the service to display a date and time in your application.
    [Index.razor]

    @page "/" 
    @inject TimeZoneService TimeZoneService

    <h1>Current date and time</h1>

    <p>Now (UTC): @DateTimeOffset.UtcNow.ToString()</p>
    <p>Now (local): @localTime.ToString()</p>

    @code {
    DateTimeOffset localTime;
    protected override async Task OnAfterRenderAsync ( bool firstRender )
    {
    if (firstRender)
    {
    // TODO: Require server render mode while instantiating the component to execute JavaScript in OnInitializedAsync.
    // In _Host.cshtml:
    localTime = await TimeZoneService.GetLocalDateTime(DateTimeOffset.UtcNow);
    }
    }
    }
    Refer to this link for more details.

    View Sample in GitHub
Permalink

To get the current data and time from client, use the date object (new Date()) in JavaScript using JavaScript Interop. It will return the browser’s date and time. Following is the code to get the current date and time from the client in Blazor WebAssembly.

[Index.razor] 

@page "/" 
@inject IJSRuntime JsRuntime 
 
<p id="date-time"></p> 
 
@code { 
    protected async override Task OnAfterRenderAsync(bool firstRender) 
    { 
        if (firstRender) 
        { 
            await JsRuntime.InvokeVoidAsync("GetDateTime"); 
        } 
    } 
} 

[index.html]

<body>  
      . . .  
      . . .  
 
   <script> 
        function GetDateTime() { 
           // Date object will return browser's date and time by default in JavaScript. 
           document.getElementById("date-time").innerHTML = new Date(); 
        } 
    </script> 
</body >

View Sample in GitHub

Permalink

Google reCaptcha is a process that helps to protect websites form spam and abuse. To implement Google reCaptcha in Blazor, refer to the Google reCaptcha script link the WebAssembly app and render the reCaptcha by calling the JavaScript function.
Follow these steps to implement Google reCaptcha in Blazor WebAssembly.

  1. Add the Google reCaptcha renderer function in a separate JavaScript file under the wwwroot folder.
    [googlereCaptcha.js]

    function googleRecaptcha(dotNetObject, selector, sitekeyValue) {
        return grecaptcha.render(selector, {
            'sitekey': sitekeyValue,
            'callback': (response) => { dotNetObject.invokeMethodAsync('CallbackOnSuccess', response); },
            'expired-callback': () => { dotNetObject.invokeMethodAsync('CallbackOnExpired', response); }
        });
    };
     
    function getResponse(response) {
        return grecaptcha.getResponse(response);
    }

  2. Add the reCaptcha script link and reference the reCaptcha.js file source in index.html.
    [index.html]

    <body> 
          . . . 
          . . . 
     
       <script src="googlereCaptcha.js"></script>
       <!-- reCaptcha rendering script -->
       <script src="https://www.google.com/recaptcha/api.js"></script>
    </body >

  3. Now call the rendering reCaptcha function in JavaScript from the Razor page using JavaScript Interop and show the reCaptcha response on button click.
    Note: To start using reCaptcha, you need to generate the API site key for your site. Refer to this link to generate the site key.
    [Index.razor]  

    @page "/"
    @inject IJSRuntime JSRuntime
    @using System.ComponentModel
     
    <h3>Google reCAPTCHA</h3>
     
    <div id="google_recaptcha "></div>
     
    <button class="btn btn-primary" @onclick="ShowResponse">Show Response</button>
     
    <br />
     
    <p>@captchaResponse</p>
     
    @code {
        private string captchaResponse;
     
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                await JSRuntime.InvokeAsync<int>("googleRecaptcha", DotNetObjectReference.Create(this), "google_recaptcha ", "your-sitekey");
            }
            await base.OnAfterRenderAsync(firstRender);
        }
     
        [JSInvokable, EditorBrowsable(EditorBrowsableState.Never)]
        public void CallbackOnSuccess(string response)
        {
            captchaResponse = response;
        }
     
        [JSInvokable, EditorBrowsable(EditorBrowsableState.Never)]
        public void CallbackOnExpired(string response)
        {
            //...
        }
     
     
        private void ShowResponse()
        {
            captchaResponse = $"The response for the Google reCAPTCHA widget: {captchaResponse}";
        }
    }

    Google recaptcha

Permalink

You can call a JavaScript function with parameters using JavaScript Interop.

Syntax:

JsRuntime.InvokeVoidAsync("JS method name", "parameters");

To call a JavaScript method with parameters in Blazor WebAssembly, you can follow the code snippet provided below: 

[Index.razor]

@page "/" 
@inject IJSRuntime JsRuntime 

<p>Here's an example of how to call a JavaScript method with parameters in Blazor WebAssembly.</p> 

@code { 
    protected override async void OnInitialized () 
    { 
        string content = "JavaScript function called with parameter"; 
        await JsRuntime.InvokeVoidAsync("jsFunction", content); 
    } 
} 

[index.html]

<body> 
      . . . 
      . . . 

   <script>
        function jsFunction(value) {
            // Parameter value has been passed here.
            console.log(value);
        };
    </script>
</body >

Refer to this documentation for more information. 

View Sample in GitHub

Permalink

Close a browser window from a page in Blazor WebAssembly using JavaScript Interop with the window.close() method. The window.close() method closes the currently opened window.

In the following example, open a new browser window and close it using the window.close() method with a button onclick event.

[Index.razor]

@page "/"
@inject IJSRuntime JsRuntime

<button @onclick="@(e => OnButtonClick("open"))">Open Window</button>
<button @onclick="@(e => OnButtonClick("close"))">Close Window</button>

@code {        
    private void OnButtonClick(string value)
    {
        JsRuntime.InvokeVoidAsync($"window.{value}");
    }
}
Permalink

To read a JSON file in Blazor WebAssembly, you can utilize the HttpClient class along with the GetFromJsonAsync() method.  

Follow these steps to achieve that: 

To proceed, create or load a JSON file in the wwwroot folder. As an example, I have created a basic employee.json file and will read its values within a Razor component. 

[wwwroot/employee.json]

[{"id": "emp1"}, {"id": "emp2"}, {"id": "emp3"}]

[Index.razor]

@page "/"
@inject HttpClient Http

@if (employees == null)
{
    <p>Loading...</p>
}
else
{
    @foreach (var employee in employees) 
    { 
        <p>Employee ID: @employee.Id</p>
    }
}


@code {
    private Employee[] employees;

    protected override async Task OnInitializedAsync()
    {
        employees = await Http.GetFromJsonAsync<Employee[]>("employee.json");
    }

    public class Employee
    {
        public string Id { get; set; }
    }
}

Refer to this documentation for more details.

Permalink

Get a user agent in Blazor WebAssembly using JavaScript Interop with the navigator.userAgent property.

[Index.razor]

@page "/"
@inject IJSRuntime JsRuntime

<p>@userAgent</p>

@code {

    private string userAgent { get; set; }

    protected override async Task OnInitializedAsync()
    {
        userAgent = await JsRuntime.InvokeAsync<string>("getUserAgent");
        
    }
}

[index.html]

<body> 
      . . . 
      . . . 
   <script>
        window.getUserAgent = () => {
            return navigator.userAgent;
        };
    </script> 
</body > 

View Sample in GitHub

Permalink

When we run a Blazor application with a SQL Server localdb for storing authentication data, by following these steps we can implement SQLite in it:

  • Update the NuGet package.
  • Modify the Startup.cs file and connection string.
  • Modify the database migration code.

Updating the NuGet package:
Remove the Microsoft.EntityFrameWorkCore.SqlServer NuGet package from the current package manager and add the Microsoft.EntityFrameWorkCore.Sqlite package.

Modifying the Startup.cs file:
Change the Startup.cs file from options.UseSqlServer to options.UseSqlite.

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
…………………. . .
namespace BlazSqlite
{
    public class Startup
    {
………………… . . 
public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SqlDbContext>(options =>
                options.UseSqlite(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)…………… . }

Modify the connection string in the AppSettings.json file.

DataSource=<yourdbname>.db” (points to your sqlite database file)

Modifying the database migration code:
Modify the database migration code to avoid the errors that would occur after performing the previous steps.
Change the “00000000000000_CreateIdentitySchema.cs” file as follows.

“.HasAnnotation(“SqlServer:ValueGenerationStrategy”, SqlServerValueGenerationStrategy.IdentityColumn)” or “.Annotation(“SqlServer:ValueGenerationStrategy”, SqlServerValueGenerationStrategy.IdentityColumn)”

to “.HasAnnotation(“Sqlite:Autoincrement”, true)” or “.Annotation(“Sqlite:Autoincrement”, true)”

Run the app and start registering a new user for authentication.
Refer to this link to learn how to create a SQL database with a Blazor Server application.
Refer to this link for more information about authentication with SQLite and Blazor.

Permalink

When using Blazor WebAssembly to enforce Office 365 authentication, it is necessary to include the tenant ID and client ID in the appsettings.json file, which can be found under your Azure account.

In Azure AD, a tenant is a dedicated Azure service instance that a company receives and owns after signing up for a Microsoft cloud service such as Azure or Microsoft 365. Each tenant ID is distinct and separate from other tenants.

Procedure outline

  1. Create a new environment in Power Apps and note the web API service root URL.
  2. Register your Azure account.
  3. Create a WebAssembly app using Azure AD for authentication.
  4. Update the callback URL in the app to run the app.
  5. Grant API permissions to access data from the dataverse and modify the code to display the data in the app.

Please refer to this link for a detailed explanation of the implementation of Office 365 authentication.

Permalink

The routing interception concept is sometimes used to restrict user access to some page or link if they did some work or made some changes on a specific page.
In the following code, you can use the NavigateTo() method to intercept routing inside the If condition. The routing happens according to the values passed in the condition.

  1. Create the class file [RouteData.cs].

     …………………. ..
    namespace BlazorApp.Data
    {
        public class RouteData
        {
            public string Textfield { get; set; }
        }
    }

  2. To register the RouteData class as a scoped service in Program.cs, you can use the following code snippet.
    [Program.cs]

    builder.Services.AddScoped<BlazorServerApp.Data.RouteData>();

  3. Add the following code to the [Index.razor] page.

     @page "/" 
    @inject Data.RouteData RouteData
    <p>Type the text below to allow Home page intercepting</p>
    <textarea @bind="RouteData.Textfield"></textarea>

  4. Check the condition and allow the navigation process in the [NavMenu.razor] page as shown.

    @inject NavigationManager NavigationManager
    @inject {Your App name}.Data.RouteData RouteData
    <div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
        <ul class="nav flex-column">
            <li class="nav-item px-3">
                <NavLink class="nav-link" @onclick="Navigate" Match="NavLinkMatch.All">
                    <span class="oi oi-home" aria-hidden="true"></span> Home
                </NavLink>
            </li>
                      ……………………. . .
        </ul>
    </div>
    @code {
        private bool collapseNavMenu = true;
        private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
        private void ToggleNavMenu()
        {
            collapseNavMenu = !collapseNavMenu;
        }
               private void Navigate()
        {
            if (RouteData.Textfield == null)
            NavigationManager.NavigateTo("");
        }
    ………………… . . .

  5. Output

    Home page routing is intercepted when you type text in the text area. When the text area value is null, the app navigates to the home page.

    View Sample in GitHub
Permalink

We can use NavigationManager, which has a built-in LocationChanged event that will be triggered when the user navigates away from the current page.
In the following code, we use an alert JS function by adding the IJSRuntime to show the user has navigated by overriding the OnInitialized() method through the alert message.

[Index.razor]

@page "/"
@inject NavigationManager nav
@inject IJSRuntime JSRuntime
<h1>Detect Navigation events</h1>
@code {
    protected override void OnInitialized()
    {
        nav.LocationChanged += (o, e) =>
        {
           JSRuntime.InvokeVoidAsync("alert", "User has navigated to other page");
        };  }}

When the user navigates from the home page to the counter page or vice versa, the alert message stating “User has navigated to other page” will be shown.

View Sample in GitHub

Permalink

We generally use authentication for specific pages to prevent access by unauthorized users. In the case that we do want a specific page to be accessed by any users, not only authorized users, we will use the anonymous users category.

To make a Blazor page accessible by all users, use [AllowAnonymous] with @attribute code in the _Imports.razor file in your app.

[_Imports.razor]

@attribute [AllowAnonymous]

By doing this users can access the secured page (here we secured the fetch data) without a login.

Permalink

As the Blazor client runs in the browser, both user authorization and authentication for WebAssembly will be completely handled by the back-end API.
The back-end web API must handle the authorization on every API call, and it tells the Blazor app whether the user is authenticated and has resource access. It enables your Blazor app to show the correct context to the user.

  1. Create a Blazor WebAssembly app using Visual Studio 2019 with ASP.NET Core hosting enabled.

  2. Reorganize the folder names according to usage:

    • Sample.Server is renamed to Sample.Api as we are going to use this as our API.
    • Sample.Client is renamed to Sample.WebApp.
    • Sample.Shared is renamed to Sample.Common.

  3. In the Sample.Common folder, create a Models folder, and move the generated WeatherForecast class to this folder and change its namespace to Sample.Models. Add the AuthorizedUser class in the Models folder under the Sample.Shared folder.

    namespace Sample.Common
    {
        public class AuthorizedUser
        {
            public string Name { get; set; }
            public string Roles { get; set; }
        }

  4. Add a new SettingsController file in the Sample.Api folder.

    using Sample.Models;
    using Microsoft.AspNetCore.Mvc;
     
    namespace Sample.Api.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class SettingsController : ControllerBase
        {
            [HttpGet("user")]
            public AuthorizedUser GetUser()
            {
                return new AuthorizedUser();
     
                // User signed in:
                //return new AuthorizedUser { Name = "UserName" };
            }
        }
    }

  5. Prepare your Sample.WebApp

    1. Install the NuGet package Microsoft.AspNetCore.Components.Authorization.

    2. Add the installed package in the _Imports.razor file.

    3. Wrap the <CascadingAuthenticationState> in the App.razor file.

    4.  <CascadingAuthenticationState>
          <Router AppAssembly="@typeof(Program).Assembly">
              ...
          </Router>
      </CascadingAuthenticationState>

    5. Add a Service folder and add a ClientAuthorizationService class in it.

    6. using Sample.Models;
      using Microsoft.AspNetCore.Components;
      using Microsoft.AspNetCore.Components.Authorization;
       
      using System;
      using System.Collections.Generic;
      using System.Net.Http;
      using System.Security.Claims;
      using System.Threading.Tasks;
       
      namespace Sample.WebApp.Services
      {
          public class ClientAuthorizationService : AuthenticationStateProvider
          {
              private const string AuthenticationType = "BackEnd";
              private readonly HttpClient _httpClient;
             public ClientAuthorizationService(HttpClient httpClient)
          {
               if (httpClient == null) throw new ArgumentNullException(nameof(httpClient));
               _httpClient = httpClient;
              }
             public string ApiUriGetAuthorizedUser { get; set; }
       
             public string ApiUriSignIn { get; set; }
       
             public string ApiUriSignOut { get; set; }
       
              public AuthorizedUser AuthorizedUser { get; private set; } = new AuthorizedUser();
           public override async Task<AuthenticationState> GetAuthenticationStateAsync()
         {
        ClaimsPrincipal user;
        if (!string.IsNullOrEmpty(ApiUriGetAuthorizedUser))
        AuthorizedUser = await _httpClient.GetJsonAsync<AuthorizedUser>(ApiUriGetAuthorizedUser);
        if (string.IsNullOrEmpty(AuthorizedUser.Name))     {
                      user = new ClaimsPrincipal();
                 }
        else   {
       var identity = new ClaimsIdentity(CreateClaims(AuthorizedUser), AuthenticationType);
        user = new ClaimsPrincipal(identity);
       }
      return new AuthenticationState(user);
         }
       
        private static IEnumerable<Claim> CreateClaims(AuthorizedUser authorizedUser)
          {      yield return new Claim(ClaimTypes.Name, authorizedUser.Name);
       
            var roles = authorizedUser.Roles?.Split(',') ?? new string[0];
      foreach (var role in roles)
       yield return new Claim(ClaimTypes.Role,role.Trim());
      }
      }
      }

    7. Add the following code in the Program.cs file.

    8. using Microsoft.AspNetCore.Components.Authorization;
      using System.Net.Http;
      ...
       public static async Task Main(string[] args)
      {
          var builder = WebAssemblyHostBuilder.CreateDefault(args);
       
          builder.Services.AddAuthorizationCore();
          builder.Services.AddScoped<ClientAuthorizationService>(CreateAuthorizationService);
          builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<ClientAuthorizationService>());
          builder.Services.AddOptions();
          ...
       private static ClientAuthorizationService CreateAuthorizationService(IServiceProvider serviceProvider){
          var httpClient = serviceProvider.GetRequiredService<HttpClient>();
          var service = new ClientAuthorizationService(httpClient)
          {
              ApiUriGetAuthorizedUser = "api/settings/user",
       
              ApiUriSignIn = "AzureADB2C/Account/SignIn",
              ApiUriSignOut = "AzureADB2C/Account/SignOut",
          };
          return service;
      }

    9. Create a new Razor component SignInDisplay.razor in the shared folder.

    10. @using Sample.WebApp.Services
      @inject ClientAuthorizationService AuthorizationService
      <AuthorizeView>
          <Authorized>
              <div>
                  <span class="form-control">@AuthorizationService.AuthorizedUser.Name</span>
              </div>
              <div>
                  <a class="btn btn-outline-primary" href="@AuthorizationService.ApiUriSignOut">Sign Out</a>
              </div>
          </Authorized>
          <NotAuthorized>
              <div>
                  <a class="btn btn-outline-primary" href="@AuthorizationService.ApiUriSignIn">Sign In</a>
              </div>
          </NotAuthorized>
      </AuthorizeView>

    11. In the Shared/MainLayout page, add the following code.

    12. <div class="top-row px-4 auth">
      <SignInDisplay />
      </div>

  6. Run the app with Sample.Api as the startup file and see the output as follows.

    Since Windows authorization is not yet implemented in the API, If you click the Sign In button, you will be rerouted to a page stating, “Sorry, there’s nothing at this address.” When you change the code in the GetUser() method in the SettingsController to include the name of the user, the app will be shown as follows.

  7. Refer to this link for more information.

Permalink

Role-based authorization is a declarative way of limiting resource access that first appeared in ASP.NET (pre-Core). In order for the user to access certain resources, developers must specify a role that the user belongs to. This is done by using the [Authorize] attribute. Users can have a single role or multiple roles depending on the backing store used. The following procedure explains how to implement role-based authorization.

  1. Create a Blazor WebAssembly app and add the following role service in the Startup class under ConfigureServices.

     public void ConfigureServices(IServiceCollection services)
            {
             ………………….. .
                services.AddDefaultIdentity<IdentityUser>()
                        .AddRoles<IdentityRole>()
                        .AddEntityFrameworkStores<ApplicationDbContext>();}

  2. Add specific roles in your database by overriding the OnModelCreating method of ApplicationDBContext. The User and Admin roles are added in the following code.

     public class ApplicationDbContext : IdentityDbContext
            {
                public ApplicationDbContext(DbContextOptions options) : base(options)
                {
                }
     
                protected override void OnModelCreating(ModelBuilder builder)
                {
                    base.OnModelCreating(builder);
     
                    builder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "User", NormalizedName = "USER", Id = Guid.NewGuid().ToString(), ConcurrencyStamp = Guid.NewGuid().ToString() });
                    builder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Admin", NormalizedName = "ADMIN", Id = Guid.NewGuid().ToString(), ConcurrencyStamp = Guid.NewGuid().ToString() });
                }
            }
    Once the roles are added, generate a migration and apply it to your database.

  3. Add – Migration SeedRoles
    Update-Database
  4. Add users to the roles by updating the action on the Accounts controller. All new users are added to the User role, except for the admin email.
    [AccountsController.cs]

    [HttpPost]
            public async Task<IActionResult> Post([FromBody]RegisterModel model)
            {
                var newUser = new IdentityUser { UserName = model.Email, Email = model.Email };
                var result = await _userManager.CreateAsync(newUser, model.Password);
                if (!result.Succeeded)
                {
                    var errors = result.Errors.Select(x => x.Description);
                    return BadRequest(new RegisterResult { Successful = false, Errors = errors });
                }  
                await _userManager.AddToRoleAsync(newUser, "User");
                if (newUser.Email.StartsWith("admin"))
                {
                    await _userManager.AddToRoleAsync(newUser, "Admin");
                }
                return Ok(new RegisterResult { Successful = true });
            }
  5. Update the Login method in the LoginController.
    Add roles as claims to the JSON web token (JWT) since we are assigning new users to roles at signup, so we need to pass this information to Blazor. Add the following code in the Login method.

  6. Current users can be taken through UserManager, which is used to get their roles.
    public async Task<IActionResult> Login([FromBody] LoginModel login)
    {
    …………….. .
    var user = await _signInManager.UserManager.FindByEmailAsync(login.Email);
                var roles = await _signInManager.UserManager.GetRolesAsync(user);
                var claims = new List<Claim>();
     
                claims.Add(new Claim(ClaimTypes.Name, login.Email));
                       
                 foreach (var role in roles)
                {
                    claims.Add(new Claim(ClaimTypes.Role, role));
                }
    …………….. . .
    }

  7. Add roles in client-side Blazor.
    Once the new users are signed up, we have to get those roles via JWT. To do this, we add the following code in the ParseClaimsFromJwt method, which will take JWT, decode it, extract claims, and return it.

    private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
            {
                var claims = new List<Claim>();
                var payload = jwt.Split('.')[1];
                var jsonBytes = ParseBase64WithoutPadding(payload);
                var keyValuePairs = JsonSerializer.Parse<Dictionary<string, object>>(jsonBytes);
     
                keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);
     
                if (roles != null)
                {
                    if (roles.ToString().Trim().StartsWith("["))
                    {
                        var parsedRoles = JsonSerializer.Parse<string[]>(roles.ToString());
     
                        foreach (var parsedRole in parsedRoles)
                        {
                            claims.Add(new Claim(ClaimTypes.Role, parsedRole));
                        }
                    }
                    else
                    {
                        claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
                    }
     
                    keyValuePairs.Remove(ClaimTypes.Role);
                }
     
                claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));
     
                return claims;
            }
     
            private byte[] ParseBase64WithoutPadding(string base64)
            {
                switch (base64.Length % 4)
                {
                    case 2: base64 += "=="; break;
                    case 3: base64 += "="; break;
                }
                return Convert.FromBase64String(base64);
            }
    We have to check that the first character is [, indicating it’s a JSON array. If the role claim is present and if the [ character is found, then we have to extract the individual role names from the roles entered. We have to loop these role names and add each as a claim, but if the role is not an array, then it is added as a single role claim.

    To call ParseClaimsFromJwt, we need to update the MarkUserAsAuthenticated method as shown.
    public void MarkUserAsAuthenticated(string token)
            {
                var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt"));
                var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
     
                NotifyAuthenticationStateChanged(authState);
            }

  8. Update the Login method on the AuthService to pass the token rather than the email when calling MarkUserAsAuthenticated.

    public async Task<LoginResult> Login(LoginModel loginModel)
            {
    ……………. .
                var result = await _httpClient.PostJsonAsync<LoginResult>("api/Login", loginModel);
     
                if (result.Successful)
                {
                    await _localStorage.SetItemAsync("authToken", result.Token);
                    ((ApiAuthenticationStateProvider)_authenticationStateProvider).MarkUserAsAuthenticated(result.Token);
                    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", result.Token);
     
                    return result;
                }
     
                return result;
            }

  9. Apply role-based authentication to the API.
    We can allow access to a specific page by an admin user alone through the Authorize attribute as shown.

    namespace BlazorWebAssembly.Server.Controllers
    {
        …………. . .
     
        public class SampleDataController : Controller
        {
             …… . .
            [Authorize(Roles = "Admin")]
            [HttpGet("[action]")]
            public IEnumerable<WeatherForecast> WeatherForecasts()
            {
                var rng = new Random();
                return Enumerable.Range(1, 5).Select(index => new WeatherForecast
                {
                    Date = DateTime.Now.AddDays(index),
                    TemperatureC = rng.Next(-20, 55),
                    Summary = Summaries[rng.Next(Summaries.Length)]
                });
            }
        }

  10. Apply role-based authentication in the Blazor page.
    Use the @attribute directive with the [Authorize] attribute in a Blazor page to restrict the user access to specific pages.
    The following code allows the admin user alone to see the fetchdata page.

    @page "/fetchdata"
    @attribute [Authorize(Roles = "Admin")]
    @using BlazorAuthorization.Shared
    Role based authentication 1

    When we log in using accounts other than admin, we are unable to load the fetchdata page.

    Role based authentication 2

  11. We can also add role-based authorization using the AuthorizeView component in the Index.razor page as shown in the following code.

    <AuthorizeView Roles="User">
        <p>You can only see this if you satisfy the IsUser policy.</p>
    </AuthorizeView>  
    <AuthorizeView Roles="Admin">
        <p>You can only see this if you satisfy the IsAdmin policy.</p>
    </AuthorizeView>

    The output text is shown according to the accounts (admin/user) the user is signed in as.
    Refer to this link for further information about role-based authorization in Blazor WebAssembly.

Permalink

Google authentication is a process by which the user can use their Google accounts to get authorized to access particular data. Follow this procedure to implement Google authentication.

  1. Prerequisites
    • Visual Studio latest version
    • .NET Core SDK latest version.
  2. Create a Blazor Server app with the latest .NET support.

  3. Set the Authentication Type as Individual Accounts and then click Create.

  4. Go to Tools > NuGet Package Manager > Package Manager Console. Before running the app, we have to perform migration by running the following command in the Package Manager Console.

  5. Update-Database


    To configure a Google API Console project, check whether SSL is enabled by right-clicking the project name, selecting Properties, and selecting the Debug property. I need this URL for configuration.

  6. To create a Google API Console project, follow these steps.

    1. Go to https://developers.google.com/identity/sign-in/web/sign-in#before_you_begin.

    2. Click the Credentials page link under the topic “Create authorization credentials.” Sign in with your Google account and create a project by providing a project name.

    3. Click Create Credentials and select OAuth client ID.

    4. Select the Application type and type the name of the OAuth ID and the redirect URI (localhost URL given to enable SSL in your app) as shown.


    5. Click Create and note your client ID and client secret, which are important for Google authentication.

  7. Install the Google Authentication middleware NuGet package by typing the following command in the Package Manager Console.

  8. NuGet\Install-Package Microsoft.AspNetCore.Authentication.Google -Version 7.0.9

  9. Configure the Blazor app to use Google authentication. Right-click the project, select Manage User Secrets, and type the following code.

    {  "Authentication:Google:ClientId": "your Google client ID",
      "Authentication:Google:ClientSecret": "your Google client secret"}

  10. Open the Program.cs file and add the following code under the ConfigureServices method.

    builder.Services.AddAuthentication().AddGoogle(googleOptions =>
    {
    googleOptions.ClientId = "Authentication:Google:Your Google ClientId";
    googleOptions.ClientSecret ="Authentication:Google:Your Google ClientSecret here";
    });

  11. Note the following about this code:

    • The AddGoogle() method is used to configure the authentication process in our application.

    • ClientID and ClientSecret will be read from the secrets.json file by the code.

  12. Add Google authorization to the Blazor page.
    Use the Authorize attribute by using the @ directive in a Blazor page to restrict unauthorized users.

     @page "/fetchdata"
    @attribute [Authorize]
    @using Microsoft.AspNetCore.Authorization
    This code will allow the authorized user alone to see the fetchdata page

  13. Output

    The following image shows the output after running the application.

  14. The following image shows the output after clicking Login to navigate to the authorization page.

    The following image shows the output after the user is authorized using their Google account.

Permalink

Blazor applications are component-based. Blazor components can be used in existing ASP.NET MVC applications. Follow these steps to learn how Blazor components are used in the view page of an MVC application.

  1. Prerequisites:
    • Visual Studio 2019
    • .NET Core 3.1
  2. Create a ASP.NET MVC application.

    Open Visual Studio 2019 and select ASP.NET Core Web App (Model-View-Controller) in the Create a new project page and configure the project as shown.

  3. Add the reference Microsoft.AspNetCore.Components in your dependencies.

  4. Add the Blazor component folder in the View/Shared folder.

    Then add a Razor component inside the Component folder.

  5. Add the following code to the created Component.razor file.

    @using Microsoft.AspNetCore.Components
        <h3>Blazor Component in MVC</h3>
        <button @onclick="Test" class="btn btn-dark">Click to get answer</button>
        <br />
        <div >@Data </div>
    @code {
        [Parameter]  
        public string Data { get; set; } = string.Empty;
           private void Test()
        {
            Data = "Button Clicked";
        }

  6. Add the script reference to the _Layout.cshtml file.

    <base href="~/" />
    <script src="_framework/blazor.server.js"></script>

  7. Create an _Imports.razor file in the Component folder and add the following namespaces to the _Imports.razor file to access component features over your components in the application.

    @using System.Net.Http
    @using Microsoft.AspNetCore.Authorization
    @using Microsoft.AspNetCore.Components.Authorization
    @using Microsoft.AspNetCore.Components.Forms
    @using Microsoft.AspNetCore.Components.Routing
    @using Microsoft.AspNetCore.Components.Web
    @using Microsoft.JSInterop
    @using System.IO
  8. Add services.AddServerSideBlazor() under the ConfigureServices method and add endpoints.MapBlazorHub(); under the Configure method in the Startup.cs file.

    ………………… . .
    namespace blazinmvc
    {
        public class Startup
        {
            ……………… . .
           
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddServerSideBlazor();
                services.AddControllersWithViews();
            }
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {            …………….. . .
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllerRoute(
                        name: "default",
                        pattern: "{controller=Home}/{action=Index}/{id?}");
                    endpoints.MapBlazorHub();
                });
            }
        }
    }

  9. To render components on the View page, add the following code in the Index.cshtml page in the Views folder.

    @{
        ViewData["Title"] = "Home Page";
    }
    <div >
    @(await Html.RenderComponentAsync<blazinmvc.Views.Shared.Component.Component>(RenderMode.ServerPrerendered,new {  Data= " Hello World " }))
    </div>

  10. Run the application.

    After button click

  11. In the component page, we use the button to change the text defined in the Index.cshtml page inside the component rendering. Clicking the button will change the text shown on the Home page.

View Sample in GitHub

Permalink

Since there is no built-in functionality for saving files in Blazor, it is necessary to write the function in JavaScript and then invoke the JavaScript function from C# code in Blazor. In this solution, we have used JavaScript interop for file saving and generation in client-side Blazor.

  1. Create a new JavaScript file, saveFile.js, in the wwwroot folder and add the following code.
    [saveFile.js]

    function saveFile(file, Content) {
    var link = document.createElement('a');
    link.download = name;
    link.href = "data:text/plain;charset=utf-8," + encodeURIComponent(Content)
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    }

  2. Reference the new script file in the index.html page as shown.
    [index.html]

    <body>
        ………………………… . .
        <script src="_framework/blazor.webassembly.js"></script>
        <script src="saveFile.js"></script>
    </body>

  3. Invoke the JavaScript function in a new Razor page.
    [Savefile.razor]

    page "/savefile"
     
    @inject IJSRuntime JSRuntime
     
    <h1>Blazor Save & Generate File</h1><textarea @bind="fileContent"
    style="width:150px;height:100px" />
     
    <button @onclick="SaveFile">SaveFile</button>
     
    <button @onclick="DownloadFile">GenerateFile</button>
     
    @code {
     
        string Content;
     
        string fileContent;
     
        string fileName = "file.txt";
     
        public  void SaveFile()
        {
           Content = fileContent;
        }
        public async void DownloadFile()
        {
            await JSRuntime.InvokeAsync<object>("saveFile",fileName,Content);
       }
    }

  4. Run the app. Save and generate the file in text format.

    View Sample in GitHub

Permalink

In Blazor, there is no significant difference between using these two attributes. The @bind attribute is a shorthand of the @bind-value attribute and its delegate will trigger the ValueChanged event of the component.
The usage of both the @bind and @bind-value attributes in the following code examples are functionally equivalent.

@bind

<input type="text" @bind="user" />

@bind-value

<input type="text" @bind-value="user" @bind-value:event="onchange"/>
Permalink

We can bind a drop-down list in Blazor WebAssembly using the <select> tag and bind the values to the drop-down list using the @bind attribute in the tag. In the following code example, we have bind a country list along with its country codes. On changing the dropdown list, the codes corresponding to the country will appear below.

[Index.razor]

@page "/"

<p>Select country from the DropDownList</p>

<select class="form-control" @bind="@SelectedCountryID">

        @foreach (var country in CountryList)
        {
            <option value="@country.Code"> @country.Name </option>
        }
        
</select>
<br/>
    <p>@SelectedCountryID</p>

    @code {

        string selectedCountryID;

        string SelectedCountryID
        {
            get => selectedCountryID;
            set {selectedCountryID = value;}
        }

        List<Country> CountryList = new List<Country>() { new Country ("IND", "India"),new Country ("USA", "United States"),new Country ("UK", "United Kingdom")};

        public class Country
        {

            public Country(string code, string name)
            {
                Code = code;
                Name = name;
            }
            public string Code { get; set; }
            public string Name { get; set; }

        }
    }

Note: We also have our Syncfusion Dropdown List component. Please refer to the demo link for more information about our product.

View Sample in GitHub

Permalink
  • Blazor WebAssembly can run client-side C# code directly in the browser.
  • The Blazor application, as well as its dependencies and the .NET runtime, are all downloaded to the browser.
  • The application runs on the browser’s UI thread directly. The same method handles both UI notifications and event management.
  • We can re-use code and libraries from the server-side parts of the application while .NET runs on WebAssembly.
Permalink

Using MVC in a Blazor application is not yet possible, but we have an option to migrate an existing ASP.NET MVC application to a Blazor Server application.

Permalink

Browser history can be accessed in Blazor using the window’s popstate event, which is fired when the user navigates the session history. We use the history API to get the history changes in the browser, and we can manually move to a specific page using back(), forward(), go().
When routing takes place in the application or browser, you can use the following code to store the current location:

Window.history.pushState({ prevUrl: window.location.href }, null, newpath)

To retrieve the previous URL, use the following code in your new route:

Window.history.state.prevUrl

Refer to this link for more information about the popstate event.
Refer to this link for more information about the history API.

View Sample in GitHub

Permalink

To get the page referrer of where the user came from, follow these steps:

  1. Create a JavaScript function as a separate file in your application (~/wwwroot/interop.js).

    function getPageReferrer()
     {
        return document.referrer;
    }

  2. Add the script reference in the ~/wwwroot/index.html file of the Blazor WebAssembly application.

     <script src="interop.js"></script> 

  3. Invoke the JavaScript function using the IJSRuntime service in a Razor page.

     @page "/"
    @inject IJSRuntime JSRuntime
    @code {
    protected override async Task OnInitializedAsync()
    {
    var pageReferrer = await JSRuntime.InvokeAsync("getPageReferrer");
    }
    }


    View Sample in GitHub

Permalink

Unit testing is a level of software testing that is used by developers to ensure that the implemented logic meets the requirements and behaves as expected. The following example uses the bUnit testing library for Blazor components to write comprehensive and stable unit test cases. It allows you to write test cases in C# or Razor syntax to verify outcomes with semantic HTML diffing and comparison logic.

Create a simple Blazor Server/WebAssembly application.
In Visual Studio 2019, right-click on the project solution and select the Add > New Project option.


 Add-New-project-in-VS-2019
Select NUnit Test Project (.NET Core) and click Next button.

NUnit Test Project
Enter the project name and then click Create button. The created project will be displayed like the following.

Created Project
Open the NuGet manager for BlazorTest and search with the keyword bunit.web. The result will be displayed as seen in the following screenshot. Select bunit.web and install it.

Nuget Manager Window
Right-click on BlazorTest project dependencies and add the project reference BlazorServerApp.
Now, write a test case for a Razor component.
Add a Razor component to your Blazor project.

[Index.razor]

 @page "/"
 
<h3>Hello World</h3>
 
<button class="btn btn-primary" @onclick="OnButtonClick">Ping Me</button>
 
@if (IsVisible)
{
    <h5>Blazor component is clicked.</h5>
}
 
@code {
 
    public bool IsVisible { get; set; }
    protected void OnButtonClick()
    {
        IsVisible = true;
    }
}
Add the following test cases in your created test project file [UnitTest1.cs] to test the Blazor components.

[UnitTest1.cs]

using NUnit.Framework;
using Bunit;
 
namespace TestProject1
{
    public class Tests
    {
        [SetUp]
        public void Setup()
        {
        }
 
        [Test]
        public void Test1()
        {
            // Arrange
            using var context = new Bunit.TestContext();
 
            // Act
            var cut = context.RenderComponent<BlazorServerApp.Pages.Index>();
            cut.Find("button").Click();
 
            // Assert
            cut.Find("h5").MarkupMatches("<h5>Blazor component is clicked.</h5>");
        }
    }
}
Then select the View > Test Explorer options in Visual Studio 2019 and run the test.
Now you will see the list of test methods. The test run is successful, and the test case passes. You can also alter the test case if it fails.

Refer to “Unit test a Blazor component” for more details and download the sample here.

Permalink

Blazor prerendering is a process where web page elements are built and compiled on the server and static HTML is hosted in WebAssembly (client side). When Razor pages load, components are prerendered at the same time. This process improves page loading by making it faster for SPAs (single page applications), which improves search engine optimization.

To set up prerendering for a hosted Blazor WebAssembly app:


  1. Create the Blazor web Assembly App with ASP.net core Hosted. Here solution name is BlazorHosted.

  2. Delete wwwroot/index.html file from the Blazor WebAssembly Client project.

  3. In the Client project, delete the following lines in Program.cs:

    - builder.RootComponents.Add<App>("#app");
    - builder.RootComponents.Add<HeadOutlet>("head::after");

  4. Add the _Host.cshtml and _Layout.cshtml files to the Pages folder in the Server project. These files are already included in the Blazor Server template and can be obtained from there.
    Make the following changes to the _Host.cshtml file:

    • Modify the namespace based on APP namespace as BlazorHosted.Client.
    • Update the render-mode as ‘WebAssemblyPrerendered’ in host.cshtml
    <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
  5. Make the following changes to the _Layout.cshtml file:
    Update the Pages namespace at the top of the file to match namespace of the Server app’s pages like this,
    @namespace BlazorHosted.Server.Pages

    Add @using directive for the Client project at the top of the file:
    @using BlazorHosted.Client

    Update the stylesheet links to point to the WebAssembly project’s stylesheets. So, make the following changes.
    Remove the following lines
    <link href="css/site.css" rel="stylesheet" />
    <link href="{App Name space}.styles.css" rel="stylesheet" />
    <component type="typeof(App)" render-mode="ServerPrerendered" />
    Add the following lines
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />
    <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
    Modify the Blazor script source of Server side Blazor:
    <script src="_framework/blazor.webassembly.js"></script>
  6. In the endpoint mapping of the Server project in Program.cs, update the fallback from the index.html file to the _Host.cshtml page.
    Remove the following line
    app.MapFallbackToFile("index.html");
    Add the following line
    app.MapFallbackToPage("/_Host");
  7. If the Client and Server projects use one or more common services during prerendering, factor the service registrations into a method that can be called from both projects. For more information, see ASP.NET Core Blazor dependency injection.
  8. Run the Server project. The hosted Blazor WebAssembly app is prerendered by the Server project for clients.

View Sample in GitHub

Permalink

In the following example, the cookie consent banner temple will display to notify you to accept cookies. Follow the below steps to create a consent cookie in Blazor.

1. Configure the HttpContextAccessor and CookiePolicyOptions to the Program.cs file to create a consent cookie.

[Program.cs] 

builder.Services.Configure<CookiePolicyOptions>(options => 
{ 
    options.CheckConsentNeeded = context => true; 
    options.MinimumSameSitePolicy = SameSiteMode.None; 
}); 
builder.Services.AddHttpContextAccessor(); 
var app = builder.Build(); 
app.UseCookiePolicy();

2. Now, add the Cookie consent banner template as a Razor component under the Shared folder.

[ConsentCookie.razor]

@using Microsoft.AspNetCore.Http.Features
@using Microsoft.AspNetCore.Http

@inject IHttpContextAccessor Http
@inject IJSRuntime JSRuntime


@if (showBanner)
{
    <div id="cookieConsent" class="alert alert-info alert-dismissible fade show" role="alert">
        Consent to set cookies.
        <button type="button" class="accept-policy close" data-dismiss="alert" aria-label="Close" data-cookie-string="@cookieString" @onclick="AcceptMessage">
            Accept Cookie
        </button>
    </div>
}
@code {
    ITrackingConsentFeature consentFeature;
    bool showBanner;
    string cookieString;

    protected override void OnInitialized()
    {
        consentFeature = Http.HttpContext.Features.Get<ITrackingConsentFeature>();
        showBanner = !consentFeature?.CanTrack ?? false;
        cookieString = consentFeature?.CreateConsentCookie();
    }

    private void AcceptMessage()
    {
        // JsInterop call to store the consent cookies.
        JSRuntime.InvokeVoidAsync("CookieFunction.acceptMessage", cookieString);
    }
}

3. Add the JavaScript function in the _Layout.cshtml/_Host.cshtml/index.cshtml file to store the cookie.

[_Layout.cshtml]/_Host.cshtml/index.cshtml 

<body>
      . . .
      . . .

      <script>
        window.CookieFunction = {
            acceptMessage: function (cookieString) {
                document.cookie = cookieString;
            }
        };    
   </script>
</body>

4. Refer to the cookie consent banner template Razor component in the MainLayout.razor file.

[MainLayout.razor]

<main> 
	<div class="top-row px-4"> 
		<ConsentCookie /> 
    </div>   
	<article class="content px-4"> 
		@Body 
    </article> 
</main>

5. Run the application, and you will find the consent cookie banner.

6. Now, click the Accept cookie button to store the cookie in the browser.

View Sample in GitHub

Permalink

Blazor doesn’t manipulate the DOM directly at C# side. You can call the JavaScript method by using JavaScript Interop to get an element by ID or class. 

  • The getElementsByClassName() method returns a collection of all elements in the document with the specified class names.
  • The getElementById() method returns a collection of all elements in the document with the specified ID names.

[Index.razor]

@page "/"
@inject IJSRuntime JsRuntime

<h1 id="headingElement">Hello, world!</h1>

<p class="para-element">Welcome to your new app.</p>

@code {
    protected override async void OnAfterRender(bool firstRender)
    {
        await JsRuntime.InvokeVoidAsync("elementId");
    }
}

[_Host.cshtml]

<body>
      . . .
      . . .

      <script>
        function elementId() {
            // Get element with the specified ID name
            var idValue = document.getElementById("headingElement");
            console.log(idValue.innerHTML);
            // Get element with the specified Class name
            var classValue = document.getElementsByClassName("para-element");
            console.log(classValue[0].innerHTML);
        }
    </script>
</body>

Refer to “Call JavaScript functions from .NET methods in ASP.NET Core Blazor” for more details. 

Permalink

Configure the logger in the builder in the Program.Main configuration file. Add a package reference for Microsoft.Extensions.Logging.Configuration to the Blazor app’s project file using the NuGet package manager.

Now, add the namespace as Microsoft.Extensions.Logging to Program.cs and add the builder logging configuration. This configuration will prevent the logging info in the browser console.

[Program.cs]

using Microsoft.Extensions.Logging;
. . .
. . .
public class Program
    {
        public static async Task Main(string[] args)
        {
            . . .
            . . .
         Builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
         
        }
    }

Refer to “Logging configuration” for more details.

Permalink

In Blazor, you can call a JavaScript method using JavaScript interop to scroll the page to the top position.

In the following code, the button event triggers when the button is clicked and scrolls to the page top.

[Index.razor]

@page "/"
@inject IJSRuntime JsRuntime

<div style="background-color:lightgrey;padding:60px 60px 1000px">
    <h2>Blazor App</h2>
    This example demonstrates how to scroll to the top based on the button click event.<br />
    Scroll down and click the button; it scrolls to the top position.
</div>
<div><br />
<button @onclick="OnButtonClick" class="btn btn-primary">Click Button</button>
</div>

@code {

    private async void OnButtonClick()
    {
        await JsRuntime.InvokeVoidAsync("OnScrollEvent");
    }
}

[_Host.cshtml]

<body>
. . .
. . .
<script>
        // When the user clicks the button, the page scrolls to the top
        function OnScrollEvent() {
            document.documentElement.scrollTop = 0;
        }
    </script>
</body>

Permalink

Follow the below steps to convert an existing Blazor Server project into a Blazor WebAssembly project.

Prerequisites:

  • .NET Core 3.1/.NET 5.0
  • Visual Studio 2019

1. Open the existing Blazor Server app project file (.csproj) and add the Blazor WebAssembly SDK and package references.

[BlazorServerApp.csproj]

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
   . . .
   . . .

   <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.4" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.4" PrivateAssets="all" />
    <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
  </ItemGroup>

</Project>

2. Delete the Startup.cs file configuration in the existing Blazor Server app and add the required WebAssembly configurations to the Program.cs file. Add the following code for WebAssembly configuration.

[Program.cs]

using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace BlazorServerApp
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

            await builder.Build().RunAsync();
        }
    }
}

3. Now add the below namespace to your converting application in the _Import.razor file.

[_Import.razor]

@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.WebAssembly.Http

4. Delete the _Host.cshtml, Error.cshtml, and Error.cshtm.cs files under the Pages folder and create an index.html file and add the below code snippet under wwwroot/index.html file.

[index.html]

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>{{Your_Application_Name}}</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="{{Your_Application_Name}}.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

5. Now, run the application. The existing Blazor Server project is now converted into a Blazor WebAssembly project.

Permalink

In Blazor, you can call a JavaScript method using JavaScript interop to copy the input element text to the clipboard.

In the following code, the text entered into the text box will be copied by clicking the button, and it will be displayed in the alert box.

[Index.razor]

@page "/"
@inject IJSRuntime JSRuntime

<div class="form-inline">
    <input id="form-control" type="text" value="Hello World" />
    <button type="button" class="btn btn-primary" @onclick="CopyTextToClipboard">Copy</button>
</div>

@code {
    private async Task CopyTextToClipboard()
    {
        await JSRuntime.InvokeVoidAsync("copyClipboard");
    }
}

[_Host.cshtml]

<body>
. . .
.    .   .
<script>
        function copyClipboard() {
            /* Get the text field */
            var copyText = document.getElementById("form-control");
            /* Select the text field */
            copyText.select();
            /* Copy the text inside the text field */
            document.execCommand("copy");
            /* Alert the copied text */
            alert("Copied the text: " + copyText.value);
        }
    </script>
</body>
Permalink

As opposed to how session cookies work, when opening a page in a new tab or window, a new session occurs with the browsing context. To access the browser sessionStorage in Blazor apps, write custom

code or use a third-party package. The accessed data can be stored in localStorage and sessionStorage. Know that localStorage is scoped to the user’s browser. If the user reloads the page or closes and reopens the browser, the state persists. Session storage is similar to local storage, but data in the session storage will be cleared after the session.

Install the Blazored.SessionStorage Nuget package in the NuGet package manager to store the session data in Blazor. Add the Blazored SessionStorage configuration to the WebAssembly app.

[Program.cs]

using Blazored.SessionStorage; 

. . . 
. . . 

            . . . 
            . . . 

            builder.Services.AddBlazoredSessionStorage(); 

[Index.razor]

@page "/" 

@inject Blazored.SessionStorage.ISessionStorageService sessionStorage 

<h2>@Name</h2> 

<button class="btn btn-primary" @onclick="Clear">Clear Session</button> 

@code { 
    public string Name; 
    protected override async Task OnInitializedAsync () 
    { 
        await sessionStorage.SetItemAsync("ID", "20019"); 
        await sessionStorage.SetItemAsync("Name", "John Smith"); 
        Name = "ID: " + await sessionStorage.GetItemAsync<string>("ID") + "Name : " + await sessionStorage.GetItemAsync<string>("Name"); 
    } 
    public async void Clear () 
    { 
        //this will clear the session data 
        await sessionStorage.ClearAsync(); 
    } 
} 
Permalink

RenderFragment is a delegate that renders a UI segment. So, it does not have an Empty state. It can be null, which is equivalent to empty in the sense that it will produce no rendered output. In the following example, see that the RenderFragments are in a null state or contain a value.

@if (childContent != null) 
{ 
    <p>@childContent</p> 
} 
else 
{ 
    <p>RenderFragment is null</p> 
} 
  
<button class="btn btn-primary" @onclick="OnButtonClick">Click</button> @*RenderFragment is null by default, click the button to update the RenderFragment*@ 
  
@code { 
    private string textContent = "Welcome to your new Blazor app."; 
    private RenderFragment childContent { get; set; } 
     
    private void OnButtonClick() 
    { 
        childContent = BuildRenderTree => BuildRenderTree.AddContent(1, textContent); 
    } 
} 
Permalink

An <iframe> tag is known as an inline frame or frame within a frame. It’s an HTML element that allows documents to be embedded within the current page. The following example illustrates how to embed a URL inside an iframe in the Blazor Web App and Blazor WebAssembly Standalone App targeting .NET 8.0.

[Home.razor]

@page "/"

<iframe width="560" height="315" src="https://blazor.net" frameborder="0" allowfullscreen></iframe>

Blazor Web App – View Sample in GitHub

Blazor WebAssembly Standalone App – View Sample in GitHub

Permalink

Blazor virtualization is a technique for limiting UI rendering to just the parts that are currently visible. It improves the perceived performance of component rendering using the Blazor framework’s built-in virtualization support with the virtualize component. When you are using a long list-item component with a loop, you can use virtualization for better performance.

Use the Virtualize component when:

  • A set of data items in a loop needs to be rendered.
  • Many items can’t be seen when scrolling.
  • Items to be rendered are equal in size.

Prerequisites:

  • .NET 5.0 and above.
  • Visual Studio 2019 Preview 3 (v16.8) or greater.

Follow the below example to achieve this.

[Index.razor]

@page "/"

<Virtualize Items="employees" Context="employee">
    <tr>
        <td>@employee.EmployeeId</td>
        <td>@employee.Salary</td>
    </tr>
</Virtualize>

@code {

    private List<Employee> employees;

    protected override async Task OnInitializedAsync()
    {
        employees = await EmployeeDetails();
    }

    private async Task<List<Employee>> EmployeeDetails()
    {
        List<Employee> empList = new List<Employee>();

        for (int i = 1; i <= 30; i++)
        {
            var emp = new Employee()
            {
                EmployeeId = $"EmpID - {i}",
                Salary = $"Salary - {i * 100}",
            };

            empList.Add(emp);
        }
        return await Task.FromResult(empList);
    }

    public class Employee
    {
        public string EmployeeId { get; set; }
        public string Salary { get; set; }
    }
}

Refer to “ASP.NET Core Blazor component virtualization” for more details.

View Sample in GitHub

Permalink

You can read static files by creating HttpClient Get calls. The GetStringAsync method sends a request to the specific URI and returns the response body as a string in an async operation. 

@inject HttpClient Http

@code {

    protected override async Task OnInitializedAsync()
    {
        var content = await Http.GetStringAsync(request Uri);

    }
}

If you can read local file, you will use ReadAsync() and  Encoding.UTF8.GetString() methods. Here is an example:

@page "/read localfile" 
<InputFile OnChange="@HandleFileSelection" /> 
<button @onclick="ReadFile">Read File</button> 
@if (!string.IsNullOrEmpty(fileContent)) 
{ 
    <div>@fileContent</div> 
} 

@code { 
    private IBrowserFile? file; 
    private string? fileContent; 
    private void HandleFileSelection(InputFileChangeEventArgs e) 
    { 
        file = e.File; 
    } 
    private async Task ReadFile() 
    { 
        if (file != null) 
        { 
            var stream = file.OpenReadStream(); 
            var buffer = new byte[file.Size]; 
            await stream.ReadAsync(buffer, 0, (int)file.Size); 
            fileContent = System.Text.Encoding.UTF8.GetString(buffer); 
        } 
    } 
} 

Note: The project mentioned above is designed for reading local text format documents. 

View Sample in GitHub  

Permalink

RenderFragment is used to render components or content at run time in Blazor. The RenderFragment class allows you to create the required content or component in a dynamic manner at runtime. In the following code example, the content is created at runtime on OnInitialized.

[Index.razor]

@page "/"

@childContent

@code {
    private string textContent = "Welcome to your new Blazor app.";
    private RenderFragment childContent { get; set; }

    private RenderFragment AddContent() => builder =>
    {
        builder.AddContent(1, textContent);
    };

    protected override void OnInitialized()
    {
        childContent = AddContent();
    }
}

Refer to ASP.NET Core Blazor templated components” for more details.

Permalink

To localize the text in a Blazor WebAssembly (client-side) app, follow these steps:

1. Create a Blazor WebAssembly project and add the Microsoft.Extensions.Localization NuGet package using NuGet Package Manager.

2. Add the culture resource files in the Shared/ResourceFiles folder and add a drop-down menu to switch between localized text.

[CultureDropDown.razor]

@using  System.Globalization
@inject IJSRuntime JSRuntime
@inject NavigationManager UrlHelpers

<strong>Culture:</strong>
<select class="form.control" @bind="Culture" style="width: 140px; margin-left: 10px;">
    @foreach (var culture in supportedCultures)
    {
        <option value="@culture">@culture.DisplayName</option>
    }
</select>

@code
{
    CultureInfo[] supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es-ES"),
        new CultureInfo("de")
    };

    CultureInfo Culture
    {
        get => CultureInfo.CurrentCulture;
        set
        {
            if (CultureInfo.CurrentCulture != value)
            {
                var jsInvoke = (IJSInProcessRuntime)JSRuntime;
                jsInvoke.InvokeVoid("blazorCulture.set", value.Name);

                UrlHelpers.NavigateTo(UrlHelpers.Uri, true);
            }
        }
    }
}

[MainLayout.razor]

<div class="top-row px-4">
      <CultureDropDown />
      . . .
 </div>

3. Now add the localization configuration and get the current locale’s culture using JSInterop, which is stored in browser window’s local storage.

[Program.cs]

…
using Microsoft.JSInterop;
using System.Threading.Tasks;

namespace BlazorLocaleWASM
{
  public class Program
  {
     public static async Task Main(string[] args)
     {
        …
        …
                builder.Services.AddLocalization();
                var host = builder.Build();
        var jsInterop = host.Services.GetRequiredService<IJSRuntime>();
        var result = await jsInterop.InvokeAsync<string>("blazorCulture.get");
        if (result != null)
        {
          var culture = new CultureInfo(result);
          CultureInfo.DefaultThreadCurrentCulture = culture;
          CultureInfo.DefaultThreadCurrentUICulture = culture;
        }

        await host.RunAsync();
    }
  }
}

[index.html]

<html>

<head>
    . . .
    . . .
</head>

<body>
  ...
     . . .
<script>
        window.blazorCulture = {
            get: () => window.localStorage['BlazorCulture'],
            set: (value) => window.localStorage['BlazorCulture'] = value
        };
    </script>
</body>

</html>

4. Add the text/content for localization in the Index.razor file.

[Index.Razor]

@page "/"
@using BlazorLocaleWASM.Shared.ResourceFiles
@inject Microsoft.Extensions.Localization.IStringLocalizer<Resource> Locale

<h1>@Locale["Hello world"]</h1>

@Locale["Welcome to your new app"]

<SurveyPrompt Title="How is Blazor working for you?" />

5. By default, Blazor WebAssembly carries minimal globalization resources required to display values, such as dates and currency, in the user’s culture. Applications that must support dynamically changing the culture should configure BlazorWebAssemblyLoadAllGlobalizationData in the project file.

[Project file]

<PropertyGroup>        <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>

6. Run the application.

You can download the reference sample here.

Please refer to this link for more information.

Permalink

Follow these steps to create a Blazor WebAssembly (client-side) application using a command-line interface (CLI).

Create a Blazor WebAssembly App in CLI

Open a command prompt where you want your project to be located and run the following command.

dotnet new blazorwasm -o BlazorWebAssemblyApp

Navigate to Blazor WebAssembly App

Run the cd BlazorWebAssemblyApp command to navigate to the BlazorWebAssemblyApp folder.

Run the application

The dotnet run command runs the application. See the following output in the default browser.

Note: If you have installed multiple SDK versions and need a specific framework version (e.g., net5.0, netcoreapp3.1) project, then add the -f flag to the dotnet new blazorwasm command. Refer here for the available flag options.

View Sample in GitHub

Permalink

Follow these steps to create a Blazor Server application using a command-line interface (CLI). 

Create a Blazor Server App in CLI 

Open a command prompt where you want your project to be located and run the following command. 

.NET CLI 

dotnet new blazorserver -o BlazorServerApp 

Navigate to Blazor Server App 

Run the cd BlazorServerApp command to navigate to the BlazorServerApp folder. 

Run the apllication. 

The dotnet run command runs the application. See the following output in the default browser. 

Note: If you have installed multiple SDK versions and need a specific framework version (e.g., .net7.0) project, then add the -f or –framework flag to the dotnet new blazorserver command. 

.NET CLI 

dotnet new blazorserver -o BlazorServerApp –framework net6.0 

Refer to this documentation for more details. 

View Sample in GitHub 

Permalink

Follow the step-by-step procedure below to create a Blazor Server Application in Visual Studio 2019.

Download and install Visual Studio 2019

Download and install the latest version of Visual Studio 2019 with the ASP.NET and web development workload.

Create a new project

Open Visual Studio 2019 and click Create a new project.

Select Blazor app from the template

Select Blazor App from the template list and click the Next button.

blazor template

Configuring the project

The project configuration window will pop up. Click the Create button to create a new project with the default project configuration.
Blazor server configure

Choose Blazor Server App

Select a target framework based on your requirement, choose the Blazor Server App from the dashboard, and then click Create to create a new Blazor Server application.

Blazor server template

Blazor Server App structure

Now the Blazor Server App is created, and the structure look like the following image.
Server structure

Run the application.

Press Ctrl + F5 to run the application and find the output in default browser.
server output

Refer to this link for more details.

Permalink

Follow the step-by-step procedure below to create a Blazor WebAssembly (client-side) application in Visual Studio 2019.

Download and install Visual Studio 2019

Download and install the latest version of Visual Studio 2019 with the ASP.NET and web development workload.

Create a new project

Open Visual Studio 2019 and select Create a new project.

Select Blazor app from the template
Select Blazor App from the template list and click the Next.

Choose Blazor WebAssembly App
Select a target framework based on your requirements, choose Blazor WebAssembly App from the dashboard, and then click Create to create a new Blazor WebAssembly application.

Blazor WebAssembly App structure
Now the Blazor WebAssemblyApp is created, and the structure look like in the below image.

Run the application.
Press ctrl + F5 to run the application and see the below output in default browser.

Refer to this link for more details.

Permalink

Data annotation localization in Blazor server-side and WebAssembly (client-side) apps is achieved by referencing the Resource class localized string. Follow these steps to achieve this:

  1. Add the required localized error messages to the Resource file in the Resource folder.
  2. In the validation attribute, set the ErrorMessageResourceType as typeof(Resource).
  3. Assign the required validation message to the ErrorMessageResourceName property.

[Index.razor]

@page "/"
@using {{ApplicationName}}.Resource
@using System.ComponentModel.DataAnnotations;

<EditForm Model="@_employee" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <InputText id="name" @bind-Value="_employee.Name" />
    <button type="submit">Submit</button>
</EditForm>

@code {
    public class Employee
    {
        [Required(ErrorMessageResourceName = "RequiredError", ErrorMessageResourceType = typeof(Resource))]
        [StringLength(5, ErrorMessageResourceName = "LengthError", ErrorMessageResourceType = typeof(Resource))]
        public string Name { get; set; }
    }

    private Employee _employee = new Employee();

    private void HandleValidSubmit()
    {
        Console.WriteLine("OnValidSubmit");
    }
}
Permalink

You can add smooth scrolling to a Blazor app using a combination of Blazor components and JavaScript interop, similar to the functionality available in jQuery UI components.  

Step 1: Create a New Blazor App Run the following command to create a new Blazor WebAssembly app:  

.NET CLI  

dotnet new blazorserver -n SmoothScrollDemo 

Step 2: Add CSS Styles Open the wwwroot/css/site.css file and add the following CSS class for smooth scrolling: 

[wwwroot/css/site.css] 
 

.smooth-scroll { 

    scroll-behavior: smooth; 

} 

Step 3: Create a SmoothScroll Component Create a new Blazor component named SmoothScroll.razor

 [SmoothScroll.razor]

<!-- Pages/SmoothScroll.razor --> 
@page "/Smoothscroll" 
@inject IJSRuntime JSRuntime 

<h3>Smooth Scrolling Example</h3>  

<div class="smooth-scroll"> 
    <p> 
        This is a demonstration of smooth scrolling in a Blazor app. 
        Click the button below to smoothly scroll to the bottom of the page. 
    </p> 
    <button @onclick="ScrollToBottom">Scroll to Bottom</button> 
</div> 
<div class="content"> 
    <!-- Add some content here to make the page longer --> 
    <h4>Smooth Scrolling in Blazor: Elevating User Experience with Elegance</h4> 
    <p>In the realm of modern web development, the user experience reigns supreme, and every nuance plays a pivotal role in crafting an immersive and delightful interaction. One such understated yet highly impactful element is the art of smooth scrolling. Blazor, the cutting-edge web framework by Microsoft, empowers developers with the ability to seamlessly integrate this feature into HTML elements. By adding an enchanting fluidity to the scrolling mechanism, Blazor transforms the user journey into an elegant dance, enhancing engagement and delivering a heightened sense of sophistication.</p> 
    <h4>The Essence of Smooth Scrolling</h4> 
    <p>Smooth scrolling, at its core, is the art of imbuing scrolling actions with an exquisite gracefulness. Unlike the conventional abrupt and jarring movements that characterize traditional scrolling, smooth scrolling introduces a harmonious glide that seamlessly navigates users through various sections or elements on a web page. This simple yet transformative touch has the power to transcend the mundane, transforming user interactions into a symphony of motion and elegance.</p> 
    <h4>Unveiling the Benefits</h4>  
    <ol> 
        <li> 
            <h6>Enhanced User Engagement:</h6> 
            By eliminating the disorienting jumps, smooth scrolling creates a captivating rhythm that keeps users immersed and engaged as they explore the content. 
        </li> 
        <li> 
            <h6>Visual Fluidity:</h6> 
             The graceful transitions between sections establish a visual continuity that feels natural and intuitive, amplifying the overall aesthetics of the website. 
        </li> 
        <li> 
            <h6>Improved Readability:</h6> 
             For lengthy articles or content-rich pages, smooth scrolling guarantees a comfortable reading experience by gradually revealing the content. 
        </li> 
        <li> 
            <h6>  Subtle Sophistication: </h6> 
          Smooth scrolling lends an air of refinement to the user journey, elevating the perception of the website's design and enhancing its overall brand image. 
        </li> 
        <li> 
            <h6>Extended Time on Page:</h6> 
             The fluidity and beauty of smooth scrolling entice users to linger, thereby increasing their time spent on the website and fostering a deeper connection. 
        </li> 
    </ol> 
</div> 

@code { 
    private async Task ScrollToBottom() 
    { 
        await JSRuntime.InvokeVoidAsync("scrollToBottom"); 
    } 
} 

Step 4: Add the following JavaScript function for scrolling to the bottom of the page in _Host.cshtml. 

[_Host.cshtml]

<body> 
……. 
    <script src="_framework/blazor.server.js"></script> 
    <script> 
        window.scrollToBottom = function () { 
            window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); 
        }; 
    </script> 
</body> 

Step 5: Test the Smooth Scrolling Effect Run the Blazor app using the following command: 
NET CLI

dotnet run 

View Sample in GitHub  

Permalink

DateTime.Now.ToShortDateString() and DateTime.Now.ToLongDateString() are methods that return the current culture date format. The string returned by the ToShortDateString and ToLongDateString methods are culture-sensitive. It reflects the pattern defined by the current culture’s date format. You can get the current culture date format by using either of the following methods.

<h2>@DateTime.Now.ToShortDateString()</h2>

or

<h2>@DateTime.Now.ToLongDateString()</h2>
Permalink

You can customize the loading text in a Blazor WebAssembly application by editing the tag for target framework version 3.* or the
tag for target framework version 5.* in the index.html page. In the following example, I have customized the loading text while loading.

[index.html] for target framework version 3.*

<body>
    <app>
        <div style="position:absolute; top:30vh; width:100%; text-align:center">
            <h1> Blazor WASM Application</h1>
            <p>The application is loading...</p>
        </div>
    </app>
    . . .
    . . .
</body>

[index.html] for target framework version 5.*

<body>
    <div id="app">
        <div style="position:absolute; top:30vh; width:100%; text-align:center">
            <h1> Blazor WASM Application </h1>
            <p>The application is loading...</p>
        </div>
    </div>
    . . .
    . . .
</body>

Refer to this link for more details.

View Sample in GitHub

Permalink

In the following example, LoadingScreen will display a loading message while data is loaded asynchronously. Once the data has been loaded, it replaces the loading message with the actual content.

[LoadingScreen.razor]

@if (contentLoaded)
{
    @ChildContent
}
else
{
     <div style="position:absolute; top:30vh; width:100%; text-align:center">
         <h3>Blazor Server Application</h3>
         <p>The application is loading...</p>
     </div>
}

@code {

    bool contentLoaded;

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await Task.Delay(4000);
        contentLoaded = true;
    }
}

Wrap the Router in the LoadingScreen to show the loading screen in the Blazor app.

[App.razor]

<LoadingScreen>
       <Router . . .
                . . .
                . . .
        </Router>
</LoadingScreen>

Please refer to this link for more details.

View Sample in GitHub

Permalink

Languages are changed by using localization in the Blazor server-side application. In the following example, the languages are changed from English to French and vice versa by clicking a button. 

[Program.cs] 

builder.Services.AddLocalization(); 

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); 
… 

var localizeOptions = new RequestLocalizationOptions() 

                .SetDefaultCulture("en-US") 

                .AddSupportedCultures("en-US", "fr-FR") 

                .AddSupportedUICultures("en-US", "fr-FR"); 

app.UseRequestLocalization(localizeOptions); 

app.MapControllers(); 
… 

[Culture.cs]

. . .
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
namespace BlazorAppLocale
{
    [Route("/[controller]")]
    [ApiController]
    public class Culture : ControllerBase
    {
        public ActionResult SetCulture()
        {
            IRequestCultureFeature culture = HttpContext.Features.Get<IRequestCultureFeature>();
            Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(
                    new RequestCulture(new string[] { "en-US", "fr-FR" }
                    .Where(option => option != culture.RequestCulture.Culture.Name)
                    .FirstOrDefault())));

            return Redirect("/");
        }
    }
}

Add the culture resource files in the Resources/Pages folder.

[Index.razor]

@page "/"
@inject Microsoft.Extensions.Localization.IStringLocalizer<Resource> locale

<h1>@locale["Hello World"]</h1>

@locale["Welcome to your new app."]

<SurveyPrompt Title="How is Blazor working for you?" />

Add the button for changing the language.

[MainLayout.razor]

@inject NavigationManager UriHelper

<div class="top-row px-4"> 
    <button class="btn btn-secondary" @onclick="OnSelected">Change Language</button>
</div>

@code {

    private void OnSelected()
    {
        UriHelper.NavigateTo("Culture", true);
    }
}

You can download the reference sample here.

Please refer to this link for more information.

Permalink

Blazor has a built-in InputFile component from .NET 5.0 that can be used to upload files to the server. You can check the file size in the OnChange event and upload or prevent uploading the files to the server.

[Index.razor]

@page “/”

<h3>File Upload</h3>

<InputFile OnChange=”OnInputFileChange” />

@if (file?.Size < 1000000)
{
    <p>Name: @file.Name</p>
    <p>Size in bytes: @file.Size</p>
    <p>Last modified date: @file.LastModified.DateTime.ToShortDateString()</p>
    <p>Content type (not always supplied by the browser): @file.ContentType</p>
}
else
{
    <p>File size exceeds more than 1MB</p>
}

@code {
    IBrowserFile file;

    void OnInputFileChange(InputFileChangeEventArgs e)
    {
        file = e.File;
        if (e.File.Size < 1000000)
        {
            // Upload files to the server from here
        }
    }
}

Note: Syncfusion offers a feature-rich, easy-to-use File Upload component for Blazor. You can check it out here.

Permalink

Raw HTML can be rendered in Blazor by using MarkupString. You can define the property and pass it to a parameter by using MarkupString.

[Index.razor]

@page "/"

<button class='btn btn-primary' @onclick="OnClickButtonEvent">Counter</button>

@((MarkupString)MyMarkup)

@code { 
    private int count = 0;
    public string MyMarkup;

    public void OnClickButtonEvent()
    {
        ++count;
        MyMarkup = "<div>Current count: <span style=\"color:red\"><b>" + @count + "</b></span></div>";
    }
}
Permalink

Yes, you can host and deploy the blazor server-side application without using Azure but by using AWS or Firebase. Here you can find the steps for hosting the Blazor application using AWS. 

  • Create the Blazor application 
  • Publish the application using the context menu from the Solution in Solution Explorer in Visual Studio.
  • Click Publish. If there is no error, your application will be published successfully.

For more details reference the links Host the application to AWS and Host the application to Firebase

Permalink

You need to add the Response Caching Middleware to the service collection in the startup class.

public void ConfigureServices(IServiceCollection services)
    {
       services.AddResponseCaching();
    }

Then, you can configure the app to use the middleware with the UseResponseCaching extension method, which adds the middleware to the Startup.Configure

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseResponseCaching();   
 }

Refer to this thread for more information.

Permalink

The AuthorizeView component supports policy-based authorization. You can use the Policy parameter for policy-based authorization.

<AuthorizeView Policy="IsShowContent">
   <p>You can only see the content if enabled the IsShowContent policy.</p>
</AuthorizeView>

Policy based authorization in Asp.Net Core

The policies are registered in the application Startup classes ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthorization(config =>
        {
            config.AddPolicy("IsShowContent", policy => policy.RequireClaim("IsShowContent", "true"));
        });
    }
Permalink

The Blazor framework includes synchronous and asynchronous lifecycle methods. You can load the full page asynchronously using the below methods,

  • OnInitializedAsync – It is invoked when the component is ready to start and has received its initial parameters from its parent in the render tree.
protected override async Task OnInitializedAsync()
{
await ...
}
  • OnParametersSetAsync – It is invoked when the component is initialized and has received its first set of parameters from its parent component.
protected override async Task OnParametersSetAsync()
{
    await ...
}
  • OnAfterRenderAsync – It is invoked after a component has finished rendering. The firstRender is set to true at first time when the component instance is rendered.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

Permalink

A Progressive Web Application is a Single Page Application that uses modern browser APIs, and  performs like a desktop app. It can work offline and load instantly, and independent of network speed.

Find the steps for create PWA with blazor app:

  • Create a project from the PWA template
  • Run the application, after creating the app
  • After it launched in the browser, there we have the option for installing the app (PWA)

  • Once installed, the app appears in its own window without an address bar.

Please refer to the documentation link for more details: https://docs.microsoft.com/en-us/aspnet/core/blazor/progressive-web-app?view=aspnetcore-3.1&tabs=visual-studio#create-a-project-from-the-pwa-template

Permalink

Share with

Couldn't find the FAQs you're looking for?

Please submit your question and answer.