共用方式為


ASP.NET Core Blazor 樣板化元件

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

Robert Haken 提供

本文說明樣板化元件如何接受一或多個 UI 範本作為參數,然後將其用作元件轉譯邏輯的一部分。

樣板化元件

樣板化元件是接收一或多個 UI 範本作為參數的元件,可在元件的轉譯邏輯中使用。 藉由使用樣板化元件,您可建立更能重複使用的較高層級元件。 範例包含:

  • 資料表元件,可讓使用者指定資料表頁首、資料列和頁尾的範本。
  • 清單元件,可讓使用者指定範本來轉譯清單中的項目。
  • 導覽列元件,可讓使用者指定開始內容和導覽連結的範本。

樣板化元件是藉由指定類型為 RenderFragmentRenderFragment<TValue> 的一或多個元件參數來定義。 轉譯片段代表要轉譯的 UI 區段。 RenderFragment<TValue> 採用可在叫用轉譯片段時指定的型別參數。

注意

如需 RenderFragment 的詳細資訊,請參閱 ASP.NET Core Razor 元件

樣板化元件通常會為泛型類型,如下列 TemplatedNavBar 元件 (TemplatedNavBar.razor) 示範。 下列範例中的泛型類型 (<T>) 可用來轉譯 IReadOnlyList<T> 值,在此案例中,這是顯示具有寵物詳細資料元件連結之導覽列之元件的寵物清單。

TemplatedNavBar.razor

@typeparam TItem

<nav class="navbar navbar-expand navbar-light bg-light">
    <div class="container justify-content-start">
        @StartContent
        <div class="navbar-nav">
            @foreach (var item in Items)
            {
                @ItemTemplate(item)
            }
        </div>
    </div>
</nav>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<nav class="navbar navbar-expand navbar-light bg-light">
    <div class="container justify-content-start">
        @StartContent
        <div class="navbar-nav">
            @foreach (var item in Items)
            {
                @ItemTemplate(item)
            }
        </div>
    </div>
</nav>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<nav class="navbar navbar-expand navbar-light bg-light">
    <div class="container justify-content-start">
        @StartContent
        <div class="navbar-nav">
            @foreach (var item in Items)
            {
                @ItemTemplate(item)
            }
        </div>
    </div>
</nav>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<nav class="navbar navbar-expand navbar-light bg-light">
    <div class="container justify-content-start">
        @StartContent
        <div class="navbar-nav">
            @foreach (var item in Items)
            {
                @ItemTemplate(item)
            }
        </div>
    </div>
</nav>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<nav class="navbar navbar-expand navbar-light bg-light">
    <div class="container justify-content-start">
        @StartContent
        <div class="navbar-nav">
            @foreach (var item in Items)
            {
                @ItemTemplate(item)
            }
        </div>
    </div>
</nav>

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

    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default;

    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; } = default;
}
@typeparam TItem

<nav class="navbar navbar-expand navbar-light bg-light">
    <div class="container justify-content-start">
        @StartContent
        <div class="navbar-nav">
            @foreach (var item in Items)
            {
                @ItemTemplate(item)
            }
        </div>
    </div>
</nav>

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

    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default;

    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; } = default;
}

使用樣板化元件時,可以使用符合參數名稱的子項目來指定範本參數。 在下列範例中,<StartContent>...</StartContent><ItemTemplate>...</ItemTemplate>TemplatedNavBar 元件的 StartContentItemTemplate 提供 RenderFragment<TValue> 範本。

當您想要指定隱含子系內容的內容參數名稱時,請在元件元素上指定 Context 屬性 (不含任何包裝子矽元素)。 在下列範例中,Context 屬性會出現在 TemplatedNavBar 元素上,並套用至所有 RenderFragment<TValue> 範本參數。

Pets1.razor

@page "/pets-1"

<PageTitle>Pets 1</PageTitle>

<h1>Pets Example 1</h1>

<TemplatedNavBar Items="pets" Context="pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-1")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-1"

<PageTitle>Pets 1</PageTitle>

<h1>Pets Example 1</h1>

<TemplatedNavBar Items="pets" Context="pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-1")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-1"

<PageTitle>Pets 1</PageTitle>

<h1>Pets Example 1</h1>

<TemplatedNavBar Items="pets" Context="pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-1")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-1"

<PageTitle>Pets 1</PageTitle>

<h1>Pets Example 1</h1>

<TemplatedNavBar Items="pets" Context="pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-1"

<h1>Pets Example 1</h1>

<TemplatedNavBar Items="pets" Context="pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}
@page "/pets-1"

<h1>Pets Example 1</h1>

<TemplatedNavBar Items="pets" Context="pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}

或者,您可使用 RenderFragment<TValue> 子系元素上的 Context 屬性來變更參數名稱。 在下列範例中,Context 是設定於 ItemTemplate,而不是 TemplatedNavBar

Pets2.razor

@page "/pets-2"

<PageTitle>Pets 2</PageTitle>

<h1>Pets Example 2</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate Context="pet">
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-2")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-2"

<PageTitle>Pets 2</PageTitle>

<h1>Pets Example 2</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate Context="pet">
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-2")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-2"

<PageTitle>Pets 2</PageTitle>

<h1>Pets Example 2</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate Context="pet">
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-2")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-2"

<PageTitle>Pets 2</PageTitle>

<h1>Pets Example 2</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate Context="pet">
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-2"

<h1>Pets Example 2</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate Context="pet">
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}
@page "/pets-2"

<h1>Pets Example 2</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate Context="pet">
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}

RenderFragment<TValue> 類型的元件引數具有名為 context 的隱含參數可供使用。 在下列範例中,未設定 Context@context.{PROPERTY} 提供寵物值給範本,其中 {PROPERTY}Pet 屬性:

Pets3.razor

@page "/pets-3"

<PageTitle>Pets 3</PageTitle>

<h1>Pets Example 3</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{context.PetId}?ReturnUrl=%2Fpets-3")" class="nav-link">
            @context.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-3"

<PageTitle>Pets 3</PageTitle>

<h1>Pets Example 3</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{context.PetId}?ReturnUrl=%2Fpets-3")" class="nav-link">
            @context.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-3"

<PageTitle>Pets 3</PageTitle>

<h1>Pets Example 3</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{context.PetId}?ReturnUrl=%2Fpets-3")" class="nav-link">
            @context.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-3"

<PageTitle>Pets 3</PageTitle>

<h1>Pets Example 3</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{context.PetId}")" class="nav-link">
            @context.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-3"

<h1>Pets Example 3</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{context.PetId}")" class="nav-link">
            @context.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}
@page "/pets-3"

<h1>Pets Example 3</h1>

<TemplatedNavBar Items="pets">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{context.PetId}")" class="nav-link">
            @context.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}

使用泛型類型的元件時,可能的話會推斷類型參數。 不過,您可使用名稱符合類型參數的屬性來明確指定類型,這就是上述範例中的 TItem

Pets4.razor

@page "/pets-4"

<PageTitle>Pets 4</PageTitle>

<h1>Pets Example 4</h1>

<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-4")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-4"

<PageTitle>Pets 4</PageTitle>

<h1>Pets Example 4</h1>

<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-4")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-4"

<PageTitle>Pets 4</PageTitle>

<h1>Pets Example 4</h1>

<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}?ReturnUrl=%2Fpets-4")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-4"

<PageTitle>Pets 4</PageTitle>

<h1>Pets Example 4</h1>

<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-4"

<h1>Pets Example 4</h1>

<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}
@page "/pets-4"

<h1>Pets Example 4</h1>

<TemplatedNavBar Items="pets" Context="pet" TItem="Pet">
    <StartContent>
        <a href="/" class="navbar-brand">PetsApp</a>
    </StartContent>
    <ItemTemplate>
        <NavLink href="@($"/pet-detail/{pet.PetId}")" class="nav-link">
            @pet.Name
        </NavLink>
    </ItemTemplate>
</TemplatedNavBar>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}

TemplatedNavBar 元件 (TemplatedNavBar.razor) 中提供的範例假設初始轉譯之後,Items 集合不會變更;或者,如果確實變更,則不需要維護 ItemTemplate 中使用的元件/元素狀態。 如需無法預期這類使用方式的樣板化元件,請參閱保留與 @key 的關聯性一節。

使用 @key 保留關聯性

範本化元件通常用來轉譯項目的集合,例如資料表或清單。 在這類一般情況下,我們無法假設使用者會避免項目範本定義中的具狀態元件/元素,或不會對 Items 集合進行其他變更。 針對這類範本元件,必須保留與 @key 指示詞屬性的關聯性。

注意

如需 @key 指示詞屬性的詳細資訊,請參閱在 ASP.NET Core Blazor 中保留元素、元件和模型關聯性

下列 TableTemplate 元件 (TableTemplate.razor) 示範範本元件,該元件會保留與 @key的關聯性。

TableTemplate.razor

@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr @key="@item">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> RowTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr @key="@item">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> RowTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr @key="@item">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> RowTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr @key="@item">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

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

    [Parameter, EditorRequired]
    public RenderFragment<TItem> RowTemplate { get; set; } = default!;

    [Parameter, EditorRequired]
    public IReadOnlyList<TItem> Items { get; set; } = default!;
}
@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr @key="@item">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

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

    [Parameter]
    public RenderFragment<TItem> RowTemplate { get; set; } = default;

    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; } = default;
}
@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr @key="@item">@RowTemplate(item)</tr>
        }
    </tbody>
</table>

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

    [Parameter]
    public RenderFragment<TItem> RowTemplate { get; set; } = default;

    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; } = default;
}

請考慮下列 Pets5 元件 (Pets5.razor),其示範鍵入資料以保留模型關聯性的重要性。 在元件中,每個在 OnAfterRenderAsync 中新增寵物的反覆項目都會使 Blazor 重新呈現 TableTemplate 元件。

Pets5.razor

@page "/pets-5"

<PageTitle>Pets 5</PageTitle>

<h1>Pets Example 5</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td><input value="@pet.PetId" /></td>
        <td><input value="@pet.Name" /></td>
    </RowTemplate>
</TableTemplate>


@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Insert new pet every 5 seconds
        if (pets.Count < 10)
        {
            await Task.Delay(5000);
            pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
            StateHasChanged();
        }
    }

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-5"

<PageTitle>Pets 5</PageTitle>

<h1>Pets Example 5</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td><input value="@pet.PetId" /></td>
        <td><input value="@pet.Name" /></td>
    </RowTemplate>
</TableTemplate>


@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Insert new pet every 5 seconds
        if (pets.Count < 10)
        {
            await Task.Delay(5000);
            pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
            StateHasChanged();
        }
    }

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-5"

<PageTitle>Pets 5</PageTitle>

<h1>Pets Example 5</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td><input value="@pet.PetId" /></td>
        <td><input value="@pet.Name" /></td>
    </RowTemplate>
</TableTemplate>


@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Insert new pet every 5 seconds
        if (pets.Count < 10)
        {
            await Task.Delay(5000);
            pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
            StateHasChanged();
        }
    }

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-5"

<PageTitle>Pets 5</PageTitle>

<h1>Pets Example 5</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td><input value="@pet.PetId" /></td>
        <td><input value="@pet.Name" /></td>
    </RowTemplate>
</TableTemplate>


@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Insert new pet every 5 seconds
        if (pets.Count < 10)
        {
            await Task.Delay(5000);
            pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
            StateHasChanged();
        }
    }

    private class Pet
    {
        public int PetId { get; set; }
        public string? Name { get; set; }
    }
}
@page "/pets-5"

<h1>Pets Example 5</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td><input value="@pet.PetId" /></td>
        <td><input value="@pet.Name" /></td>
    </RowTemplate>
</TableTemplate>


@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Insert new pet every 5 seconds
        if (pets.Count < 10)
        {
            await Task.Delay(5000);
            pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
            StateHasChanged();
        }
    }

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}
@page "/pets-5"

<h1>Pets Example 5</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td><input value="@pet.PetId" /></td>
        <td><input value="@pet.Name" /></td>
    </RowTemplate>
</TableTemplate>


@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 1, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 2, Name = "Salem Saberhagen" },
        new Pet { PetId = 3, Name = "K-9" }
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // Insert new pet every 5 seconds
        if (pets.Count < 10)
        {
            await Task.Delay(5000);
            pets.Insert(0, new Pet() { PetId = pets.Count + 1, Name = "<new pet>" });
            StateHasChanged();
        }
    }

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}

此示範可讓您:

  • 從數個已轉譯的資料列中選取 <input>
  • 請研究頁面焦點在寵物集合自動成長的過程中會有怎樣的行為。

如果沒有在 TableTemplate 元件中使用 @key 指示詞屬性,頁面的焦點會維持在資料表的相同索引位置 (列) 上,導致每次新增寵物時焦點都會轉移。 若要示範這一點,請移除 @key 指示詞屬性和值、重新啟動應用程式,並嘗試在新增項目時修改域值。

其他資源