Templated Views & Slots

Build your own Tag Helpers whose markup can be overridden, and pass content into named slots

Writing Tag Helpers in ASP.NET Core is very powerful, but you have to generate the rendered HTML in C# code. This can make it cumbersome as most developers are used to working with Razor Pages and Partials. StellarAdmin Tag Helpers adds a few helpers to make it easier for you to create your own Tag Helpers while still using the power of the Razor rendering engine.

  • Templated Tag Helpers allow you to use Razor Views from your own Tag Helpers, drastically simplifying the process of developing your own Tag Helpers.
  • Named slots allows you to easily pass content to a specific section of a Tag Helpers. A caller can pass content into specific spots of a Tag Helper's output with <sa-slot-content>, which the markup renders through <sa-slot-outlet>.

Templated Tag Helpers

If you want to create your own Tag Helpers while using Razor partials to design the layout, you can inherit your Tag Helper from StellarAdminTemplatedTagHelperBase and return the name of the partial in the ViewName property.

[HtmlTargetElement("docs-trip-card")]
public class TripCardTagHelper : StellarAdminTemplatedTagHelperBase
{
    [HtmlAttributeName("booking")]
    public Booking? Booking { get; set; }

    protected override string ViewName => "_TripCard";

    public TripCardTagHelper(ICompositeViewEngine viewEngine)
        : base(viewEngine) { }

    protected override object? GetViewModel()
    {
        return Booking;
    }
}

The markup lives in Pages/Shared/_TripCard.cshtml.

@model DocsSamples.Booking

<sa-card class="w-full max-w-sm">
    <sa-card-header>
        <sa-card-title>@Model.Destination</sa-card-title>
        <sa-card-description>Booking @Model.Id</sa-card-description>
    </sa-card-header>
    <sa-card-content>
        <div class="flex items-center justify-between text-sm">
            <sa-badge variant="BadgeVariant.Outline">@Model.Status</sa-badge>
            <span class="font-medium">@Model.Amount.ToString("C")</span>
        </div>
    </sa-card-content>
</sa-card>

Using the Tag Helper is then a single element:

Theme
<docs-trip-card booking="StaticData.Bookings[0]"/>

Swapping the view per instance

Every templated Tag Helper accepts a view attribute that allows you to overrides the view for that instance, so the same Tag Helper can render through a different view where needed.

Theme
<docs-trip-card booking="StaticData.Bookings[1]" view="_TripCardCompact"/>

Slot outlets

A templated Tag Helper's view can declare slots that allows the user to specify the content for a slot when using the Tag Helper. The view renders <sa-slot-outlet name="..."> where content should go. The child content of <sa-slot-outlet> is used as fallback content when the caller does not supply content for the slot.

<sa-card-footer>
    <sa-slot-outlet name="actions">
        <sa-button size="ButtonSize.Small" variant="ButtonVariant.Outline">View details</sa-button>
    </sa-slot-outlet>
</sa-card-footer>

The caller fills the slot by placing <sa-slot-content> with the matching name among the Tag Helper's children.

Theme
<docs-booking-card booking="StaticData.Bookings[2]">
    <sa-slot-content name="actions">
        <sa-group gap="GroupGap.Small">
            <sa-button size="ButtonSize.Small">Check in</sa-button>
            <sa-button size="ButtonSize.Small" variant="ButtonVariant.Outline">Cancel trip</sa-button>
        </sa-group>
    </sa-slot-content>
</docs-booking-card>

Fallback content

When the caller leaves the slot unfilled, the outlet renders its fallback children instead.

Theme
<docs-booking-card booking="StaticData.Bookings[3]"/>

Named slots in any Tag Helper

Named slots are not limited to templated Tag Helpers - any StellarAdmin Tag Helper can host them. The host executes its child content (which registers the <sa-slot-content> children with the host) and reads slots back with TryGetNamedSlot, rendering them wherever it chooses:

[HtmlTargetElement("docs-booking-summary")]
public class BookingSummaryTagHelper : StellarAdminTagHelperBase
{
    [HtmlAttributeName("booking")]
    public Booking? Booking { get; set; }

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var childContent = await output.GetChildContentAsync();

        output.TagName = "div";

        // Header row, with the "actions" slot rendered at the end when filled
        if (TryGetNamedSlot("actions", out var actions))
        {
            output.Content.AppendHtml(actions);
        }

        // The remaining children become the body
        output.Content.AppendHtml(childContent);
    }
}

The caller mixes slot content and regular children freely — the <sa-slot-content> element assigns itself to the nearest StellarAdmin ancestor and renders nothing in place:

Theme
<docs-booking-summary booking="StaticData.Bookings[0]">
    <sa-slot-content name="actions">
        <sa-button size="ButtonSize.ExtraSmall" variant="ButtonVariant.Outline">Edit</sa-button>
    </sa-slot-content>
    <p>Two travelers, departing 12 October. Payment received in full.</p>
</docs-booking-summary>

API Reference

<sa-slot-content>

Assigns its child content to a named slot on the nearest StellarAdmin ancestor Tag Helper and renders nothing in place. Assigning the same slot name twice on the same host is an error.

Prop

Type

<sa-slot-outlet>

Renders the content of the named slot on the hosting Tag Helper. When the slot is unfilled — or the view renders as a plain partial, outside a templated Tag Helper — it renders its own child content as the fallback instead. It renders no element of its own.

Prop

Type

On this page