Parametere · binding · event-handling

Komponenter og events

Byg UI af genbrugelige komponenter med parametre, two-way binding og EventCallback — parent/child-flow som Pokémon-liste der vælger detail.

Læringsmål

Hvornår opdele i komponenter? Når samme UI gentages (Pokémon-kort, LoL-række) eller når parent skal koordinere state (valgt id + detail-panel) uden 200 linjer Razor i én fil.

Tjek dig selv før du går videre

  • Du kan forklare EventCallback vs Action med parent/child-eksempel.
  • Du ved hvorfor child ikke må mutere [Parameter] direkte.
  • Du har brugt bind-lab og forstår two-way flow.

Komponenter er Blazors byggeklodser. En fil = én komponent (typisk). Parent sender data ned; child notifier op via callbacks.

Two-way binding preview

Skriv trænernavn — preview opdateres som ved @bind.

Valgt træner: —

Parametre og EventCallback

Parent ↔ child

Klik kortet eller Afspil — se EventCallback gå op, mens parametre går ned.

ParentPokemonList · selectedId
parametre ↓
ChildPokemonCard · klik mig

@* PokemonCard.razor *@
<div class="card" @onclick="OnSelect">
  <h3>@Name</h3>
  <p>#@Id</p>
</div>

@code {
  [Parameter] public string Name { get; set; } = "";
  [Parameter] public int Id { get; set; }
  [Parameter] public EventCallback<int> Selected { get; set; }

  private async Task OnSelect() => await Selected.InvokeAsync(Id);
}
@* PokemonList.razor (parent) *@
@foreach (var p in mons)
{
  <PokemonCard Name="@p.Name" Id="@p.Id" Selected="OnPick" />
}
<p>Valgt id: @selectedId</p>

@code {
  int? selectedId;
  List<Mon> mons = ...;

  private Task OnPick(int id)
  {
    selectedId = id;
    return Task.CompletedTask;
  }

  record Mon(string Name, int Id);
}

EventCallback vs EventCallback<T>: Uden payload når parent bare skal reagere («luk modal»). Med int / string når child sender id (Pokémon #25, champion «Ahri»). Parent ejer konsekvensen — fx await Lol.GetChampionAsync(id).

Cascading parameters (kort): når mange børn skal kende samme «valgt træner» eller tema uden at hver PokemonCard får 10 parametre — se livscyklus-lektionen; events opad er stadig primary for valg-handlinger.

@bind dybere

@bind-Value="text" er syntaks-sukker for value + ValueChanged + ValueExpression. Til delayed API-søgning kan I bruge @bind-Value:after="Search" (.NET 8+) så søgning kører efter opdatering.

<input @bind="filter" @bind:event="oninput" />

@bind:event="oninput" opdaterer ved hvert tastetryk (default er often onchange ved blur).

Hvornår @bind-Value:after? LoL-filter der kalder OpgaverAPI efter tekstfelt opdateres — undgå at kalde API inde i set på property; lad Blazor færdiggøre bind, derefter søg.

<input @bind="filter" @bind:event="oninput" @bind:after="SearchAsync" />

C# vs JavaScript

Faldgrube: At mutere child’s [Parameter] direkte i child — one-way flow ned, events op. Parent ejer sandheden om selectedId.

Genbrug i større UI

Samme ChampionRow i LoL-liste og i «favoritter»-sidebar — kun parametre og callbacks skifter.

Trin ved refaktorering: udtræk markup → tilføj [Parameter] → erstat hardcoded klik med EventCallback → parent injecter service og henter API-data.

Quiz 1

Hvordan sender child «jeg blev valgt» til parent?

Quiz 2

Hvad gør @bind-Value i praksis?

EventCallback vs Action

Hvorfor det betyder noget: Når ChampionRow melder valg opad, skal parent opdatere selectedId og måske hente detail fra OpgaverAPI. EventCallback sørger for korrekt re-render og kan await’es; Action gør ikke.

// Svagt i Blazor-komponenter
[Parameter] public Action<int> OnPick { get; set; } = _ => { };

// Korrekt — async-friendly og render-aware
[Parameter] public EventCallback<int> OnPick { get; set; }
private Task PickAsync() => OnPick.InvokeAsync(ChampionId);

Parent kan await’e og hente LoL-detail før UI vises:

private async Task OnChampionPicked(int id)
{
    selectedId = id;
    detail = await Lol.GetChampionAsync(id);
}

Sådan fejler det i praksis (@bind): Parent binder @bind-SelectedId på child, men child sætter også SelectedId = id i onclick — state kæmper, UI «hopper tilbage». Parent ejer bound property; child bruger EventCallback eller lader @bind håndtere Changed-kanalen alene.

@* Anti-pattern: child skriver til parent's parameter *@
@code {
  [Parameter] public int SelectedId { get; set; }
  void BadClick(int id) => SelectedId = id; // undgå — brug EventCallback eller @bind-SelectedId
}

Mini-opgave

ChampionRow med EventCallback<string>; parent holder selectedId og henter detail fra OpgaverAPI ved valg.

Quiz 3

Notify parent om valg?

Læs hos Microsoft

Prøv med API

  • Byg PokemonCard og genbrug på liste fra Pokémon-demo.
  • Parent viser detail-panel når EventCallback fyrer.
16476 #1016476 #1116476 #1216476 #1316476 #1416475 #216475 #516475 #7

Baseret på MAGS Blazor-wiki og Microsoft Learn — Blazor.