Components

Introduction

Spark uses server-side rendereded Blazor (Blazor SSR) for it’s routing and frontend views.

Blazor SSR utilizes Razor components to split your markdown into re-usable chunks. These components also have the ability to contain logic specific to themselves and can even have url routes invoke them.

Razor components let you mix in HTML markup with C#, allowing you to quickly create dynamic components to use in your app.

@inject DeveloperService developerService
@code {
    private List<Developer> developers { get; set; }
    protected override void OnInitialized()
    {
        developers = await developerService.GetDevelopers();
    }
}

<ul>
    @foreach (var developer in developers)
    {
    <li>
        @developer.Name
    </li>
    }
</ul>

Use of HTMX

Spark integrates the popular Htmx library to enhance page navigations and form submissions.

A fresh Spark project will have the hx-boost="true" attribute set in the MainLayouts body tag. This will convert all regular HTML anchors and forms into AJAX requests so the browser doesn’t have to do a full page reload.

You can also utilize all of the other features HTMX provides. To learn more checkout out their documentation.

Components

Razor components are re-usable elements you can use anywhere in your application.

Your components should be created in the Pages/Components directory.

Creating Razor Components

To create a new Razor component, simply run the Spark make component command.

spark make component SomeComponent

A new Razor component named SomeComponent.razor will be created in the Pages/Components directory.

<p>My Component</p>

@code {

    protected override async Task OnInitializedAsync()
    {

    }
}

Rendering Components

To display a component, you may use a Razor component tag within another Razor component or page. Razor component tags are just the name of it’s source file.

<SomeComponent />

Passing Data To Components

You can pass data to a Razor component or page by using Component Parameters.

Component parameters are defined using public C# properties on the component class with the [Parameter] attribute.

For example, below is a razor component called ChildComponent.razor. It accepts a parameter of Title.

<h1>@Title</h1>

@code {
    [Parameter]
    public string Title { get; set; } = "";
}

Then, in a parent Razor component, you can render the child component and pass the Title parameter value.

<div>
    <SomeComponent Title="This is my title" />
</div>

@code {
}

In this example, the following markup would ultimately be rendered to the browser.

<div>
	<h1>This is my title</h1>
</div>

Child Content

You will often need to pass additional content to your razor components via a RenderFragment. RenderFragments can be HTML markup or even other Razor Components.

For example, the below ChildComponent.razor component has a ChildContent parameter of type RenderFragment. This represents a segment of UI to render.

<h1>
    @ChildContent
</h1>

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

Then, in a parent Razor component, you can render the child component while passing content between it’s opening and closing tags.

<ChildComponent>
    Spark is <span class="text-yellow-600">Awesome!</span>
</ChildComponent>

In this example, the following markup would ultimately be rendered to the browser.

<h1>Spark is <span class="text-yellow-600">Awesome!</span></h1>

Dependency Injection

You can take advantage of ASP.NET’s dependency injection system by adding the @inject directive in your Razor components.

.NET will automatically resolve the dependency for you provided it has been registered in the apps service container.

@inject IConfiguration Config

@code {
    private string name = '';

    protected override void OnInitialized()
    {
        name = Config.GetValue<string>("Developer:Name");
    }
}

Page State

Spark exposes the PageState object to all Razor components as a cascading parameter.

This parameter can be used in any Razor component in a Spark app.

The PageState object has two properties.

public class PageState
{
	public User User { get; set; } = new();
	public string AppName { get; set; }
}

These properties are set for you automatically when a route is visited, giving you access to the authenticated users data and the apps name.

For example, you can display the authenticated users name on a page like this:

@code {
	[CascadingParameter]
    public PageState PageState { get; set; } = default!;
}

<div>@PageState.User.Name</div>

HTTP Context

Because Spark use Blazor SSR, you can access the HttpContext in your Razor components through a cascading parameter.

@code {
	[CascadingParameter]
    public HttpContext? HttpContext { get; set; }
}

Pages

Your Blazor pages should be created in the Pages directory.

A Blazor page is a Razor component with a @page "/some-url" attribute at the top that represents the url it’s located at. The Blazor router will automatically register this route for you.

You can read more about Pages here

Layouts

Layout files define a top level template for pages in Spark apps.

Spark projects come with a MainLayout.razor file setup for you already in the Pages/Layouts directory. All pages will use this layout by default.

Creating Layouts

To create your own layout file, add a new razor component to the Pages/Layouts directory. For example, Pages/Layouts/NewLayout.razor.

Layout files need to inherit LayoutComponentBase and have an @Body tag to let Blazor know where to render the pages content.

@* Pages/Layouts/NewLayout.razor *@
@inherits LayoutComponentBase
<!DOCTYPE html>
<html lang="en">
    <head>
        <HeadOutlet />
    </head>
    <body>
        @Body
    </body>
</html>

To use the new layout on a page, add the @layout attribute.

@page "/developers"
@layout NewLayout

<h2>Episodes</h2>

Default Layout

The default layout used by your pages is set in the Pages/PageRoutes.razor file.

...
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layouts.MainLayout)"/>
...

To change your default layout, simply pass a different layout to the DefaultLayout parameter.

...
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layouts.NewLayout)"/>
...