Inject content into your Blazor components with typed Render Fragments
October 6, 2022 · 3 minute read · Tags: blazor
I’ve been doing some more work on the new courseware for https://practicaldotnet.io this week and ran into an interesting use case for Blazor’s Render Fragments.
The application is split up into a couple of different areas.
- Course content (lessons, videos, that sort of thing)
- Pretty much everything else (member pages, landing pages, integration with payment providers etc.)
The course content part of the app (called LearnUI) is entirely separate from the rest of the site, in its own Razor Class Library.
Here’s how the main Blazor app then consumes/displays this LearnUI.
@page "/course/{course}/{*slug}"
<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
</CourseContent>
With this, the main “host” app handles routing, then defers to the CourseContent
component (from the Razor Class Library) to actually render something on screen.
As with everything, there are benefits and trade-offs to be considered by having these parts of the UI in separate places but overall I find this logical separation forces me to think a bit more carefully before throwing in markup and logic.
Unlock this course!
But what if we need/want to render content within LearnUI which it doesn’t own?
For example, if someone doesn’t have access to a course, and attempts to view any part of the course, show them an unlock screen, with buttons to go ahead and purchase the course.
That unlock markup and logic doesn’t belong in LearnUI (LearnUI doesn’t have any logic/knowledge of online payments or course prices), so we’d need a way to inject that UI in, and have it shown in place of the course content.
Enter the humble (but extremely useful) RenderFragment
Blazor components can accept one or more RenderFragment
parameters, and they mostly do what you would expect; they enable your components to receive a fragment of UI which they can then choose to render.
So, we can make the CourseContent
component accept a RenderFragment
called LockedUI
and render that if the course is locked (the user doesn’t have access).
CourseContent Markup
@if (locked) {
@LockedUI
} else {
@page.Content
}
CourseContent @Code
@code {
private bool locked;
[Parameter]
public RenderFragment LockedUI { get; set; }
...
}
Then, in the consuming page/component, we can define some content to be shown for this LockedUI
:
<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
<LockedUI>
<p>Unlock this course now!</p>
</LockedUI>
</CourseContent>
Pass data via the Render Fragment
So far so good, but what if we want to show some more detail about the course that’s locked.
For example, we may want this LockedUI
fragment to show the course name, or details about how many lessons are included.
<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
<LockedUI>
<p>Unlock {course name here} now!</p>
</LockedUI>
</CourseContent>
It feels like that information should come from LearnUI as it has the responsibility for retrieving and showing course contents, but how can we tap into that when providing UI for the LockedUI
Render Fragment.
The answer is to get CourseContent
to expose those details.
We can modify the RenderFragment
parameter to be strongly typed, and pass an object “back” when we render it.
@if (locked) {
@LockedUI(course)
}
@code {
CourseDetails course;
[Parameter]
public RenderFragment<CourseDetails> LockedUI { get; set; }
}
In the consuming app, we can access this data via @context
.
<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
<LockedUI>
<h3>Unlock @context.Name</h3>
<p>Includes @context.Curriculum.LessonCount() Lessons</p>
</LockedUI>
</CourseContent>
With that, our consuming app can render the CourseContent
component, provide UI to render if the lesson is locked, and easily display any context (data) that the CourseContent
component exposes via the LockedUI RenderFragment
.
In Summary
You can inject UI into a component using Render Fragments, effectively making a component adaptable, or customisable, in different use cases.
Sometimes you’ll want your UI to reference data from the component which defines the Render Fragment. If so you can make the Render Fragment strongly typed and pass data into it at the point of rendering.