2. 使用Blazor中的模板功能

1 概述

可以使用参数将数据传递到组件中。 参数定义组件的公共 API。 使用参数将数据传递到组件的语法与在标准 HTML 元素上定义属性相同,即使用键值对。key 是参数名称,value 是您希望传递给组件的数据。

2 生命周期

常用的生命周期为

  1. OnInitialized/OnInitializedAsync:仅仅运行一次
  2. OnParametersSet/OnParametersSetAsync:每次参数变化时执行
  3. OnAfterRender/OnAfterRenderAsync:主要用于执行JavaScript和其他的DOM操作
    image

3 父子组件

  • 传参
    父组件->子组件:使用组件普通参数
    子组件->父组件:使用组件事件参数

3.1 父传子

3.1.1 在Home文件夹下创建TrialDetails组件

  • IsOpen属性用于侧边栏的显示
<div class="offcanvas @(IsOpen ? "show" : "") offcanvas-end" tabindex="-1" id="trailDetail" aria-labelledby="trailDetailLabel">
    <div class="offcanvas-header">
        <h5 class="offcanvas-title" id="offcanvasLabel">Trail Detail</h5>
        <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
    </div>
    <div class="offcanvas-body p-0">
       @if (SelectedTrail is not null)
       {
           <div class="card shadow p-0">
               <img src="@SelectedTrail.Image" class="card-img-top" alt="@SelectedTrail.Name">
               <div class="card-body">
                   @* 标题 *@
                   <h5 class="card-tiele">@SelectedTrail.Name</h5>
                   @* 位置 *@
                   <h6 class="card-subtitle mb-5 text-muted">
                       <span class="bi bi-geo">@SelectedTrail.Location</span>
                   </h6>
                   @* 描述 *@
                   <div class="d-flex justify-content-between">
                       @* 时间 *@
                       <span>
                           <span class="bi bi-clock mr-2"></span>
                           @SelectedTrail.TimeFormatted
                       </span>
                       @* 路程 *@
                       <span>
                           <span class="bi bi-infinity mr-2"></span>
                           @SelectedTrail.Length km
                       </span>
                   </div>
                   @* 详情介绍 *@
                   <p class="mt-4">@SelectedTrail.Description</p>
               </div>
           </div>
       }
    </div>
</div>

@code {
    [Parameter,EditorRequired]
    public Trail? SelectedTrail { get; set; }
    [Parameter,EditorRequired]
    public bool IsOpen { get; set; }
}

3.2 子传父

3.2.1 在TrailCard组件中添加事件

我们需要在Home页面中的TrailCard组件中将数据传出来作为选择的Trail,所以需要在TrailCard中定义事件。

注意:
Blazor 中的事件不是 .NET 意义上的真实事件; 他们只是代表。 这适
用于 Blazor 的内置 DOM 事件系统和开发人员使用组件参数定义的事件。这
也意味着在任何给定时间,任何给定事件只能有一个处理程序。

我们可以使用名为 EventCallback 的 TrailCard 上的不同类型来定义事件。通过对事件使用此类型,Blazor 将自动在处理事件的组件上调用 StateHasChanged,无需手动调用它。

<div class="col-3 card shadow m-5 p-0">
    <img src="@Trail.Image" class="card-img-top" alt="@Trail.Name">
    <div class="card-body">
        @* 标题 *@
        <h5 class="card-tiele">@Trail.Name</h5>
        @* 位置 *@
        <h6 class="card-subtitle mb-5 text-muted">
            <span class="bi bi-geo">@Trail.Location</span>
        </h6>
        @* 描述 *@
        <div class="d-flex justify-content-between">
            @* 时间 *@
            <span>
                <span class="bi bi-clock mr-2"></span>
                @Trail.TimeFormatted
            </span>
            @* 路程 *@
            <span>
                <span class="bi bi-infinity mr-2"></span>
                @Trail.Length km
            </span>
        </div>
        @* 采用EventCallback后,null检测不再需要。同时支持异步回调。 *@
        <button class="btn btn-primary" title="View" @onclick="@(() => OnSelected.InvokeAsync(Trail))">
            <i class="bi bi-binoculars"></i>
        </button>
    </div>
</div>

@code
{
    [Parameter, EditorRequired] public Trail Trail { get; set; }
    [Parameter,EditorRequired]
    public EventCallback<Trail> OnSelected { get; set; }
}

3.2.2 在Home页面中触发事件

为了关闭侧边栏,这里添加了一个遮罩层

@page "/"
@inject HttpClient Http
<PageTitle>Blazing Trails</PageTitle>


@if (_trails == null)
{
    <p>Loading...</p>
}
else
{
    <button class="btn btn-primary" type="button" 
            data-bs-toggle="offcanvas" 
            data-bs-target="#trailDetail" 
            aria-controls="trailDetail">
        Button with data-bs-target
    </button>
    <TrailDetails SelectedTrail="_selectedTrail" IsOpen="_isDetailOpen"></TrailDetails>
    <div class="container">
        <div class="row">
            @foreach (var trail in _trails)
            {
                <TrailCard Trail="trail" OnSelected="HandelTrailSelected" />
            }
        </div>
    </div>
}

@if (_isDetailOpen)
{
    <div class="offcanvas-backdrop fade show" 
         @onclick="CloseSidebar"></div>
}
@code {
    private IEnumerable<Trail>? _trails;
    private Trail? _selectedTrail;
    private bool _isDetailOpen = false;
    protected override async Task OnInitializedAsync()
    {
        try
        {
            _trails = await Http
                .GetFromJsonAsync<IEnumerable<Trail>>
                    ("trails/trail-data.json");
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"There was a problem loading trail data: {ex.Message}");
        }
    }

    private void HandelTrailSelected(Trail trail)
    {
        _selectedTrail = trail;
        _isDetailOpen =!_isDetailOpen;
    }

    private void CloseSidebar()
    {
        _isDetailOpen=false;
    }

}

3.2.3 在TrailDetials中完善Close按钮

  • 添加OnClosed事件用于在Home页面中触发关闭方法。
<button type="button" class="btn-close" @onclick="@(()=>OnClosed.InvokeAsync())"></button>
  • 在Home页面中触发
<TrailDetails SelectedTrail="_selectedTrail" IsOpen="_isDetailOpen" OnClosed="CloseSidebar"></TrailDetails>

3.3 最终效果

image

posted @ 2026-02-03 14:33  show2723  阅读(3)  评论(0)    收藏  举报