Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render Partial View to HTML string in ASP.NET Core 2.2

I am trying to use AJAX to call my Controller and return a Partial View with a model as a string so that I can inject it into my HTML. I have done this before in MVC5 using a controller interface, but I can't seem to find anything on how to do this for a Partial View in Asp.Net Core 2.2. I have found examples of how to render a View to a string, but I could not modify them to work for a partial view.

Controller Action:

    public JsonResult GetUpsertPartialView(MessageBoard messageBoard)
    {
        Dictionary<string, string> result = new Dictionary<string, string>();
        string errorState = "0";
        string errorMessage = "";

        try
        {
            result["view"] = ""; // My call to render the Partial View would go here.
        }
        catch (Exception)
        {
            errorState = "1";
            errorMessage = "An error was encountered while constructing the View.";
        }
        result["errorState"] = errorState;
        result["errorMessage"] = errorMessage;
        return Json(result);
    }

AJAX Call:

   $.ajax({
      type: "GET",
      url: "../Home/GetUpsertPartialView/",
      data: messageBoardData,
      contentType: 'application/json; charset=utf-8',
      success: function (data) {
         console.log(data);
         $("#messageBoardModalBody").val(data.view);
         $("#messageBoardModal").modal("show");
      }
   });

I've confirmed that my Controller is being hit, the parameters are being passed correctly, and I'm receiving the correct data back from the Action to my AJAX call. The only thing I'm missing is the ability to render a Partial View directly to a string.

If there is another way to do this in Asp.net Core, I'm open to other options.

Note: I'm just doing this to learn Asp.Net Core. Please let me know if any more information is needed.

like image 829
Jack Avatar asked Feb 11 '19 21:02

Jack


People also ask

How can we call a partial view in view of ASP NET core?

Declare partial views In ASP.NET Core MVC, a controller's ViewResult is capable of returning either a view or a partial view. In Razor Pages, a PageModel can return a partial view represented as a PartialViewResult object. Referencing and rendering partial views is described in the Reference a partial view section.

How do I render a razor view to string?

Listing 1: Rendering a Razor View to String from within a Controller. The RenderViewToString() method works by receiving a controller context and virtual view path (i.e., ~/views/item/page. cshtml ) and optional model data that are passed to the view.

How do I render a partial view inside?

Partial function which renders the Partial View. The name of the View and the object of the CustomerModel class are passed to the @Html. Partial function. In order to add Partial View, you will need to Right Click inside the Controller class and click on the Add View option in order to create a View for the Controller.


3 Answers

It is possible to get a partial view's (or several) from any where (a controller, page model etc) using a service:

The Origin of this answer, he deserves the credit (I'm adding it here because I needed something like that and it took me quite a while to find it)

Defining the interface for the Dependency Inejection

public interface IViewRenderService
{
    Task<string> RenderToStringAsync(string viewName, object model);
}

The service's implementation

public class ViewRenderService : IViewRenderService
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IServiceProvider _serviceProvider;

    public ViewRenderService(IRazorViewEngine razorViewEngine,
        ITempDataProvider tempDataProvider,
        IServiceProvider serviceProvider)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
        _serviceProvider = serviceProvider;
    }

    public async Task<string> RenderToStringAsync(string viewName, object model)
    {
        var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
        var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

        using (var sw = new StringWriter())
        {
            var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);

            if (viewResult.View == null)
            {
                throw new ArgumentNullException($"{viewName} does not match any available view");
            }

            var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
            {
                Model = model
            };

            var viewContext = new ViewContext(
                actionContext,
                viewResult.View,
                viewDictionary,
                new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
                sw,
                new HtmlHelperOptions()
            );

            await viewResult.View.RenderAsync(viewContext);
            return sw.ToString();
        }
    }
}

Adding the class in the ConfigureServices function:

services.AddScoped<IViewRenderService, ViewRenderService>();

Finally, Using the service

string html = await m_RenderService.RenderToStringAsync("<NameOfPartial>", new Model());
like image 60
Aharon Ohayon Avatar answered Oct 18 '22 20:10

Aharon Ohayon


The ControllerExtensions implementation

    public static class ControllerExtensions
{
    /// <summary>
    /// Render a partial view to string.
    /// </summary>
    public static async Task<string> RenderViewToStringAsync(this Controller controller, string viewNamePath, object model = null)
    {
        if (string.IsNullOrEmpty(viewNamePath))
            viewNamePath = controller.ControllerContext.ActionDescriptor.ActionName;

        controller.ViewData.Model = model;

        using (StringWriter writer = new StringWriter())
        {
            try
            {
                var view = FindView(controller, viewNamePath);

                ViewContext viewContext = new ViewContext(
                    controller.ControllerContext,
                    view,
                    controller.ViewData,
                    controller.TempData,
                    writer,
                    new HtmlHelperOptions()
                );

                await view.RenderAsync(viewContext);

                return writer.GetStringBuilder().ToString();
            }
            catch (Exception exc)
            {
                return $"Failed - {exc.Message}";
            }
        }
    }

    private static IView FindView(Controller controller, string viewNamePath)
    {
        IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;

        ViewEngineResult viewResult = null;

        if (viewNamePath.EndsWith(".cshtml"))
            viewResult = viewEngine.GetView(viewNamePath, viewNamePath, false);
        else
            viewResult = viewEngine.FindView(controller.ControllerContext, viewNamePath, false);

        if (!viewResult.Success)
        {
            var endPointDisplay = controller.HttpContext.GetEndpoint().DisplayName;

            if (endPointDisplay.Contains(".Areas."))
            {
                //search in Areas
                var areaName = endPointDisplay.Substring(endPointDisplay.IndexOf(".Areas.") + ".Areas.".Length);
                areaName = areaName.Substring(0, areaName.IndexOf(".Controllers."));

                viewNamePath = $"~/Areas/{areaName}/views/{controller.HttpContext.Request.RouteValues["controller"]}/{controller.HttpContext.Request.RouteValues["action"]}.cshtml";

                viewResult = viewEngine.GetView(viewNamePath, viewNamePath, false);
            }

            if (!viewResult.Success)
                throw new Exception($"A view with the name '{viewNamePath}' could not be found");

        }

        return viewResult.View;
    }

}

Usage in your controller actions:

var html = await this.RenderViewToStringAsync("actionName" , model);
like image 4
Mohammad Avatar answered Oct 18 '22 19:10

Mohammad


Why Json result with html string? You can return a partial view directly to return html.

public IActionResult GetUpsertPartialView(MessageBoard messageBoard)
{

    return PartialView("someviewname", messageBoard);
}
like image 3
Joe Audette Avatar answered Oct 18 '22 19:10

Joe Audette