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 WebAssembly application by referring to this link.

  2. Then, create a new Class Library and name it as Shared to handle custom validation using the custom validation attribute.

    Class library

  3. Now, add the Shared project to the existing Blazor WebAssembly application using the following steps.

       
    •   Right click the Blazor WebAssembly application name and select Add à Project Reference.
    •  
    • In the Reference Manager dialog, select the Shared project as shown. Click OK
    • .
      Reference Manager
       
    •   After the Shared project is added, the Blazor WebAssembly application looks like the following
    .
    Shared Project

  4. Install the System.ComponentModel.Annotations NuGet package to perform custom validation in the Shared project.

  5. In this illustration, two form fields, Name and Organization, are declared inside the model Customer class, and the custom validation attribute for the Organization is set. When the user enters an incorrect Organization name, a validation message appears. In this illustration, the Organization name is set to “Microsoft”, and if the user enters a value other than “Microsoft”, the validation message “Invalid customer log-in.” appears.

  6. In the Shared class library, rename the Class1.cs as Customer.cs and create a model Customer class.

     using System.ComponentModel.DataAnnotations;
     
    namespace Shared
    {
        public class Customer
        {
            [Required]
            public string Name { get; set; }
     
            [Required]
            public string Organization { get; set; }
        }
    }

     

  7. Create a new OrganizationValidationAttribute.cs and then the OrganizationValidationAttribute class, which is derived from ValidationAttribute. The ValidationAttribute from System.ComponentModel.Annotations acts as the base class for validation.
    [OrganizationValidationAttribute.cs]

      using System.ComponentModel.DataAnnotations;
     
    namespace Shared
    {
        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 });
            }
        }
    }

    The ValidOrganizationName property can be set from the model Customer 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.

      Shared Solution Explorer

  8. Add the following custom validation attribute to the Organization property.

    [Required]
    [OrganizationValidation(ErrorMessage = "Invalid customer log-in.", ValidOrganizationName = "Microsoft")]
    public string Organization { get; set; }

     

  9. Moving back to the Blazor WebAssembly application, add the following code to the Index.razor page to render a form with validation settings.
    [Index.razor]

           @page "/"
     
    <EditForm Model="_product" OnValidSubmit="Submit" style="width:600px;">
        <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="_product.Name" />
                <ValidationMessage For="@(() => _product.Name)" />
            </div>
        </div>
        <div class="form-group row">
            <label for="organization" class="col-md-2 col-form-label">Organization:</label>
            <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>

     

  10. Create a new Index.razor.cs inside the Pages folder and add the following code for Customer class initialization and form submission.
    [Index.razor.cs]

       using Shared;
    using System;
     
    namespace CustomValidationAttributes.Pages
    {
        public partial class Index
        {
            private Customer _product = new Customer();
            public void Submit() =>
                Console.WriteLine($"{_product.Name}, {_product.Organization}");
        }
    }

    The following outputs will be seen after submitting the form.
    Validation Message Output

    Shared Project Output

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 Student.cs and two model classes – Student and PersonalDetails. Here, add ValidateComplexType belonging to the ObjectGraphDataAnnotationsValidator above the complex type declaration.

    using System.ComponentModel.DataAnnotations;
     
    namespace NestedComplexModel
    {
        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 "/"
     
    <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

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 in Visual Studio 2019 by following this link.

  2. Once the application has been created, in the Startup.cs file, call the AddCors method under ConfigureServices to add the cross-origin resource sharing services to the service collection. To add a new policy to the configuration, call the AddPolicy within the AddCors method.
    [Startup.cs]

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddCors(options =>
        {
            options.AddPolicy("NewPolicy", builder =>
             builder.AllowAnyOrigin()
                          .AllowAnyMethod()
                          .AllowAnyHeader());
        });
    }

  3. Under the Configure method in the Startup.cs file, call the UseCors method and pass the appropriate policy name to add the CORS middleware to the application pipeline.
    [Startup.cs]

    public void   Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        …………………..
        app.UseRouting();
        app.UseCors("NewPolicy");
        app.UseAuthorization();
        …………………..
    }

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

View Sample in GitHub

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 Startup.cs file, call the AddCors method under the ConfigureServices to add the cross-origin resource sharing services to the service collection. To add a default policy to the configuration, call the AddDefaultPolicy within the AddCors method.

[Startup.cs]

public void ConfigureServices(IServiceCollection services)
{
    ………….. . . .
    services.AddCors(options =>
    {
        options.AddDefaultPolicy (builder =>
         builder.AllowAnyOrigin()
                      .AllowAnyMethod()
                      .AllowAnyHeader());
    });
}

Since we are using the default policy, we do not need to include the policy name in the UseCors method, as shown.

[Startup.cs]

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ………….. . . . 
    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 so that it can be identified. In the Startup.cs file, call the AddCors method under ConfigureServices to add the cross-origin resource sharing services to the service collection. To add a user-defined (custom) policy to the configuration, call AddPolicy within the AddCors method.

 [Startup.cs]

public void ConfigureServices(IServiceCollection services)
{
    …………… . . .
    services.AddCors(options =>
    {
        options.AddPolicy("NewPolicy", builder =>
         builder.WithOrigins("https://localhost:8080")
                      .AllowAnyHeader()
                      .WithMethods("GET");
    });
}

Under the Configure method in the Startup.cs file, call the UseCors method and pass the user-defined (custom) policy name to add the CORS middleware to the application pipeline.

[Startup.cs]

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ………….. . . .
    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 C# (.NET) methods from JavaScript functions. 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 and Microsoft.EntityFrameworkCore.SqlServer 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 int Salary { get; set; }
    }

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

    public class ApplicationDbContext : DbContext
    {
       public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
       {}
       public virtual DbSet<EmpClass> DisplayRecords { 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.

    The database is now connected to the Blazor application.

  5. Add the connection strings 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 FromSqlRaw extension method to execute a stored procedure.
    [Empservices.cs]

    using System.Linq;
    using {{Your_App_Name}}.Data;
    using Microsoft.EntityFrameworkCore;
    public class Empservices
        {
            protected readonly ApplicationDbContext _dbcontext;
     
            public Empservices(ApplicationDbContext _db)
            {
                _dbcontext = _db;
            }
     
            public EmpClass[] GetEmpDetails()
            {
                EmpClass[] empobj;
                // Here "exec Employee" is a stored procedure of SQL Server Database.
                empobj = _dbcontext.DisplayRecords.FromSqlRaw("exec Employee").ToArray();
                return empobj;
            }
        }

  7. Add the DbContext configuration to the Startup.cs file.
    [Startup.cs]

    using Microsoft.EntityFrameworkCore;
    using {{Your_App_Name}}.Services;
    public class Startup
    {
      public void ConfigureServices(IServiceCollection services)
       {
          services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Myconnection")));
          services.AddScoped<Empservices>();
       }
       // . . .
    }

  8. Now call the GetEmpDetails() method in the Index.razor file and the table to display the database records.
    [Index.razor]

    @page "/"
    @using {{Your_App_Name}}.Data
    @using {{Your_App_Name}}.Services
    @inherits OwningComponentBase<Empservices>
     
    <table border="1">
        <tr>
            <th>Empid</th>
            <th>Empname</th>
            <th>Salary</th>
        </tr>
        @foreach(var emp in empdetails)
        {
         <tr>
             <td>@emp.Empid</td>
             <td>@emp.Empname</td>
             <td>@emp.Salary</td>
         </tr>
        }
    </table>
     
    @code {
        EmpClass[] empdetails;
        protected override void OnInitialized()
        {
            // Here Service property is inherited from OwningComponentBase.
            empdetails = Service.GetEmpDetails();
        }
    }

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

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.
    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.
  5. Refer to this link for more details.

    View Sample in GitHub

Permalink

To use two-way binding in a Blazor component, add the bind attribute to a field. Two-way data binding pushes the changes to the component, gets the data from the component end, and updates its own data accordingly.

In the following example, we created a Select component and implemented two-way binding to the parent component.

Create a Select component in the Pages folder.

[SelectComponent.razor]

<div>
    <select id="Item" class="form-control-sm" @bind="@ListItem">
        @foreach (var list in SelectList)
        {
            @if (ListItem != null && String.Equals(list, ListItem, StringComparison.OrdinalIgnoreCase))
            {
                <option selected value="@list">@list</option>
            }
            else
            {
                <option value="@list">@list</option>
            }
        }
    </select>
</div>

@code {
    public IEnumerable<string> SelectList = new List<string>()
    {
        "List 1",
        "List 2",
        "List 3",
        "List 4",
        "List 5"
     };

    private string listItem { get; set; }

    [Parameter]
    public string ListItem
    {
        get { return listItem; }
        set {
            if (listItem != value)
            {
                listItem = value;
                if (ListItemChanged.HasDelegate)
                {
                    ListItemChanged.InvokeAsync(value);
                }
            }
        }
    }

    [Parameter]
    public EventCallback<string> ListItemChanged { get; set; }
}

Now define the Select component to in the Index.razor page and implement the two-way binding using the @bind attribute.

[Index.razor]

@page "/"

<div>
    <label>Bind Value</label>
    <input type="text" @bind="@Content" @bind:event="oninput" />
    <p>@Content</p>
</div>

<div>
    <SelectComponent @bind-ListItem="@Content" />
</div>

@code {
    [Parameter]
    public string Content { get; set; } = string.Empty;

}

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 “Blazor” in the password field.
    [CustomValidatorAttribute.cs]

    using System.ComponentModel.DataAnnotations;
     
    namespace {{Your_App_Name}}.Pages
    {
        public class CustomValidationAttribute : ValidationAttribute
        {
            public string ValidPassword { get; set; }
     
            protected override ValidationResult IsValid(object password, ValidationContext validationContext)
            {
                var content = password.ToString().ToLower();
                if (content.Equals(ValidPassword.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 password 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]
            public string UserName { get; set; }
            [Required]
            [CustomValidation(ErrorMessage = "The entered password is wrong ", ValidPassword = "Blazor")]
            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 5.0, 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 NET5_0
        blazorVersion = "Blazor App version is .NET5.0";
#elif NETCOREAPP3_1
        blazorVersion = "Blazor App version is netcoreapp3.1";
#endif
    }
}

Refer to this link for more details.

Permalink

Calling the StateHasChanged() method in Blazor, you can be notified that the state has been changed and re-render the components to push their changes to the browser using SignalR. The following example demonstrates how the Counter component is pushing the increment count data automatically using the Timer function and updating the UI in the browser by calling the StateHasChanged() method in 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 HttpContent to get the user agent and IP address details by default.
Extend the AddHttpContextAccessor() configuration to the ConfigureServices method in the Startup.cs file.

[Startup.cs]

public void ConfigureServices(IServiceCollection services)
{
    // . . .
    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 ServerPrerendered. 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”.

@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))

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.

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 using browser storage in Blazor, we can save and load data within a component. The following example demonstrates that the counter value is updated with a button click and the count value stored in local storage. When you refresh the page, the saved data will load from the browser 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 Reshiru.Blazor.IndexedDB.Framework 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 Blazor.IndexedDB.Framework;
    public class Program
    {
       public static async Task Main(string[] args)
       {
               builder.Services.AddScoped<IIndexedDbFactory, IndexedDbFactory>();
         }
    }

  3. Now add the properties inside the class to store and process data in the Data folder.
    [IndexDb.cs]  

    using Blazor.IndexedDB.Framework;
    using Microsoft.JSInterop;
    using System.ComponentModel.DataAnnotations;
     
    namespace {{Your_App_Name}}.Data
    {
        public class IndexDb : IndexedDb
        {
            public IndexDb (IJSRuntime JSRuntime, string name, int version) : base(JSRuntime, name, version) { }
     
         public IndexedSet<Employee> Employees { get; set; }
        }
     
        public class Employee
        {
            [Key]
            public long 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 Blazor.IndexedDB.Framework
    @using {{Your_App_Name}}.Data

  5. Add the Razor component to add and store the data using IndexedDB in the Index.razor file.
    [Index.razor]

    @page "/"
    @inject IIndexedDbFactory DbFactory
     
    <h1>Employees</h1>
    @if (employ != null)
    {
        <table class="table">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>First name</th>
                    <th>Last name</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var employee in employ)
                {
                    <tr>
                        <td>@employee.Id</td>
                        <td>@employee.FirstName</td>
                        <td>@employee.LastName</td>
                        <td><button @onclick="@(() => DeleteForm(employee))">Delete</button></td>
                    </tr>
                }
            </tbody>
        </table>
    }
     
    <fieldset>
        <legend>Add new person</legend>
        <EditForm Model="@newEmployee" OnValidSubmit="@SaveForm">
            <InputText placeholder="First name" @bind-Value="@newEmployee.FirstName" />
            <InputText placeholder="Last name" @bind-Value="@newEmployee.LastName" />
            <button type="submit">Add</button>
            <p><ValidationSummary /></p>
            <DataAnnotationsValidator />
        </EditForm>
    </fieldset>
     
    @code {
        Employee newEmployee = new Employee();
        List<Employee> employ;
     
        protected async Task OnInitializedAsync()
        {
            using var db = await DbFactory.Create<ExampleDb>();
            employ = db.Employees.ToList();
        }
     
        async Task SaveForm()
        {
            // Add employee details.
        }
     
        async Task DeleteForm(Employee person)
        {
            // Delete the employee.
        }
    }

  6. Refer to this blog for more details.

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="3.2.0" />
      </ItemGroup>
     
      <ItemGroup>
        <ProjectReference Include="..\BlazorWasmApp\BlazorWasmApp.csproj" />
      </ItemGroup>
     
    </Project>

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

    public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           if (env.IsDevelopment())
           {
                        app.UseWebAssemblyDebugging();
           }
        
                 app.UseBlazorFrameworkFiles();
          app.UseEndpoints(endpoints =>
          {
              endpoints.MapFallbackToFile("index.html");
                    });
          }
    }

  4. Add inspectUri in the ASP.NET Core app launchSettings.json file.
    [launchSettings.json]

    "profiles": {
        "IIS Express": {
          "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
                },
     
         "AspNetCoreApp": {
          "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
          }
    }


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;
    . . .
     
    public class Program
     {
        public static async Task Main(string[] args)
        {
                         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.

  12. 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]

@using System.Threading
@implements IDisposable

// . . .
@code {
    private CancellationTokenSource cancellationToken = new CancellationTokenSource();

    protected override async Task OnInitializedAsync()
    {
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json", cancellationToken.Token);
    }

    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 using CSS isolation, you can use CSS files or specific components in your app only and not 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 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

LocationChangedis an event handler that will fire when the navigation location has been changed. The following example uses a JavaScript interop function to alert the user when the navigation location changes.

@inject NavigationManager UriHelper
@inject IJSRuntime JSRuntime

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

    private void DetectNavigation(object sender, LocationChangedEventArgs e)
    {
        JSRuntime.InvokeVoidAsync("alert", "Navigation event 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 new Date().getTimezoneOffset() 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. Add the JavaScript function using JS Interop. Get the current offset time from UTC in minutes using the new Date().getTimezoneOffset() method.
    [_Host.razor]

    <body>
         @*Requires render-server mode as "Server" while initializing the component to execute JavaScript in OnInitializedAsync.*@
         <component type="typeof(App)" render-mode="Server" />
          . . . 
          . . . 
     
       <script>
            function GetTimezoneValue() {
                // Returns the time difference in minutes between UTC time and local time.
                return new Date().getTimezoneOffset();
            }
        </script>
    </body >

  2. Now display the UTC time and calculate the user time by using the offset time difference.
    [Index.razor]

    @page "/"
    @inject IJSRuntime JsRuntime
    <h1>Current DateTime</h1>
     
    <p>Now (UTC): @DateTimeOffset.UtcNow.ToString()</p>
    <p>Now (local): @localTime.ToString()</p>
     
    @code {
        private DateTimeOffset localTime;
        private TimeSpan? userTime;
     
        protected override async Task OnInitializedAsync()
        {
            if (userTime == null)
            {
                int timeDiffer = await JsRuntime.InvokeAsync<int>("GetTimezoneValue");
                userTime = TimeSpan.FromMinutes(-timeDiffer);
            }
     
            // Converting to local time using UTC and local time minute difference.
            localTime = DateTimeOffset.UtcNow.ToOffset(userTime.Value);
        }
    }
    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 call a JavaScript function with parameters using JavaScript Interop.

Syntax:

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

Follow this code to call a JavaScript method with parameters in Blazor WebAssembly

[Index.razor]

@page "/"
@inject IJSRuntime JsRuntime

@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

Use the HttpClient class with the GetFromJsonAsync() method to read a JSON file in Blazor WebAssembly. Follow these steps to read the JSON file.

Create or load a JSON file under the wwwroot folder. For example, here we have created a simple employee.json file and read its values in 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, we 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. Add a new class to [Startup.cs].

    …………….. . 
    public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
                services.AddServerSideBlazor();
                services.AddSingleton<WeatherForecastService>();
                services.AddScoped <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 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 2019
    • .NET Core 3.1
  2. Create a Blazor Server app by clicking the option on the Create a new project page as shown.

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

  3. 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.

  4. 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. We need this URL for configuration.

  5. 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.

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

  7. Microsoft.AspNetCore.Authentication.Google

  8. 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"}

  9. Open the Startup.cs file and add the following code under the ConfigureServices method.

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddAuthentication().AddGoogle(googleOptions =>
                {
                    googleOptions.ClientId = Configuration["Authentication:Google:ClientId"];
                    googleOptions.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
                });

  10. 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.

  11. 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

  12. Output

    The following image shows the output after running the application.

  13. 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 jJSRuntime.InvokeAsync<string>("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.

Follow the steps below to create a prerender Blazor WebAssembly app.

  1. Create a new Blazor WebAssembly application using the .NET CLI.

    dotnet new blazorwasm -o BlazorPrerendering.Client

  2. Add a new empty ASP.NET Core Web App to host the project.

    dotnet new web -o BlazorPrerendering.Server


    The solution should now look like this.


  3. Now, add the WebAssembly (client side) project reference in the host server project file (BlazorPrerendering.Server.csproj) and also include the NuGet package (Microsoft.AspNetCore.Components.WebAssembly.Server) using NuGet Package Manager.

    [BlazorPrerendering.Server.csproj]

     <Project Sdk="Microsoft.NET.Sdk.Web">
     
     . . .
     . . .
     
      <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="3.2.1" />
      </ItemGroup>
     
      <ItemGroup>
        <ProjectReference Include="..\BlazorPrerendering.Client\BlazorPrerendering.Client.csproj" />
      </ItemGroup>
     
     
    </Project>

  4. Configure the host in the host server project Startup.cs file.

    [BlazorPrerendering.Server/Startup.cs]

    namespace BlazorPrerendering.Server
    {
        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
     
                services.AddSingleton<HttpClient>(sp =>
                {
                    // Get the address that the app is currently running at
                    var server = sp.GetRequiredService<IServer>();
                    var addressFeature = server.Features.Get<IServerAddressesFeature>();
                    string baseAddress = addressFeature.Addresses.First();
                    return new HttpClient { BaseAddress = new Uri(baseAddress) };
                });
            }
           
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    // Call UseDeveloperExceptionPage on the app builder in the Development environment. 
                    app.UseDeveloperExceptionPage();
                    app.UseWebAssemblyDebugging();
                }
                else
                {
                    app.UseExceptionHandler("/Error");
                    app.UseHsts();
                }
     
                app.UseHttpsRedirection();
                // Call UseBlazorFrameworkFiles on the app builder.
                app.UseBlazorFrameworkFiles();
                app.UseStaticFiles();
                app.UseRouting();
     
                // Change the fallback from the index.html file
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapRazorPages();
                    endpoints.MapControllers();
                    endpoints.MapFallbackToPage("/_Host");
                });
            }
     
        }
    }

  5. Now, you need to create a folder called Pages in the root of the host server project and create a razor page file called _Host.cshtml with the following code.

    [BlazorPrerendering.Server/Pages/_Host.cshtml]

     @page "/"
    @namespace BlazorPrerendering.Server.Pages
    // Need to enable Tag Helpers on the Razor Page
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
     
    <!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>Prerendering client-side Blazor</title>
        <base href="~/" />
        <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
        <link href="css/app.css" rel="stylesheet" />
        <link href="BlazorPrerendering.Client.styles.css" rel="stylesheet" />
     
    </head>
    <body>
        <component type="typeof(BlazorPrerendering.Client.App)" render-mode="WebAssemblyPrerendered" />
     
        <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>

  6. Now, remove the default static wwwroot/index.html file from the Blazor WebAssembly client project.

  7. Delete the following line in Program.cs in the client project.

    [BlazorPrerendering.Client /Program.cs]

     builder.RootComponents.Add<App>("#app"); 


    Now run the application, and you will find the prerendered Blazor WebAssembly app.

Refer to “Prerender and integrate ASP.NET Core Razor components” for more details.

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 startup file to create a consent cookie.

[Startup.cs]

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

public class Startup
    {
       . . .
       . . .
       public void ConfigureServices(IServiceCollection services)
        {
           . . .
                        .   .   .
                         services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            services.AddHttpContextAccessor();
        }
               public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
          . . .
                      .   .   .
                       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 _Host.cshtml file to store the cookie.

[_Host.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]

<div class="page">
    . . .
    . . .

    <div class="main">
        . . .
        . . .
        <ConsentCookie />

        <div class="content px-4">
            @Body
        </div>
    </div>
</div>

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.

Permalink

Blazor doesn’t manipulate the DOM directly at C# side. You cancall 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;
. . .
. . .

public class Program
    {
        public static async Task Main(string[] args)
        {
            . . .
            . . .

            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();
    }
}

Refer to the GitHub SessionStorage repository for more details.

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 app.

[Index.razor]

@page "/" 

<iframe width="560" height="315" src="https://blazor.net" frameborder="0" allowfullscreen></iframe>
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 or local 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);

    }
}

Please refer to “HttpClient Class” for more details.

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

Blazor WASM CLI

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.

wasm cli host

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.

dotnet new blazorserver -o BlazorServerApp

blazorserver cli

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.

server-cli-output

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 . create 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 .
create new project

Select Blazor app from the template

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

Configuring the project

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

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 wasm template

Blazor WebAssembly App structure

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

Run the application.

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

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

To use jQuery UI components in a Blazor application, follow these steps:

Add jQuery reference to host the page

Add jQuery and its UI component script and style references in the ~/_Host.cshtml file.

Initialize the jQuery component

Initialize the jQuery components in the OnAfterRender lifecycle method in your Blazor application by using the JavaScript Interop’s InvokeVoidAsync method.

Refer to the following code samples.

[_Host.cshtml]

….
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="~/script.js"></script>
….

[Index.razor]

@page "/"
@inject IJSRuntime jsRuntime

    <div id="top">
        <a href="#section1">Go Section 1</a>
        <a href="#section2">Go Section 2</a>
        <a href="#section3">Go Section 3</a>
    </div><br>
    <div id="section1">
        In the web project, pagination is a very important part where huge numbers of records are listed from the
        database. In that case, Ajax pagination is a preferable way because it will help to improve the User
        Interface
        of your website. You'll easily be able to implement the Ajax pagination with our PHP Pagination class.
        Pagination class helps to generate the pagination links and get the records from the database using Ajax.In
        the
        web project, pagination is a very important part where huge numbers of records are listed from the database.
        In
        that case, Ajax pagination is a preferable way because it will help to improve the User Interface of your
        website. You'll easily be able to implement the Ajax pagination with our PHP Pagination class. Pagination
        class
        helps to generate the pagination links and get the records from the database using Ajax.
    </div>
    <br /><br />
    <div id="section2">
        <a href="#top">BackToTop</a><br />
        In the web project, pagination is a very important part where huge numbers of records are listed from the
        database. In that case, Ajax pagination is a preferable way because it will help to improve the User
        Interface
        of your website. You'll easily be able to implement the Ajax pagination with our PHP Pagination class.
        Pagination class helps to generate the pagination links and get the records from the database using Ajax.In
        the
        web projec