Objective
I have a form on my page that will call the method OnFinish
when the user clicks the button to send the form out. In that method, I want to add new markup to the page. (The code for the form is not relevant, so I won't add it on here)
Problem
There does not seem to be a simple way (if any) to add razor markup from C# code.
What I've tried
The first thing I tried was straight markup in the C# Code. That looks like this:
private void OnFinish(EditContext editContext)
{
<a>Placeholder</a>
}
This did - of course - not work, because I was writing it in a .cs file.
In the .razor file I tried doing the same:
@code
{
private void OnFinish(EditContext editContext)
{
<a>Placeholder</a>
}
}
The compiler didn't like this either and raised the following error:
The name '__builder' does not exist in the current context
I quickly learned from this question, that markup cannot be written in a @code
block.
According to this official ASP.NET blog post, writing markup in a @functions
block should work:
@functions
{
private void OnFinish(EditContext editContext)
{
<a>Placeholder</a>
}
}
This, unfortunately, had the same outcome as using a @code
block.
EDIT: After reading through the blog post again I realised, that the feature only works in .cshtml
files. I tested this and, indeed, it works in .cshtml
files. But these files cannot be used as components, so it doesn't work for me after all.
I tried using a RenderTreeBuilder __builder
as a parameter in the method, which was suggested in this answer.
The problem with this solution is, that the __builder
parameter is only available from within the markup code like this:
<a>@__builder</a>
and not from the C# code (that's why the parameter is used).
The next thing I found was the @helper
block, which is mentioned in this answer. I applied this to my code like this:
@helper OnFinish(EditContext editContext)
{
<a>Placeholder</a>
}
This does not work either, as the compiler raises the following two errors:
The helper directive is not supported.
The name 'helper' does not exist in the current context
Conclusion
Now, this is where the search comes to an end. I spent so much time researching this, that I want to believe that there might not be any solution to this at all. What have I missed?
EDIT
Some have mentioned the idea of putting the markup in an if, while the if gets then activate when some condition comes true (e.g. I have a bool that I set to true or I change a variable and the if recognizes that).
That solution can be seen in either this other question or this answer in the current thread.
The code for that solution would look like this:
if (renderNewMarkup)
{
<a>Placeholder</a>
}
@functions
{
private void OnFinish(EditContext editContext)
{
renderNewMarkup = true;
}
}
This does work but it's very unpleasant in my opinion. The problem I have with this is the use of variables.
In my program, I retrieve some data from the UI and would pass it further to the then created markup. I can give you an example:
private void OnFinish(EditContext editContext)
{
Dictionary<string, string> dict = new()
{
["Key1"] = boundVariable,
["Key2"] = otherBoundVariable,
["Key3"] = anotherBoundVariable
}
// Create markup with dict variable
}
The markup I want to add to the HTML is a custom razor component that has a Dictionary<string, string>
parameter.
You can see that I now can't just simply create the markup there. What I have to achieve what I want would look like this:
@if (renderNewCustomComponent)
{
<Custom DictParameter="@dict" />
}
@code
{
private Dictionary<string, string> dict;
private void OnFinish(EditContext editContext)
{
dict = new()
{
["Key1"] = boundVariable,
["Key2"] = otherBoundVariable,
["Key3"] = anotherBoundVariable
}
renderNewCustomComponent = true;
}
}
I hope you see what I mean by this.
Then there's an even bigger problem with this solution. When I want to call a method, e.g. AddElementToHTML()
, that will always add some element to the HTML, that would not be possible. Yes, with some complex way, with a @foreach
or something, that would be possible, but why wouldn't the language just allow that in the first place?