Blazor FAQ - General

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

Yes, Syncfusion Blazor components are indeed mobile-friendly. They have been developed with a mobile-first approach, ensuring that they are responsive and adaptive across various devices. This means that whether users access applications built with Syncfusion Blazor components on desktops, tablets, or smartphones, they will experience consistent usability and functionality.

Permalink
  • Comprehensive Component Library: The Syncfusion Blazor UI library includes unique components: Diagram, Smith Chart, Bullet Chart, Kanban Chart, Rating, Word Processor, Image Editor, Mention, AppBar, Speed Dial, Message, Toast, In-place Editor, Query Builder, and PowerPoint library.

  • Theming and Customization: Customize your Blazor application with our Fluent, Material 3, Bootstrap 5, high contrast, or dark mode themes. Additionally, our Theme Studio tool allows rich customization of these themes according to your preferences.

  • Performance Optimization: Syncfusion provides a CRG (custom resource group) to optimize loading performance by selectively loading specific component scripts and CSS styles as needed.

  • Adaptive and Touch-Friendly Design: All Syncfusion Blazor components are touch-friendly and adaptively render based on device, ensuring an optimal user experience across phones, tablets, and desktops.

  • Data and UI Virtualization: For large-scale applications, applicable components support data and UI virtualization, enhancing performance and scalability. This feature is available in components like DataGrid, Scheduler, and Gantt Chart.

  • Seamless Data Binding: Syncfusion Blazor components support seamless data binding for data-bound controls such as DataGrid, Pivot Table, Gantt Chart, and Scheduler. It includes two-way data binding for applicable component properties, enabling efficient data management.

  • Extensive Documentation and Demos: Access over 1,000 interactive demos, comprehensive user guide documentation, and class references to facilitate a smoother learning curve and implementation process.

Permalink

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

To use fluent validation in a Blazor application:

  1. Create a Blazor application using the link.

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

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

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

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


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

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

     

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

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

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

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

    Fluent Validator Output


    View Sample in GitHub

Permalink

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

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

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

Please refer to the code example below.

[Index.razor]

@page "/"

@using System.ComponentModel.DataAnnotations;

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

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

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

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

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

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

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

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

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

</EditForm>

@code{

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

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

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

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

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

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

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

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

View Sample in GitHub

Permalink

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

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

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

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

  4. Models

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

 View Sample in GitHub 

Permalink

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

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

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

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

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

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

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

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

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

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

      Nested Form Output

View Sample in GitHub

Permalink

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

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

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

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

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

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

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



View Sample in GitHub

Permalink

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

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

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

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

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

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

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

     

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

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


    Bootstrap css output


    View Sample in GitHub
Permalink

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

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

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

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

    npm init -y


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

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

    npm install webpack webpack-cli –save-dev

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

    npm install @syncfusion/ej2-maps

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

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

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

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

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

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

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

     

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

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

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

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

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

Permalink

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

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

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

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

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

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

Permalink

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

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

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

    [.csproj]

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

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

    [bundleconfig.json]

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

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

    Output


    View Sample in GitHub

Permalink

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

[Program.cs] 

…. 

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

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

[Program.cs]

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

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

[Progaram.cs]

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

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

Permalink

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

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

meta tags

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

meta title

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

h1 tag

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

meta description

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

meta keywords

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

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

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

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

meta tags

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

meta title

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

h1 tag

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

meta description

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

meta keywords

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

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

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

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

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

Permalink

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

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

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

[Index.razor]


@page "/"

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

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

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

View Sample in GitHub

Permalink

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View Sample in GitHub 

Permalink

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

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

[Counter.Razor]

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

<h1>Counter with Timer disposal</h1>

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

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

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

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

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

View Sample in GitHub

Permalink

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

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

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

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

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

  4. To run the application offline:

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

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

Razor file

@page "/" 

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

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

Refer to this link for more details.

View Sample in GitHub

Permalink

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

[Index.razor.css]

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

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

[Index.razor]

@page "/"

<h1>Blazor App</h1>

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

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

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

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

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

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

     

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

    Refer to this link for more details.


    View Sample in GitHub
Permalink

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

[Index.razor]

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

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

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

[Counter.razor]

@page "/counter"
@using System.Timers

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

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

    private void IncrementCount()
    {
        currentCount++;
    }

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

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

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

Refer to this link for more details.

Permalink

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

[Program.cs]

builder.Services.AddHttpContextAccessor();

[Index.razor]

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

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

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

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

Refer to this link for more details.

Permalink

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

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

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

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

[Pages/_Host.cshtml] 

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

Refer to this link for details. 

Permalink

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

[Index.razor]

@page "/"
@using System.Timers;

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

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

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

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

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

View Sample in GitHub

Permalink

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

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

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

[Pages/AnimeState.razor]


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

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

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

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

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

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

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

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

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

[Index.razor]

@page "/"

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

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

View Sample in GitHub

Permalink

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

[Index.razor]

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

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

@code {
    private int currentCount;
    private bool isCount;

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

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

Refer to this link for more details.

Permalink

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

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

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

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

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

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

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

View Sample in GitHub

Permalink

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

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

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

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

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

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

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

Refer to this link for more information. 

View Sample in GitHub 

Permalink

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Refer to this documentation for more details.

View Sample in GitHub 

Permalink

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

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

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

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

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

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

Refer to this documentation for more details.

Permalink

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

  1. Create Blazor application

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

  2. Add OIDC and authentication configuration

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

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