Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net Core render ViewComponent to variable

I am trying to render ViewComponent to a string in a service in ASP.NET Core 3.0 (Pages).

I found two partial solutions to my problem, but both are for older pre 3.0 version and have some issues that I do not know how to resolve.

  • First one is taken from GitHub Gist, but designed with controllers in mind and NullView was removed 3.0 (and is really hard to find any reference on it).
  • The second option is here, but fails one line 42 (no view is found). I believe that this is due to the changes made in .net core, where the views are now precompiled and the razor view engine therefore cannot find them.

I did some of my own experimenting, but could not get it working (I believe I am facing the same issue as the second solution I posted). I would appreciate any help regarding this matter on how to get it working. I've added my best attempt and a minimum project to GitHub, where Render is in the Service folder and in the Index.cshtml.cs is the call to the service.

Thanks!

like image 484
Jaka Konda Avatar asked Aug 02 '26 16:08

Jaka Konda


1 Answers

The Reasons

  1. NullView is an internal class now. But that's a very simple class that does nothing. In other words, simply copy & paste it from the src into your source code.
  2. The tutorials you linked above is used to render a plain view. However, since you're trying to render a view component instead of a view, you should avoid passing a view path directly. You should pass a ViewComponent Class Name (or Type) instead.

    Model = await _viewRender.RenderToStringAsync(
        "/Components/Test/Default.cshtml", // don't pass a view path since we're not rendering a view file but a view component
        "Test",                            // because the `ViewComponent` Name is Test
        new TestModel { StrToPrint = "Print From Service" });
    
  3. According to the official docs, The runtime searches for the view in the following paths:

    1. /Views/{Controller Name}/Components/{View Component Name}/{View Name}
    2. /Views/Shared/Components/{View Component Name}/{View Name}
    3. /Pages/Shared/Components/{View Component Name}/{View Name}

    However, your Test ViewComponent resides in Pages/Components/Test/Default.cshtml, which can not be found by Razor by default. Either configure a custom View Location, or move it to the standard location such that Razor can find the view files.

  4. Finally, rendering a ViewComponent as a page seems a bit of overkill. I would suggest you should use IViewComponentHelper to render the ViewComponent as an IHtmlContent such that I can write to a StringWriter:

    public class MyViewComponentContext 
    {
        public HttpContext HttpContext { get; set; }
        public ActionContext ActionContext { get; set; }
        public ViewDataDictionary ViewData { get; set; }
        public ITempDataDictionary TempData { get; set; }
    }
    private async Task<string> Render( MyViewComponentContext myViewComponentContext,string viewComponentName,object args) {
        using (var writer = new StringWriter())
        { 
            var helper = this.GetViewComponentHelper(myViewComponentContext, writer);
            var result = await helper.InvokeAsync(viewComponentName, args);  // get an IHtmlContent
            result.WriteTo(writer, HtmlEncoder.Default);
            await writer.FlushAsync();
            return writer.ToString();
        }
    }
    

Demo

If I fix the issues as I described above, when running your project I'll get the correct result:

enter image description here

like image 141
itminus Avatar answered Aug 05 '26 06:08

itminus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!