7

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!

1 Answer 1

7

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.