Calling JavaScript from .NET in Blazor

JavaScript should be added into either /Pages/_Host.cshtml in Server-side Blazor apps, or in wwwroot/index.html for Web Assembly Blazor apps.

BlazorApp.js

var BlazorApp = {
    helloWorld: function () {
        alert("Hello World");
    },
    hello: function (name) {
        alert("Hello " + name);
    },
    sayHi: function (name) {
        return "Hello " + name;
    }
};

add to _Host.cshtml :

<script src="~/scripts/BlazorApp.js"></script>

Index.razor

@page "/"
@inject IJSRuntime JsRuntime;

<div>
    Name : <input @bind-value="name"/>
</div>

<button @onclick="Hello">Hello</button>
<button @onclick="SayHi">Say Hi</button>

<p>@output</p>

@code
{
    private string name;
    private string output;

    protected override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
    // here blazor is ready to call javascript
            Console.WriteLine("Javascript is ready");
            JsRuntime.InvokeVoidAsync("BlazorApp.helloWorld");
        }

        return base.OnAfterRenderAsync(firstRender);
    }

    private async Task Hello()
    {
        await JsRuntime.InvokeVoidAsync("BlazorApp.hello", name);
    }

    private async Task SayHi()
    {
        output = await JsRuntime.InvokeAsync<string>("BlazorApp.sayHi", name);
    }
}

References
https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/
https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/updating-the-document-title/

Integrating Tailwind CSS with Blazor

Creating An npm Project

Make sure that you have Node.js and npm CLI tools installed on your machine. Open the root directory of your Blazor Project.

npm init

Adding Tailwind CSS & Other Packages

npm install -D tailwindcss postcss autoprefixer postcss-cli

Configuring PostCSS

As mentioned earlier, POSTCSS will be responsible for transforming the tailwind.css to your own version. Create a new JS file in the root directory of the project and name it postcss.config.js.

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}

Configuring Tailwind CSS

npx tailwindcss init

tailwind.config.js

module.exports = {
content: ["./**/*.html","./**/*.razor"],
theme: {
extend: {},
},
plugins: [],
}

In the wwwroot\css folder add a new CSS file and name it app.css. ( You could name it anything). This is what POSTCSS will use to generate your site’s CSS resource. Here we will be adding imports from the tailwind CSS library.

@tailwind base;
@tailwind components;
@tailwind utilities;

Building CSS With PostCSS CLI

Now, we need to build a script that can automate the postcss processing. Add the highlighted lines to your package.json file. What this script/command does is simple – It takes in app.css as the input and generates an app.min.css file in the same path. The newly generated file contains the complete tailwindcss content.

{
  "name": "blazorwithtailwindcss",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "buildcss": "postcss wwwroot/css/app.css -o wwwroot/css/app.min.css"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^10.2.4",
    "postcss": "^8.2.6",
    "postcss-cli": "^8.3.1",
    "tailwindcss": "^2.0.3"
  }
}
npm run buildcss

This would have generated an app.min.css file in your root directory. You can notice that all the TailwindCSS styles are generated here.

Removing Unused CSS (See Configuring Tailwind CSS instead because purge is obsolete in version 3)

Open up your tailwind.config.js and modify it to match the following.

const colors = require('tailwindcss/colors');
module.exports = {
    purge: {
        enabled: true,
        content: [
            './**/*.html',
            './**/*.razor'
        ],
    },
    darkMode: false, 
    variants: {
        extend: {},
    },
    plugins: [],
}

PS, Make sure that you run the npm run buildcss command every time you make changes in any of the related js or CSS files. (In the next section, we will discuss post-build events that could automate running the npm command every time you build or rebuild the project.)

Configuring Post Build Events

Open your solution file and paste in the following highlighted snippet. This ensures that the dotnet build system runs the npm command once the project is built.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="npm run buildcss" />
  </Target>
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>

Adding The Stylesheet Reference

Finally, add the referend of the generated CSS file to your Blazor application. Since we have Server Project, you would probably want to add the following snippet to the _Host.cshml file. If you are on a WebAssembly project, Index.html is where you need to add the following.

<link href="~/css/app.min.css" rel="stylesheet" />

Optimizing for Production

npm install -D cssnano

If you’re using Tailwind CLI, you can minify your CSS by adding the --minify flag:

npx tailwindcss -o build.css --minify

If you’ve installed Tailwind as a PostCSS plugin, add cssnano to the end of your plugin list:

postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
    cssnano: {preset: 'default'},
  }
}

References
https://codewithmukesh.com/blog/integrating-tailwind-css-with-blazor/
https://tailwindcss.com/docs/installation/using-postcss
https://tailwindcss.com/docs/optimizing-for-production

Call a Web API from ASP.NET Core Blazor Server

In Program.cs:

builder.Services.AddHttpClient();

Read String using GET method

@page "/"
@inject IHttpClientFactory ClientFactory

<PageTitle>Index</PageTitle>

<button @onclick="Button_Clicked">Call API</button>

@code {

    private async Task Button_Clicked()
    {
    // read string using GET method
        var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5055/hello-world");
        var client = ClientFactory.CreateClient();
        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Console.WriteLine(await response.Content.ReadAsStringAsync());
        }
    }

}

Send and Read JSON using POST method

private async Task Button2_Clicked()
{
// send and read json using POST method
    var url = "http://localhost:5055/hello2";
    var person = new Person() {FirstName = "Mahmood", LastName = "Ramzani"};
    var json = JsonSerializer.Serialize(person);
    var data = new StringContent(json, Encoding.UTF8, "application/json");
    var client = ClientFactory.CreateClient();
    var response = await client.PostAsync(url, data);

    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine(await response.Content.ReadAsStringAsync());
    }
}

 

References
https://docs.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-6.0&pivots=server

Creating a Blazor Layout

Any content you intend to act as a layout template for pages must descend from the LayoutComponentBaseclass. To indicate where you want the content of your page to appear you simply output the contents of the Body property.

@inherits LayoutComponentBase
<div class="main">
  <header>
    <h1>This is the header</h1>
  </header>
  <div class="content">
    @Body
  </div>
  <footer>
    This is the footer
  </footer>
</div>

References
https://blazor-university.com/layouts/creating-a-blazor-layout/
https://blazor-university.com/layouts/using-layouts/

Component Virtualization in ASP.NET Core Blazor

Virtualization is a technique that helps you to process and render only the items that are currently visible on the page (in the content viewport). We can use this technique when dealing with large amounts of data, where processing all the data and displaying the result will take time.

<div style="height:400px; overflow-y:scroll">
    <Virtualize Items="@allBooks">
        <BookSummary @key="book.BookId" Details="@book.Summary" />
    </Virtualize>
</div
<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
    <p>
        @employee.FirstName @employee.LastName has the 
        job title of @employee.JobTitle.
    </p>
</Virtualize>
<Virtualize Context="employee" Items="@employees" ItemSize="25">
    ...
</Virtualize>
<Virtualize Context="employee" Items="@employees" OverscanCount="4">
    ...
</Virtualize>

References
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/virtualization?view=aspnetcore-6.0
https://www.syncfusion.com/blogs/post/7-features-of-blazor-that-make-it-an-outstanding-framework-for-web-development.aspx/amp
https://www.syncfusion.com/blogs/post/asp-net-core-blazor-component-virtualization-in-net-5.aspx

Cancel Background long-running Tasks when a user navigates to another page in Blazor

@page "/State"
@implements IDisposable

<h3>State</h3>

<p>@output</p>

@code {
    private string output = "";
    private CancellationTokenSource cts = new();
    
    protected override async Task OnInitializedAsync()
    {
        while (!cts.IsCancellationRequested)
        {
            await Task.Delay(1000);
            var rnd = new Random();
            output = rnd.Next(1, 50).ToString();
            StateHasChanged();
        }
        
        // cts.Token.ThrowIfCancellationRequested();
    }
    
    public void Dispose()
    {
        cts.Cancel();
        cts.Dispose();
    }
}

References
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-6.0#cancelable-background-work
https://www.syncfusion.com/faq/blazor/web-api/how-do-i-cancel-background-long-running-tasks-when-a-user-navigates-to-another-page-in-blazor

Refresh a Blazor Component with StateHasChanged

@page "/State"
<h3>State</h3>

<p>@output</p>

@code {
    private string output = "";
    
    protected override async Task OnInitializedAsync()
    {
        while (true)
        {
            await Task.Delay(1000);
            var rnd = new Random();
            output = rnd.Next(1, 50).ToString();
            StateHasChanged();
        }
    }
}

References
https://www.techiediaries.com/refresh-blazor-component-statehaschanged-invokeasync/
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-6.0#state-changes-statehaschanged

Optimizing Blazor performance using the @key directive

The @key directive is important when creating components dynamically by iterating list/IEnumerable. Adding the @key will ensure proper change detection and UI update in the collection of components.

<ul class="mt-5">
    @foreach (var person in People)
    {
        <li @key="person.Id">@person.Id, @person.Name</li>
    }
</ul>

Tip: Always use @key for components that are generated in a loop at runtime.

References
https://blazor-university.com/components/render-trees/optimising-using-key/
https://www.meziantou.net/optimizing-blazor-performance-using-the-key-directive.htm
https://www.syncfusion.com/faq/blazor/components/what-is-the-use-of-key-property

Get the Reference or Instance of a Component in ASP.NET Core Blazor

o capture a component reference in Blazor, use the @ref directive attribute. The value of the attribute should match the name of a settable field with the same type as the referenced component.

<MyLoginDialog @ref="loginDialog" ... />

@code {
    MyLoginDialog loginDialog;

    void OnSomething()
    {
        loginDialog.Show();
    }
}

References
https://docs.microsoft.com/en-us/dotnet/architecture/blazor-for-web-forms-developers/components#capture-component-references
https://www.syncfusion.com/faq/blazor/components/how-do-i-get-the-reference-or-instance-of-a-component