為單一@page 掛上專用的 CSS 或 JavaScript |HeadOutlet & SectionOutlet

引言

有時有些 @page 須要專用 CSS 或 JavaScript。這時可用 HeadOutlet\HeadContentSectionOutlet\SectionContent 來滿足需求。這技巧在 MVC 也有,稱做 @RenderSection

參考文章

HeadOutlet and HeadContent
SeSectionOutlet and SectionContentction

關鍵知識

有時有些 @page 須要專用 CSS 或 JavaScript。這時可用 HeadOutlet\HeadContentSectionOutlet\SectionContent 來滿足需求。這技巧在 MVC 也有,稱做 @RenderSection

其實 <PageTitle> 就是用 <HeadOutlet /> 實作的。

開發環境

平台: .NET8

框架: Blazor Server App

關鍵原碼

App.razor
@using Microsoft.AspNetCore.Components.Sections
<!DOCTYPE html>
<html lang="zh-hant">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <base href="/" />
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
  <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
  <link rel="stylesheet" href="app.css" />
  <link rel="stylesheet" href="YourN8BlazorApp.styles.css" />
  <link rel="icon" type="image/png" href="favicon.png" />
  <HeadOutlet @rendermode="@RenderModeForPage" />
  <SectionOutlet @rendermode="@RenderModeForPage" SectionName="myHeadScript" />
  <!-- 用 SectionOutlet 挖個洞 -->
</head>

<body>
  <Routes @rendermode="@RenderModeForPage" />
  <script src="_framework/blazor.web.js"></script>
  <script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>

</html>

@code {
  [CascadingParameter]
  private HttpContext HttpContext { get; set; } = default!;

  private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account")
      ? null
      : InteractiveServer;
}

為該 @page 加入專用的 JavaScript,此例為 Login Page 專門加掛 Tailwind CSS 。

參考:https://flowbite.com/blocks/marketing/login/

Login.razor
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Authentication.Cookies
@using Microsoft.AspNetCore.Authentication
@using System.Security.Claims
@using Microsoft.AspNetCore.Components.Sections
@using N8BlazorServerAuth.Services

@page "/Account/Login"
@inject NavigationManager navSvc
@inject ILogger<Login> Logger
@inject AccountService accSvc

<PageTitle>Log in</PageTitle>

<SectionContent SectionName="myHeadScript">
  <script src="https://cdn.tailwindcss.com"></script>
  <script>
    tailwind.config = {
      darkMode: 'class',
      ...略...
    }
  </script>
</SectionContent>

<!-- 該 Login Page 改專用 Tailwind CSS 來實作UI -->
<section class="bg-gray-50 dark:bg-gray-900">
  <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
    <a href="#" class="flex items-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
      <img class="w-8 h-8 mr-2" src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/logo.svg" alt="logo">
      Flowbite
    </a>
    <div class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
      <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
        <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
          Sign in to your account
        </h1>
        <form class="space-y-4 md:space-y-6" action="#">
          <div>
            <label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label>
            <input type="email" name="email" id="email" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="name@company.com" required="">
          </div>
          <div>
            <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Password</label>
            <input type="password" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required="">
          </div>
          <div class="flex items-center justify-between">
            <div class="flex items-start">
              <div class="flex items-center h-5">
                <input id="remember" aria-describedby="remember" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800" required="">
              </div>
              <div class="ml-3 text-sm">
                <label for="remember" class="text-gray-500 dark:text-gray-300">Remember me</label>
              </div>
            </div>
            <a href="#" class="text-sm font-medium text-primary-600 hover:underline dark:text-primary-500">Forgot password?</a>
          </div>
          <button type="submit" class="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">Sign in</button>
          <p class="text-sm font-light text-gray-500 dark:text-gray-400">
            Don’t have an account yet? <a href="#" class="font-medium text-primary-600 hover:underline dark:text-primary-500">Sign up</a>
          </p>
        </form>
      </div>
    </div>
  </div>
</section>

@code {
  [CascadingParameter]
  private HttpContext HttpContext { get; set; } = default!;

  [SupplyParameterFromForm]
  private InputModel Input { get; set; } = new();

  [SupplyParameterFromQuery]
  private string? ReturnUrl { get; set; }

  string? errorMessage;
  [...略...]

  public async Task LoginUser()
  {
    errorMessage = null;
    var authUser = accSvc.Authenticate(Input);
    if (authUser == null)
    [...略...]
  }
}

(EOF)

Last updated