dotnet / dotnet/aspnetcore

Improving type-safety of RazorComponentResult<T>

Open
#51,923 4 comments 16 reactions 0 assignees View on GitHub
analyzer api-suggestion area-blazor copilot-candidate enhancement feature-full-stack-web-ui Pillar: Complete Blazor Web
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

## Background and Motivation

ASP.Net Core 8 introduced the `RazorComponentResult` which allows a developer to render Razor Components from normal endpoints. This is very useful for for example [HTMX](https://htmx.org/) as it allows you to create very simple endpoints like this, which will return a pre-rendered Razor Component as raw HTML over the wire.

![image](https://github.com/dotnet/aspnetcore/assets/17124533/68ea7bb0-401f-4503-8565-0c180a65b9d9)

However, there is one huge problem with the ergonomics here when it comes to parameter passing, as you have to pass in dynamic objects, like the following:

![image](https://github.com/dotnet/aspnetcore/assets/17124533/40e5794a-b1c3-4e79-b46e-fb7c0ab0481f)

Without any further context you might not realize the problem, but here's the component:

![image](https://github.com/dotnet/aspnetcore/assets/17124533/8c2c8006-7096-48ed-b0eb-78704285cf53)

As you can see, this will not function as you'd expect - in fact, it will throw at runtime. The reason is that I passed in `AuthorId` when I should've passed in `UserId`. You might think, "Oh, I'll just pass in a model of type `SomeRandomComponent`" (I certainly did..), but that will give you this compiler error (https://learn.microsoft.com/en-us/aspnet/core/diagnostics/bl0005):

![image](https://github.com/dotnet/aspnetcore/assets/17124533/8c26db05-0865-470c-b794-26b13c5ba38f)
![image](https://github.com/dotnet/aspnetcore/assets/17124533/a8a8002d-0baa-4002-8795-fe20a80b656c)

```
error BL0005: Component parameter 'UserId' should not be set outside of its component.
```

So the only solution we have _is_ to pass in an anonymous object and pray we did it right.

This is not only a problem for when you forget to add a prop, but also when you forget to _remove_ a prop, as if the component doesn't have a prop with the name you specified, you also get a runtime error:

```
InvalidOperationException: Object of type 'MyNamespace.SomeRandomComponent' does not have a property matching the name 'AuthorId'.
```

There is also the problem with the fact that - as you can see in the third image (The one with the Component code), I have `[EditorRequired]` on the parameter, but the component renders just fine - no warning or anything - despite me not passing it in (in the first example)

## Proposed API

I don't have any specific APIs in mind, but in an ideal world I'd love for there to be an overload that works something like this:

![image](https://github.com/dotnet/aspnetcore/assets/17124533/a44ef482-83b0-493c-ab09-0dff7fc4f190)

## Usage Examples

![image](https://github.com/dotnet/aspnetcore/assets/17124533/82de192d-dd60-4013-9fbb-e19e991bfa78)

## Alternative Designs

* Roslyn Analyzer that analyzes usages of `RazorComponentResult` constructors and warns you (with quick fixes) whenever you don't address all errors (Removes invalid ones, and adds all parameters, as well as type-checks them)
* Roslyn Source Generator that creates custom `RazorComponentResult` for each Component:
* ![image](https://github.com/dotnet/aspnetcore/assets/17124533/f3933ac8-9e73-441d-a3aa-0a7e388f4897)
* ![image](https://github.com/dotnet/aspnetcore/assets/17124533/ff71238a-677c-4434-bf2d-447e35488a13)
* There are a few cons for this however, and that mostly comes from the fact that we lost the object initializer syntax, so we lost optional-ness as well as the ordering being semi-random (Re-ordering the component parameters would reorder the constructor parameters)

## Risks

The proposed API presumably cannot be implemented; I just mentioned what would I consider more-or-less ideal. Assuming we go that direction, then the component parameter assignment logic would presumably need to be rewritten somewhat, which could be quite a huge undertaking.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.