Nucleus .Net Core CMS

Using Blazor in Nucleus Extensions

Nucleus supports Blazor Server and Blazor WebAssembly components. Blazor Components are launched by an MVC view by using the Component tag helper. Parameters can be passed to the component using attributes with the param- prefix.

To create a Nucleus extension which uses Blazor components in Visual Studio:

  1. Start by using one of the standard Nucleus project templates ("Nucleus Complex Extension Project" or "Nucleus Simple Extension Project").
    See Walkthough - Developing a Nucleus Module for more information.
  2. Add an additional project to your solution for your Blazor components. The Blazor WebAssembly App Empty template which ships with Visual Studio is a good starting point. A typical naming convention for the components project is <ModuleName>.Client, where <ModuleName> is the name of your Nucleus extension. You may need to update the project (csproj) file to target .NET 9.0 (or later, if you are targeting a future version of Nucleus that uses a later .Net core version). You don't need to add any project dependencies for Nucleus to your Blazor components project.
  3. Create a "Components" folder in your Blazor components project and add your Blazor components to this folder. The components should be in a namespace that matches the project name, e.g., GuidGenerator.Client.Components for a project named GuidGenerator.Client. You may also want to delete some of the default files that are generated by the project template (App.razor, MainLayouyt.razor and the Pages folder).
  4. Open program.cs in your Blazor components project and remove the builder.RootComponents... lines.
  5. Add a project reference to the Blazor components project in your Nucleus extension project.
  6. Write code in your Nucleus Extension controller class to read data and perform whatever processing is needed to render your view. This is typically done in the Index action method of your controller.
  7. In your view, use the Html.AddWebAssembly Html Helper to add your Blazor WebAssembly component. This is required so that your WebAssembly files are dynamically downloaded. You can call Html.AddWebAssembly more than once to add multiple web assemblies if required.
  8. Open the package.xml file in your Nucleus extension project and add a reference to your Blazor components project's .wasm file(s). Include both the .wasm and .wasm.gz files to ensure that the compressed version is also available.
    <folder name="bin">
      <file name="Nucleus.GuidGenerator.dll" />               <!-- Nucleus extension assembly. This will already be present.  -->
      <file name="Nucleus.GuidGenerator.Client.dll" />        <!-- Components project assembly, used server-side. -->
      <file name="Nucleus.GuidGenerator.Client.wasm" />       <!-- Components project WebAssembly. -->
      <file name="Nucleus.GuidGenerator.Client.wasm.gz" />    <!-- Components project WebAssembly, compressed. -->
    </folder>

When you compile a Blazor WebAssembly project, the compiler will generate a .wasm filename with a "fingerprint" suffix, which changes when you make changes to your code and looks like random letters and numbers. For example: Nucleus.GuidGenerator.Client.3lfn6z8ve7.wasm. You don't need to include the fingerprint in the package.xml file element. The Nucleus build tools will automatically detect and use the file name which includes the "fingerprint" that .NET has generated.

Example Razor View

Your Razor view will add required scripts and styles, and render the Blazor component, passing any required parameters.

@model Nucleus.GuidGenerator.ViewModels.Viewer
@using Nucleus.ViewFeatures.HtmlHelpers
@using Nucleus.GuidGenerator.Client.Components
@Html.AddStyle("~#/viewer.css")
@* blazor.web.js (WellKnownScripts.WELLKNOWN_BLAZOR_WEB) is used for both server-rendered and webassembly components. *@
@Html.AddScript(AddScriptHtmlHelper.WellKnownScripts.BLAZOR_WEB)

@* 
  AddWebAssembly downloads and loads the specified WebAssemblies dynamically when the page is rendered. You can call Html.AddWebAssembly 
  more than once to add more web assemblies.   Html.AddWebAssembly automatically detects and replaces the file name with the name of 
  the "fingerprinted" file that .net 9 Blazor generates.
  Use the Html.AddWebAssembly line below if your render mode is InteractiveWebAssembly or InteractiveAuto. If you are using 
  InteractiveServer render mode you would not use the Html.AddWebAssembly line below.  
*@
@Html.AddWebAssembly("~#/bin/Nucleus.GuidGenerator.Client.wasm")

<component type="typeof(GuidGeneratorComponent)" param-NewGuidCount="@Model.NewGuidCount" param-nonce="@Context.Nonce()" render-mode="WebAssembly" />
  1. The @model directive is a standard MVC/Razor directive that specifies the model type for the view.
  2. @using Nucleus.ViewFeatures.HtmlHelpers imports the namespaces which provide the Html.AddScript and Html.AddWebAssembly Html helpers.
  3. @using Nucleus.GuidGenerator.Client.Components is a reference to the namespace in your Blazor components project which contains the component(s) that you want to use.
  4. @Html.AddStyle (optional) adds a CSS file to the view, which can be used to style the components. Using the AddStyle html helper prevents duplicate stylesheet elements if your module is added to a page more than once, or if you are using a stylesheet that may also be added by another module on the page.
  5. @Html.AddScript(AddScriptHtmlHelper.WellKnownScripts.BLAZOR_WEB) adds the Blazor web assembly script (blazor.web.js) necessary for running Blazor components. If you are using Blazor WebAssembly and are calling Html.AddWebAssembly, this line can be omitted as it is added automatically, but it is OK to include it, as Nucleus automatically prevents duplicate script elements.
  6. @Html.AddWebAssembly("~#/bin/Nucleus.GuidGenerator.Client.wasm") adds the Blazor WebAssembly component assembly (generated when you compile your components project).
  7. The Component tag helper is used to render the Blazor component. The param-nonce="@Context.Nonce()" parameter is used to pass the nonce value for Content Security Policy (CSP). Use the nonce value if you are rendering <script> elements in your Blazor component.

Example Component

@rendermode InteractiveWebAssembly

<div class="d-flex flex-column">
  @foreach (var item in GeneratedGuids.Select((Guid guid, int index) => new { guid, index }))
  {
    <div class="my-2 d-flex">
      <input type="text" name="Guid_@(item.index)" title="Guid # @(item.index)" class="flex-1 guidControl" value="@item.guid" />
    </div>
  }
</div>
<button class="btn btn-secondary" type="button" @onclick="GenerateClicked">Generate New GUIDs</button>

<script nonce="@Nonce">
  jQuery(function()
  {
    Page.AttachCopyButton('.guidControl');
  });
</script>
  1. Note the use of the @Nonce attribute in the <script> element. This is required for the script to run properly with the automatic Content Security Policy that Nuclues generates.
using Microsoft.AspNetCore.Components;

namespace Nucleus.GuidGenerator.Client.Components;

public partial class GuidGeneratorComponent
{
  /// <summary>
  /// The number of new GUIDs to generate.
  /// </summary>
  [Parameter] public int NewGuidCount { get; set; }

  /// <summary>
  /// The nonce to use for script tags so that they work with Content Security Policy.
  /// </summary>
  [Parameter] public string Nonce { get; set; } = "";

  /// <summary>
  /// A list of generated GUIDs, used by the Razor view to generate output.
  /// </summary>
  public List<Guid> GeneratedGuids { get; set; } = [];

  /// <summary>
  /// Called when the component is initialized.
  /// </summary>
  /// <returns></returns>
  protected override Task OnInitializedAsync()
  {  
    GenerateGuids(this.NewGuidCount);
    return Task.CompletedTask;
  }

  /// <summary>
  /// Called when the "Generate" button is clicked.
  /// </summary>
  private void Generate_Clicked()
  {
    GenerateGuids(this.NewGuidCount);
    StateHasChanged(); // Refresh the UI to show the new GUIDs
  }

  /// <summary>
  /// Generate a new set of GUIDs.
  /// </summary>
  /// <param name="howMany"></param>
  private void GenerateGuids(int howMany)
  { 
    for (int count = 0; count < howMany; count++)
    {
      if (this.GeneratedGuids.Count > count)
      {
        this.GeneratedGuids[count] = Guid.NewGuid();
      }
      else
      {
        this.GeneratedGuids.Add(Guid.NewGuid());
      }
    }
  }
}
  1. The NewGuidCount and Nonce properties are passed in to the the component in the <component> tag in the MVC Razor View.