ASP.NET Core Blazor 3:使用 Blazor Server (2)

一纸年华發表於2024-06-26

1 準備工作

  繼續使用上一章專案。
  本章展示如何組合Razor元件來建立更復雜的特性。展示如何建立元件之間的父子關係,如何利用屬性配置元件,以及如何建立自定義事件,以在發生重要更改時發出訊號。還展示了元件如何從父元件接收內容,以及如何使用模板元件一致地生成內容,模板元件可以用一個或多個泛型型別引數定義。在本章結束時,演示了 Blazor應用程式如何對連線和應用程式錯誤做出反應。

2 結合元件

  Blazor 元件可以組合起來建立更復雜的功能。Blazor 資料夾中新增 SelectFilter.razor:

<div class="form-group">
    <label for="select-@Title">@Title</label>
    <select name="select-@Title" class="form-control" @bind="SelectedValue">
        <option disabled selected>Select @Title</option>
        @foreach (string val in Values)
        {
            <option value="@val" selected="@(val == SelectedValue)">
                @val
            </option>
        }
    </select>
</div>

@code
{
    public IEnumerable<string> Values { get; set; } = Enumerable.Empty<string>();
    public string SelectedValue { get; set; }
    public string Title { get; set; } = "Placeholder";
}

  該元件呈現現一個 selcect 元素,該元素允許使用者選擇城市。在 Blazor 資料夾的 PeopleList.razor 中應用此元件替換現有的 select:

<SelectFilter /> 

  當元件新增到控制器檢視或 Razor Pages 所呈現的內容中時,就會使用元件元素,當一個元件新增到另一個元件所呈現的內容中時,該元件的名稱將用作一個元素。本例向 PeopleList 元件所呈現的內容新增了 SelectFilter 元件,使用 SelectFilter 元素來實現,大小寫必須匹配。
  組合元件時,效果是一個元件將其部分佈局的職責委託給另一個元件。本例各組成部分構成父/子關係:PcopleList 元件是父元件,SelectFilter 元件是子元件。
  請求http:/localhost:5000/controllers檢視效果。

2.1 利用屬性配置元件

  使用 SelectList 元件的目標是建立一個可在整個應用程式中使用的通用特性,配置每次使用它時顯示的值。Razor 元件是用屬性配置的,這些屬性新增到應用它們的 HTML 元素中。分配給 HTML 元素屬性的值被分配給元件的 C# 屬性。Parameter 屬性應用於元件允許配置的 C# 屬性, 在 Blazor 資料夾的 SelectFilter.razor 檔案中宣告可配置屬性如下:

@code
{
    [Parameter]
    public IEnumerable<string> Values { get; set; } = Enumerable.Empty<string>();
    public string SelectedValue { get; set; }
    [Parameter]
    public string Title { get; set; } = "Placeholder";
}

  元件可以選擇它們允許配置的屬性。在本例中,Parameter屬性已應用於 SelectFilter 元件定義的兩個屬性。在 Blazor 資料夾的 PeopleList.razor ,修改了元件用於應用 SelectFilter 元件以新增配置屬性的元素,如下:

<SelectFilter values="@Cities" title="city" />

  對於每個應該配置的屬性,將一個同名屬性新增到父元素的 HTML 元素中。屬性值可以是固定的值,比如分配給 title 屬性的 City 字串,也可以是 Razor 表示式,如 @Cities (它將 City 屬性中的物件序列分配給 values 屬性)。

1.設定和接收批次配置設定

  如果存在許多配置設定,可以指定一個屬性來接收沒有被其他屬性匹配的任何屬性值,然後可將其作為一個集合應用。如下在 Blazor 資料夾的 SelectFiter.razor 檔案中接收未匹配屬性:

<select name="select-@Title" class="form-control" @bind="SelectedValue"
    @attributes="Attrs">
......
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> Attrs { get; set; }

  將 Parameter 屬性的 CaptureUnmatchedValues 引數設定為 true,將標識一個屬性作為未以其他方式匹配的屬性的集合。屬性的型別必須是 Dictionary<string,object>,這表示屬性名稱和值,可以使用@attribute 表示式應用於元素。它允許一次性應用一組屬性,上面更改的效果意味著 SelectFilter 元件接收 Values 和 Title 屬性值,其他任何屬性都分配給 Attrs 屬性並傳遞給 select 元素。
在 Blazor 資料夾的 PeopleList.razor 檔案中新增元素屬性:

<SelectFilter values="@Cities" title="city" autofocus="true" name="city" 
    required ="true"/>

  請求 http://localhost:5000/controllers。傳遞給 select 元素的屬性不影響顯示,右擊選擇元素,並從彈出選單中選擇檢查,就將看到屬性新增到 PeopleList 元件的 SelectFilter 元素中,SelectFilter 元素已新增到 SelectFilter 元件呈現的元素中。
2.在控制器檢視或 Razor Pages 中配置元件
  當使用元件元素應用元件時,屬性還用於配置元件。在 Blazor 資料夾的 PeopleList.razor,向 PeopleList 元件新增了屬性,該屬性指定應該顯示來自資料庫的多少項,以及將傳遞給 SelectFilter 元件的字串值,如下:

<SelectFilter values="@Cities" title="@SelectTitle" />
@code
{
    [Inject]
    public DataContext Context { get; set; }
    public IEnumerable<Person> People => Context.People
        .Include(p => p.Department).Include(p => p.Location)
        .Take(ItemCount);
    public IEnumerable<string> Cities => Context.Locations.Select(l => l.City);
    public string SelectedCity { get; set; }
    public string GetClass(string city) =>
        SelectedCity == city ? "bg-info text-white" : "";
    [Parameter]
    public int ItemCount { get; set; } = 4;
    [Parameter]
    public string SelectTitle { get; set; }
}

  C#屬性的值是透過將名稱以param-開頭、後跟屬性名稱的屬性新增到元件元素來提供的,在 Views/Home 資料夾的 Index.cshtm 檔案中新增配置屬性如下:

<component type="typeof(MyAdvanced.Blazor.PeopleList)" render-mode="Server"
           param-itemcount="5" param-selecttitle="@("Location")" />

  param-itemcount 屬性為 ItemCount 屬性提供一個值,param-selecttitle 屬性為 SelectTitle 屬性提供一個值。
  在使用元件元素時,可以解析為數值或 bool值的屬性值。其他值假定為 Razor 表示式而不是文字值,因為它們沒有以@作為字首,那麼,為將 SelectTitle 屬性的值指定為文字字串,就需要一個 Razor 表示式。

2.2 建立自定義事件和繫結

  SelectFilter 元件從父元件接收資料值,但它無法指示使用者何時進行選擇。為此,需要建立一個自定義事件,父元件可為其註冊一個處理程式方法,就像它為常規 HTML 元素中的事件所做的那樣。向 SelectFilter 元件新增了一個自定義事件:

<select name="select-@Title" class="form-control" @onchange="HandleSelect" 
    value="@SelectedValue">
[Parameter]
public EventCallback<string> CustomEvent { get; set; }
public async Task HandleSelect(ChangeEventArgs e)
{
    SelectedValue = e.Value as string;
    await CustomEvent.InvokeAsync(SelectedValue);
}

  自定義事件是透過新增一個型別為 EventCalback的屬性來定義的。泛型型別引數是父類的事件處理程式接收的型別,在本例中為sting。前面更改了 select 元素,所以當 select 元素觸發其 onchange 事件時,@onchange 屬性會註冊 HandleSelect 方法。透過呼叫 EventCallback.InvokeAsync 方法,HandleSelect 方法會更新 SelectedValue 屬性。
  InvokeAsync方法的引數使用從 ChangeEventArgs 物件接收到的值(從select元素接收到的值)觸發事件。更改了 PeopleList 元件,以便它接收由 SelectList 元件發出的定製事件:

<SelectFilter values="@Cities" title="@SelectTitle" CustomEvent="@HandleCustom"/>
public void HandleCustom(string newValue)
{
    SelectedCity = newValue;
}

  要設定事件處理程式,需要向使用 EventCallback屬性名稱應用子元件的元素新增一個屬性。屬性的值是一個 Razor 表示式,它選擇接收型別為 T 的引數的方法。
  請求 http://localhost:5000/controllers,並從城市列表中選擇一個值。自定義事件完成父元件和子元件之間的關係。父元素透過其屬性配置子元素,以指定呈現給使用者的標題和資料值列表。子元件使用自定義事件來告訴父元件,當使用者選擇一個值時,允許父物件在其HTML 表中突出顯示相應的行。
建立自定義繫結
  父元件可以在子元件上建立繫結,前提是它定義了一對屬性,其中一個屬性被分配一個資料值,另一個屬性是自定義事件。屬性的名稱很重要:事件屬性的名稱必須與資料屬性的名稱加 Changed 相同。
  在 Blazor 資料夾的 SelectFiter.razor 檔案中準備自定義繫結,更新了 SelectFiter 元件,以便它顯示繫結所需的屬性,如下:

[Parameter]
public string SelectedValue { get; set; }
......
[Parameter]
public EventCallback<string> SelectedValueChanged { get; set; }
public async Task HandleSelect(ChangeEventArgs e)
{
    SelectedValue = e.Value as string;
    await SelectedValueChanged.InvokeAsync(SelectedValue);
}

  注意,Parameter 屬性必須同時應用於 SelectedValue 和 SelectedValueChanged 屬性。如果省略其中任何一個屬性,資料繫結將無法按預期工作。
  父元件透過@bind-屬性繫結到子元件,其中對應於子元件定義的屬性。在本例中,子元件的屬性名是 SelectedValue,父元件可以使用@bind-SelectedValue 建立繫結。在 Blazor 資料夾 PeopleList.razor 檔案中使用自定義繫結:

<SelectFilter values="@Cities" title="@SelectTitle" @bind-SelectedValue = "SelectedCity"/>
<button class="btn btn-primary" @onclick="@(()=> SelectedCity = "San Jose")">
    Change
</button>

  請求 http://localhost:5000/controllers,並從城市列表中選擇 New York,自定義繫結將使select 元素中選擇的值透過表中的高亮顯示反映出來。單擊 Change 按鈕以在另一個方向測試繫結,城市的更改將高亮顯示。

3 在元件中顯示子內容

  顯示子內容的元件充當父元素提供的元素的包裝器。要檢視子內容是如何管理的,請給 Blazor資料夾新增名為 ThemeWrapper.razor 的 Razor 元件,如下:

<div class="p-2 bg-@Theme border text-white">
    <h5 class="text-center">@Title</h5>
    @ChildContent
</div>

@code
{
    [Parameter]
    public string Theme { get; set; }
    [Parameter]
    public string Title { get; set; }
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

  為接收子內容,元件定義了一個名為 ChildContent 的屬性。@ChildContent 表示式在元件的 html 輸出中包含子內容。div 元素使用引導主題顏色進行樣式化,並顯示標題。
  子內容是透過在應用元件時在開始和結束標記之間新增HTML元素來定義的。在 Blazor 資料夾的 PeopleList.razor 檔案中定義子內容:

<ThemeWrapper Theme="info" Title="Location Selector">
    <SelectFilter values="@Cities" title="@SelectTitle" 
        @bind-SelectedValue="SelectedCity" />
    <button class="btn btn-primary" @onclick="@(()=> SelectedCity = "San Jose")">
        Change
    </button>
</ThemeWrapper>

  配置子內容不需要附加屬性,子內容將被自動處理並分配給ChidContcent屬性。請求 http://localhost:5000/controllers,將看到選擇主題的配置屬性和用於生成響應的標題文字。

3.1 建立模板元件

  模板元件為子內容的表示帶來了更多的結構,允許顯示內容的多個部分。模板元件可以很好地整合應用程式中使用的特性,以防止程式碼和內容的重複。
要了解其工作原理,請給 Blazor 資料夾新增一個名為 TableTemplate.razor 的 Razor 元件:

<table class="table table-sm table-bordered table-striped">
    @if (Header != null)
    {
        <thead>@Header</thead>
    }
    <tbody>@Body</tbody>
</table>

@code
{
    [Parameter]
    public RenderFragment Header { get; set; }
    [Parameter]
    public RenderFragment Body { get; set; }
}

  元件為它支援的每個子內容區域定義一個 RenderFragment 屬性。TableTemplate 元件定義了兩個Renderfragment 屬性,命名為 Header 和 Body,它們代表了表格的內容部分,子內容的每個區域都是使用 Razor 表示式 @Header 和 @Body 呈現的,可以透過檢查屬性值是否為空來檢查是否為特定部分提供了內容,該元件為 Header 部分執行此操作。
  當使用模板元件時,每個區域的內容都包含在一個 HTML 元素中,該元素的標記與對應的Renderfragment 屬性的名稱相匹配。 在 Blazor 資料夾的 PeopleList.razor 檔案中應用模板元件:

<TableTemplate>
    <Header>
        <tr><th>ID</th><th>Name</th><th>Dept</th><th>Location</th></tr>
    </Header>
    <Body>
        @foreach (Person p in People)
        {
        <tr class="@GetClass(p.Location.City)">
            <td>@p.PersonId</td>
            <td>@p.Surname, @p.Firstname</td>
            <td>@p.Department.Name</td>
            <td>@p.Location.City, @p.Location.State</td>
        </tr>
        }
    </Body>
</TableTemplate>

  子內容構成與模版元件的屬性、標題和主體對應的部分,這使得 TableTemplate 元件負責表結構,而 PeopleList 元件負責提供詳情。請求 http://localhost:5000/controllers,將看到模版元件愛你生成的輸出。

3.2 在模板元件中使用泛型型別引數

  模板元件可透過使用泛型型別引數來感知資料,泛型型別引數允許父元件提供一系列資料物件和用於表示這些物件的模板。模板元件負責為每個資料物件生成內容,因此可以提供更有用的功能。
  下面將向模板元件新增對選擇顯示多少錶行和選擇錶行的支援。第一步是向元件新增一個泛型型別引數,並使用它呈現表主體的內容。在 Blazor 資料夾的 TableTemplate.razor 檔案中新增泛型型別引數:

@typeparam RowType

<table class="table table-sm table-bordered table-striped">
    @if (Header != null)
    {
        <thead>@Header</thead>
    }
    <tbody>
        @foreach(RowType item in RowData)
        {
            <tr>@RowTemplate(item)</tr>
        }
    </tbody>
</table>

@code
{
    [Parameter]
    public RenderFragment Header { get; set; }
    [Parameter]
    public RenderFragment<RowType> RowTemplate { get; set; }
    [Parameter]
    public IEnumerable<RowType> RowData { get; set; }
}

  泛型型別引數是使用 @typeparam 屬性指定的,本例為引數指定了名稱 RowType,因為引用元件將為其生成錶行的資料型別。
  元件處理的資料是透過新增一個屬性來接收的,該屬性的型別是泛型型別的物件序列。將屬性命名為 RowData,其型別為 IEnumerable<RowType>。元件為每個物件顯示的內容是使用RenderFragment<T> 屬性接收的。將這個屬性命名為 RowTemplate,它的型別是RenderFragment<RowType>,反映了為泛型型別引數選擇的名稱。
  當元件透過 RenderFragment<T>屬性接收到一個內容區段時,可透過呼叫該區段作為方法並使用該物件作為引數,來為單個物件呈現它@RowTemplate(item)
這段程式碼列舉 RowData 序列中的 RowType 物件,併為每個物件呈現透過 RowTemplate 屬性接收到的內容部分。
1.使用通用模板元件
  前面簡化了 PeopleList 元件,使其僅使用模板元件生成 Person 物件表,並且刪除了以前的特性。在 Blazor 資料夾的 PeopleList.razor 檔案中使用通用模板元件:

<TableTemplate RowType="Person" RowData="People">
    <Header>
    <tr><th>ID</th><th>Name</th><th>Dept</th><th>Location</th></tr>
    </Header>
    <RowTemplate Context="p">
        <td>@p.PersonId</td>
        <td>@p.Surname, @p.Firstname</td>
        <td>@p.Department.Name</td>
        <td>@p.Location.City, @p.Location.State</td>
    </RowTemplate>
</TableTemplate>

@code
{
    [Inject]
    public DataContext Context { get; set; }

    public IEnumerable<Person> People => Context.People
            .Include(p => p.Department)
            .Include(p => p.Location);
}

  RowIype 屬性用於指定泛型型別引數的值。RowData 屬性指定模板元件要處理的資料。RowTempaie 元素表示為每個資料物件生成的元素。當為 Renderragment屬性定義一個內容區段時,conrext屬性用於為當前處理的物件分配一個名稱。
  在本例中,使用 Context 屬性們名稱 p 分配給當前物件,然後在用於填充內容部分元素的 Razor 表示式中引用該物件。
總體效果是將模板元件配置為顯示 Person 物件。該元件為每個 Person 生成一個錶行,其中包含 td 元素,其內容是使用當前 Person 物件的屬性設定的。
刪除了用 Parameter 屬性裝飾的屬性,所以需要從應用 PepleList 元件的元素中刪除相應的屬性,在 Views/Home 資料夾的 Index.cshtml 檔案中刪除屬性:

<component type="typeof(MyAdvanced.Blazor.PeopleList)" render-mode="Server"/>

  要檢視通用模板元件,請求 http://localhost:5000/controllers。PeopleList 元件提供的資料和內容部分已被 TableTemplate 元件用於生成列表。
2.向通用模板元件新增特性
  在 Blazor 資料夾的 TableTemplate.razor 檔案中新增特性:

@typeparam RowType

<div class="container-fluid">
    <div class="row">
        <div class="col">
            <SelectFilter Title="@("Sort")" Values="@SortDirectionChoices"
            @bind-SelectedValue="SortDirectionSelection" />
        </div>
        <div class="col">
            <SelectFilter Title="@("Highlight")" Values="@HighlightChoices()"
            @bind-SelectedValue="HighlightSelection" />
        </div>
    </div>
</div>

<table class="table table-sm table-bordered table-striped">
    @if (Header != null)
    {
        <thead>@Header</thead>
    }
    <tbody>
        @foreach (RowType item in SortedData())
        {
            <tr class="@IsHighlighted(item)">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

@code {
    [Parameter]
    public RenderFragment Header { get; set; }

    [Parameter]
    public RenderFragment<RowType> RowTemplate { get; set; }

    [Parameter]
    public IEnumerable<RowType> RowData { get; set; }

    [Parameter]
    public Func<RowType, string> Highlight { get; set; }

    public IEnumerable<string> HighlightChoices() =>
        RowData.Select(item => Highlight(item)).Distinct();

    public string HighlightSelection { get; set; }

    public string IsHighlighted(RowType item) =>
        Highlight(item) == HighlightSelection ? "bg-dark text-white" : "";

    [Parameter]
    public Func<RowType, string> SortDirection { get; set; }

    public string[] SortDirectionChoices =
        new string[] { "Ascending", "Descending" };

    public string SortDirectionSelection { get; set; } = "Ascending";

    public IEnumerable<RowType> SortedData() =>
        SortDirectionSelection == "Ascending"
            ? RowData.OrderBy(SortDirection)
            : RowData.OrderByDescending(SortDirection);
}

  這些更改為使用者提供了兩個選擇元素,這兩個元素是使用本章前面建立的 SelectFilter 元件顯示的。這些新元素允許使用者按升序和降序對資料排序,並選擇用於突出顯示錶行的值。父元件提供了額外引數,為模板元件函式提供功能,來選擇用於排序和突出顯示的屬性,在 Blazor 資料夾的 PeopleList.razor 檔案中配置模板元件特性:

<TableTemplate RowType="Person" RowData="People"
    Highlight="@(p=>p.Location.City)" SortDirection="@(p=>p.Surname)">
    ......
</TableTemplate>

  Highlight 屬性為模板元件提供一個函式,該函式選擇用於突出顯示錶行的屬性,SortDirection 屬性提供一個函式,該函式選擇用於排序的屬性。要檢視效果,請求 http://localhost:5000/controllers。響應將包含新的 select 元素,這些元素可用於重改排序順定或選擇要過濾的城市。
3.重用通用模板元件
  新增到模板元件的特性都依賴於泛型型別引數,允許元件修改它呈現的內容,而不繫結到特定的類。結果是一個元件,可用於在需要表的地方顯示、排序和突出顯示任何資料型別。給 Blazor資料夾新增一個名為 DepartmmentList.razor 的 Razor 元件:

<TableTemplate RowType="Department" RowData="Departments"
               Highlight="@(d => d.Name)" SortDirection="@(d => d.Name)">
    <Header>
    <tr><th>ID</th><th>Name</th><th>People</th><th>Locations</th></tr>
    </Header>
    <RowTemplate Context="d">
        <td>@d.Departmentid</td>
        <td>@d.Name</td>
        <td>@(String.Join(", ", d.People.Select(p => p.Surname)))</td>
        <td>
            @(String.Join(", ",
                d.People.Select(p => p.Location.City).Distinct()))
        </td>
    </RowTemplate>
</TableTemplate>

@code
{
    [Inject]
    public DataContext Context { get; set; }

    public IEnumerable<Department> Departments =>
        Context.Departments.Include(d => d.People).ThenInclude(p => p.Location);
}

  TableTemplate 元件用於向使用者展示資料庫中的 Department 物件列表,以及相關的 Person 和Location 物件的詳細資訊,這些資訊透過 Entity Framework Core Include 和 Thenlnclude 方法進行查詢。更改在 Pages 資料夾的 Blazor.cshtml 檔案中元件:

@page "/pages/blazor"
<h4 class="bg-primary text-white text-center p-2">Departmments</h4>
<component type="typeof(MyAdvanced.Blazor.DepartmmentList)" render-mode="Server" />

  請求 http://localhost:5000/pages/blazor。使用模板化元件顯示響應。

3.3 級聯引數

  Blazor 透過支援級聯引數,元件提供的資料值可直接用於它的任何後代,而不由中間元件傳遞。級聯引數是使用 CascadingValue 元件定義的,該元件用於包裝部分內容。在 Blazor 資料夾的 DepartmentList.razor 檔案中建立級聯引數:

<CascadingValue Name="BgTheme" Value="Theme" IsFixed="false">
    ......
</CascadingValue>

<SelectFilter Title="@("Theme")" Values="Themes" @bind-SelectedValue="Theme" />

@code 
{
    [Inject]
    public DataContext Context { get; set; }
    public IEnumerable<Department> Departments => Context.Departments
            .Include(d => d.People).ThenInclude(p => p.Location);
    public string Theme { get; set; } = "info";
    public string[] Themes = new string[] { "primary", "info", "success" };
}

  CascadingValue 元素為它所包含的元件及其後代提供一個可用的值。Name 屬性指定引數的名稱,Value 屬性指定值,isFixed 屬性用於指定值是否會更改。程式碼清單中使用了 CascadingValue 元素來建立名為BgTheme 的級聯引數,它的值由 SelectFilter 元件的一個例項設定該元件向使用者提供了一組引導 CSS 主題名。
  在 Blazor 資料夾的 SelectFilter.razor 檔案中使用 CascadingParameter 屬性的元件直接接收級聯引數:

<div class="form-group p-2 bg-@Theme @TextColor()">
......
......
[CascadingParameter(Name = "BgTheme")]
public string Theme { get; set; }
public string TextColor() => Theme == null ? "" : "text-white";

  CascadingParameter 屬性的 Name 引數用於指定級聯引數的名稱。程式碼清單 DepartmentList.razor 中定義的 BgTheme 引數由程式碼清單 SelectFilter.razor 中的Theme屬性接收,並用於設定元件的背景。請求 http://localhost:5000/pages/blazor
  本例中使用了三個 SelectFilter 元件例項,但其中只有兩個在 Cascadingvalue 元素所包含的層次結構中。另一個例項在 Cascadingvalue 元素之外定義,不接收級聯值。

4 處理錯誤

4.1 處理連線錯誤

  Blazor 依賴於它在瀏覽器和 ASP.NETCore 伺服器之間的持久 HTTP連線。當連線中斷時,應用程式無法正常工作,並顯示一個模態錯誤訊息,阻止使用者與元件互動。Blazor 允許透過定義具有特定 id 的元素來定製連線錯誤,如程式碼清單所示。在 Pages 資料夾的 Blazor.cshtml 檔案中定義連線錯誤元素:

<link href="~/lib/connectionerrors.css" rel="stylesheet" />
    
<div id="components-reconnect-modal" 
    class="h4 bg-dark text-white text-center my-2 p-2 components-reconnect-hide">
    Blazor Connection Lost
    <div class="reconnect">
        Trying to reconnect...
    </div>
    <div class="failed">
        Reconnection Failed.
        <button class="btn btn-light" onclick="window.Blazor.reconnect()">
            Reconnect
        </button>
    </div>
    <div class="rejected">
        Reconnection Rejected.
        <button class="btn btn-light" onclick="location.reload()">
            Reload
        </button>
    </div>
</div>

  自定義錯誤元素的id 屬性必須是 componentes-reconnect-modal。當出現連線錯誤時,Blazor將查詢此元素並將其新增到表中描述的四個類中的一個。

名稱 描述
components-reconnect-show 當連線已經丟失且 Blazor 正在嘗試重連線時,將元素新增到這個類中。錯誤訊息應該顯示
給使用者,並且應該防止與 Blazor 內容的互動
components-reconnect-hide 如果重新建立連線,則將元素新增到該類中。錯誤訊息應該隱藏,並且應該允許互動
components-reconnect-failed 如果 Blazor 重連線失敗,則將該元素新增到該類中。使用者可以看到一個按鈕,該按鈕呼叫
window.Blazor.reconnect()來再次嘗試重新連線
components-reconnect-rejected 如果 Blazor 能夠到達伺服器,但使用者的連線狀態已經丟失,則將元素新增到這個類中,這
通常發生在伺服器重新啟動時。使用者可以看到一個按鈕,該按鈕呼叫 location.reload()來重
新載入應用程式並重試

  元素最初沒有新增到這些類中的任何一個,因此將其顯式新增到 components-reconnect-hide 類中,以便在出現問題之前隱藏它。
  希望針對重連線期間可能出現的每個條件向使用者顯示特定的訊息。為此,新增了為每個條件顯示訊息的元素。為了管理它們的可見性,將一個名為connectionError.css 的 CSS 樣式表新增到wwwroot 資料夾中,並用它定義如程式碼清單所示的樣式。
  wwwroot 資料夾中 connectionError.css 檔案的內容:

#components-reconnect-modal {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1000;
    overflow: hidden;
    opacity: 0.9;
}

.components-reconnect-hide {
    display: none;
}

.components-reconnect-show {
    display: block;
}

    .components-reconnect-show > .reconnect {
        display: block;
    }

    .components-reconnect-show > .failed,
    .components-reconnect-show > .rejected {
        display: none;
    }

.components-reconnect-failed > .failed {
    display: block;
}

.components-reconnect-failed > .reconnect,
.components-reconnect-failed > .rejected {
    display: none;
}

.components-reconnect-rejected > .rejected {
    display: block;
}

.components-reconnect-rejected > .reconnect,
.components-reconnect-rejected > .failed {
    display: none;
}

  這些樣式將 componentes-reconnect-modal 元素顯示為一個模態項,其可見性由 componentesrecomnecthide 和 componentes-reconnect-show 類決定。
  要檢視效果,請求http://localhost:5000/pages/blazor。等到顯示元件,就停止伺服器。當 Blazor 嘗試重新連線時,將看到一個初始錯誤訊息。幾秒鐘後,將看到指示重連線失敗的訊息。

4.2 處理未捕獲的應用程式錯誤

  Blazor 不能很好地響應未捕獲的應用程式錯誤,這些錯誤幾乎總是被視為終端。要檢視預設的錯誤行為,請將程式碼清單中所示的元素新增到 DepartmentList 元件。
  在 Blazor 資料夾的 DepartmentList.razor 檔案中新增元素:

<button class="btn btn-danger" @onclick="@(()=> throw new Exception())">Error</button>

  重啟 ASP.NET Core,請求 http://localhost:5000/pages/blazor,然後單擊 Error 按鈕。在瀏覽器中並無明顯變化,但當單擊按鈕時在伺服器上丟擲的異常被證明是致命的;使用者仍然可以使用 select 元素選擇值,因為這些是由瀏覽器顯示的,但響應選擇的事件處理程式不再有效,應用程式本質上是停止了。
  當出現未處理的應用程式錯誤時,Blazor會查詢 id 為 Blazor-error-ui 的元素,並設定其 CSS顯示屬性以阻止。程式碼清單將具有此 id 的元素新增到 Blazorcshtml 檔案,該檔案樣式化為顯示有用的訊息。

<div id="blazor-error-ui"
     class="text-center bg-danger h6 text-white p-2 fixed-top w-100"
     style="display:none">
    An error has occurred. This application will not respond until reloaded.
    <button class="btn btn-sm btn-primary" onclick="location.reload()">
        Reload
    </button>
</div>

  單擊 Error 按鈕,當顯示元素時,使用者將看到一個警告和一個重新載入瀏覽器的按鈕。

相關文章