static void

Blazor Parameters

Passing parameters down to components uses the [Parameter] attribute on properties (just get/set, don't implement any code).

<div class="row">
    <div class="col">Event: @UserEvent.EventDetails</div>
    <div class="col"> @ChildContent </div>
</div>

@code {
    //a standard parameter
    //default! - the ! null forgiving operator is useful for initializing something that is always passed in
    [Parameter]
    public UserEvent UserEvent { getset; } = default!;
    
    //the body - basically the innerHtml
    [Parameter]
    public RenderFragment? ChildContent { getset; }

    //available to all nested components- you don't have to pass them down the hierarchy.
    [CascadingParameter]
    public User CurrentUser { getset; } = default!;
    //nb- for real, use Microsoft.AspNetCore.Components.WebAssembly.Authentication/<CascadingAuthenticationState>
}

Binding

For two way binding, the component must have a second xChanged parameter

[Parameter]
public Customer Customer { getset } = default!;

[Parameter]
public EventCallback<Customer> CustomerChanged { getset }

RenderFragment

You can have several RenderFragments but the calling components must specify the property name

<h3>User details</h3>
@ChildContent

<h3>User rights</h3>
@UserRights

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

    [Parameter]
    public RenderFragment? UserRights { get; set; }
}

Call it like this:

<User>
    <UserRights>
        No permissions
    </UserRights>
    <ChildContent>
        Bob
    </ChildContent>
</User>

ChildContent is a magic name- you can omit the property tags (no <ChildContent>)

<User>
    Bob
</User>

Foreach loops are not good with RenderFragments, so use a for with counter.

@for (var i = 0; i < Users.Count; i++)
{
    var user = Users[i];
    <UserComponent User="@user">
        <NavLink href="@($"UserEdit/{user.Id}")">Edit</NavLink>
    </UserComponent>
}

RenderFragments can be generic for templates

@typeparam TItem
@if(Header is not null)
{
    @Header
}
@foreach (var item in Items)
{
    @ItemTemplate(item)
}
@if (Footer is not null)
{
    @Footer
}
@code {
    [Parameter]
    [EditorRequired]
    public required IEnumerable<TItem> Items { getset; }
    [Parameter]
    public RenderFragment? Header { getset; } = default;  
    [Parameter]
    public RenderFragment? Footer { getset; } = default;
    [Parameter]
    [EditorRequired]
    public required RenderFragment<TItem> ItemTemplate { getset; }
}

Use it by specifying Items (your collection)- you can rename the default name from "context".

<ItemList Items="Curries">
    <Header>
        <h1>The spice must flow</h1>
    </Header>
    <ItemTemplate Context="curry">
        <div class="row">
            <div class="col">
                @curry.Name
            </div>
        </div>
    </ItemTemplate>
</ItemList>

CascadingValue

Wrap cascading values in a parent component or layout.

<CascadingValue Value="CurrentUser">
    @Body
</CascadingValue>

@ref

Grab a reference to a child razor component

<Basket @bind-State="@State" @ref="_basket" />
@code
{
    private Basket _basket = default!;

Attribute splatting (CaptureUnmatchedValues)

You can copy arbitrary attributes- but this is not great for performance.

Using component

<BasketViewer State="State" class="small" title="hello"></BasketViewer>

Component

<div @attributes="InputAttributes">
</div>

@code {
    [Parameter(CaptureUnmatchedValues = true)]  
    public Dictionary<stringobject>? InputAttributes { getset; }

    [Parameter
    [EditorRequired]
    public required State State { getset; }
}