Return View as String in .NET Core
Solution 1
Thanks to Paris Polyzos and his article.
I'm re-posting his code here, just in case the original post got removed for any reason.
Create Service
in file viewToString.cs
as below code:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
namespace WebApplication.Services
{
public interface IViewRenderService
{
Task<string> RenderToStringAsync(string viewName, object model);
}
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();
}
}
}
}
2. Add the service to the Startup.cs
file, as:
using WebApplication.Services;
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IViewRenderService, ViewRenderService>();
}
3. Add "preserveCompilationContext": true
to the buildOptions
in the project.json
, so the file looks like:
{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"dependencies": {
"Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
"Microsoft.AspNetCore.Mvc": "1.0.1"
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.1"
}
},
"imports": "dnxcore50"
}
}
}
4. Define you model
, for example:
public class InviteViewModel {
public string UserId {get; set;}
public string UserName {get; set;}
public string ReferralCode {get; set;}
public int Credits {get; set;}
}
5. Create your Invite.cshtml
for example:
@{
ViewData["Title"] = "Contact";
}
@ViewData["Title"].
user id: @Model.UserId
6. In the Controller
:
a. Define the below at the beginning:
private readonly IViewRenderService _viewRenderService;
public RenderController(IViewRenderService viewRenderService)
{
_viewRenderService = viewRenderService;
}
b. Call and return the view with model as below:
var result = await _viewRenderService.RenderToStringAsync("Email/Invite", viewModel);
return Content(result);
c. The FULL controller example, could be like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using WebApplication.Services;
namespace WebApplication.Controllers
{
[Route("render")]
public class RenderController : Controller
{
private readonly IViewRenderService _viewRenderService;
public RenderController(IViewRenderService viewRenderService)
{
_viewRenderService = viewRenderService;
}
[Route("invite")]
public async Task<IActionResult> RenderInviteView()
{
ViewData["Message"] = "Your application description page.";
var viewModel = new InviteViewModel
{
UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
UserName = "Hasan",
ReferralCode = "55e12b710f78",
Credits = 10
};
var result = await _viewRenderService.RenderToStringAsync("Email/Invite", viewModel);
return Content(result);
}
public class InviteViewModel {
public string UserId {get; set;}
public string UserName {get; set;}
public string ReferralCode {get; set;}
public int Credits {get; set;}
}
}
Solution 2
If like me you have a number of controllers that need this, like in a reporting site, it's not really ideal to repeat this code, and even injecting or calling another service doesn't really seem right.
So I've made my own version of the above with the following differences:
- model strong-typing
- error checking when finding a view
- ability to render views as partials or pages
- asynchronus
- implemented as a controller extension
-
no DI needed
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using System.IO; using System.Threading.Tasks; namespace CC.Web.Helpers { public static class ControllerExtensions { public static async Task<string> RenderViewAsync<TModel>(this Controller controller, string viewName, TModel model, bool partial = false) { if (string.IsNullOrEmpty(viewName)) { viewName = controller.ControllerContext.ActionDescriptor.ActionName; } controller.ViewData.Model = model; using (var writer = new StringWriter()) { IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine; ViewEngineResult viewResult = viewEngine.FindView(controller.ControllerContext, viewName, !partial); if (viewResult.Success == false) { return $"A view with the name {viewName} could not be found"; } ViewContext viewContext = new ViewContext( controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, writer, new HtmlHelperOptions() ); await viewResult.View.RenderAsync(viewContext); return writer.GetStringBuilder().ToString(); } } } }
Then just implement with:
viewHtml = await this.RenderViewAsync("Report", model);
Or this for a PartialView:
partialViewHtml = await this.RenderViewAsync("Report", model, true);
Solution 3
ASP.NET Core 3.1
I know there are a lot of good answers here, I thought I share mine as well:
This is pulled from the source code of asp.net core on GitHub I usually use it to render HTML emails with Razor as well as returning HTML of partial views via Ajax or SignalR.
Add as transient service and inject with DI in controllers
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
public sealed class RazorViewToStringRenderer : IRazorViewToStringRenderer
{
private readonly IRazorViewEngine _viewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
public RazorViewToStringRenderer(
IRazorViewEngine viewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
public async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model)
{
//If you wish to use the route data in the generated view (e.g. use
//the Url helper to construct dynamic links)
//inject the IHttpContextAccessor then use: var actionContext = new ActionContext(_contextAccessor.HttpContext, _contextAccessor.HttpContext.GetRouteData(), new ActionDescriptor());
//instead of the line below
var actionContext = GetActionContext();
var view = FindView(actionContext, viewName);
using (var output = new StringWriter())
{
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary<TModel>(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary())
{
Model = model
},
new TempDataDictionary(
actionContext.HttpContext,
_tempDataProvider),
output,
new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return output.ToString();
}
}
private IView FindView(ActionContext actionContext, string viewName)
{
var getViewResult = _viewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
if (getViewResult.Success)
{
return getViewResult.View;
}
var findViewResult = _viewEngine.FindView(actionContext, viewName, isMainPage: true);
if (findViewResult.Success)
{
return findViewResult.View;
}
var searchedLocations = getViewResult.SearchedLocations.Concat(findViewResult.SearchedLocations);
var errorMessage = string.Join(
Environment.NewLine,
new[] { $"Unable to find view '{viewName}'. The following locations were searched:" }.Concat(searchedLocations)); ;
throw new InvalidOperationException(errorMessage);
}
private ActionContext GetActionContext()
{
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = _serviceProvider;
return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
}
}
public interface IRazorViewToStringRenderer
{
Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model);
}
Solution 4
Red's answer got me 99% of the way there, but it doesn't work if your views are in an unexpected location. Here's my fix for that.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System.IO;
using System.Threading.Tasks;
namespace Example
{
public static class ControllerExtensions
{
public static async Task<string> RenderViewAsync<TModel>(this Controller controller, string viewName, TModel model, bool isPartial = false)
{
if (string.IsNullOrEmpty(viewName))
{
viewName = controller.ControllerContext.ActionDescriptor.ActionName;
}
controller.ViewData.Model = model;
using (var writer = new StringWriter())
{
IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
ViewEngineResult viewResult = GetViewEngineResult(controller, viewName, isPartial, viewEngine);
if (viewResult.Success == false)
{
throw new System.Exception($"A view with the name {viewName} could not be found");
}
ViewContext viewContext = new ViewContext(
controller.ControllerContext,
viewResult.View,
controller.ViewData,
controller.TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
private static ViewEngineResult GetViewEngineResult(Controller controller, string viewName, bool isPartial, IViewEngine viewEngine)
{
if (viewName.StartsWith("~/"))
{
var hostingEnv = controller.HttpContext.RequestServices.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;
return viewEngine.GetView(hostingEnv.WebRootPath, viewName, !isPartial);
}
else
{
return viewEngine.FindView(controller.ControllerContext, viewName, !isPartial);
}
}
}
}
This allows you to use it as as below:
var emailBody = await this.RenderViewAsync("~/My/Different/View.cshtml", myModel);
Solution 5
The answers above are fine, but need to tweaking to get any tag helpers to work (we need to use the actually http context). Also you will need to explicitly set the layout in the view to get a layout rendered.
public class ViewRenderService : IViewRenderService
{
private readonly IRazorViewEngine _razorViewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
private readonly IHostingEnvironment _env;
private readonly HttpContext _http;
public ViewRenderService(IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider, IHostingEnvironment env, IHttpContextAccessor ctx)
{
_razorViewEngine = razorViewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; _env = env; _http = ctx.HttpContext;
}
public async Task<string> RenderToStringAsync(string viewName, object model)
{
var actionContext = new ActionContext(_http, new RouteData(), new ActionDescriptor());
using (var sw = new StringWriter())
{
var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
//var viewResult = _razorViewEngine.GetView(_env.WebRootPath, viewName, false); // For views outside the usual Views folder
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(_http, _tempDataProvider), sw, new HtmlHelperOptions());
viewContext.RouteData = _http.GetRouteData();
await viewResult.View.RenderAsync(viewContext);
return sw.ToString();
}
}
}
Hasan A Yousef
Industrial Engineer, with a slogan "If you can automate it, do not do it". Interested in all types of automation that facilitate life. Work officially as Supply Chain Manager, where Data is the main driver of availability. Combining my IT skills and interest with my work expertise, proud to deliver the best possible results for my organization.
Updated on July 08, 2022Comments
-
Hasan A Yousef almost 2 years
I found some article how to return view to string in ASP.NET, but could not covert any to be able to run it with .NET Core
public static string RenderViewToString(this Controller controller, string viewName, object model) { var context = controller.ControllerContext; if (string.IsNullOrEmpty(viewName)) viewName = context.RouteData.GetRequiredString("action"); var viewData = new ViewDataDictionary(model); using (var sw = new StringWriter()) { var viewResult = ViewEngines.Engines.FindPartialView(context, viewName); var viewContext = new ViewContext(context, viewResult.View, viewData, new TempDataDictionary(), sw); viewResult.View.Render(viewContext, sw); return sw.GetStringBuilder().ToString(); } }
which assumed to be able to call from a Controller using:
var strView = this.RenderViewToString("YourViewName", yourModel);
When I try to run the above into .NET Core I get lots of compilation errors.
I tried to convert it to work with .NET Core, but failed, can anyone help with mentioning the required
using ..
and the required"dependencies": { "Microsoft.AspNetCore.Mvc": "1.1.0", ... },
to be used in theproject.json
.some other sample codes are here and here and here
NOTE I need the solution to get the view converted to
string
in .NET Core, regardless same code got converted, or another way that can do it. -
Hasan A Yousef over 7 yearsI got
error CS0117: 'ViewResult' doesn't contain a definition for 'Content'
-
philw over 6 yearsThat pretty much works for Core 2.0, with two exceptions: (1) _razorViewEngine.FindView doesn't work on absolute paths, and I at least need those because the standard template apps don't use Views folders which it assumes. Tto use his is documented as "by design" on the Core 2.0 GitHub site, and the solution is to use _razorViewEngine.GetView, which supports absolute paths. (2) that preserveCompilationContext (not in the original article) isn't explained - why do you need it? It's not clear where to put it with COre 2.0, and it seems to work without it.
-
Dave Glassborow over 6 yearsNote: on azure, I needed to add the following to the Startup
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Weirdly worked fine locally without this ... -
martonx about 6 yearsWith this code ViewData["Message"] = "Your application description page."; will be null in the view. Why? Could anybody post a fixed version which contains correct handling of ViewData, not just view model.
-
DGaspar almost 6 yearsThis one should be way more upthere. It looks way more elegant and it worked like a charm for me.
-
LentoMan almost 6 yearsThanks, excellent solution! The only thing I changed was adding an additional wrapped extension method for partial views. I actually ran into a bit of a problem when using the previously injected ViewRenderService since it could access partial views from the view tree of other controllers. Those views did render correctly, but would never automatically recompile during debug, moving them to Shared views solved the issue!
-
Mike Moore almost 6 years@LentoMan could you post an example of the wrapper extension that you mentioned?
-
LentoMan almost 6 yearsJust add another method in the same extension class that Red suggested: ` public static async Task<string> RenderPartialViewAsync<TModel>(this Controller controller, string viewName, TModel model) { return await controller.RenderViewAsync(viewName, model, true); }`
-
Red almost 6 yearsIf that code does what you need, you could just directly call
controller.RenderViewAsync(viewName, model, true);
. I don't see you're gaining anything, other than making it clearer that it's a Partial, but that could be important in your situation. -
LentoMan almost 6 yearsYou are not wrong, one could also use named arguments to make it more readable. But in my case, I mainly use partials, so it is more about making it easier to use and matching the result methods already available on the controller.
-
Janis Veinbergs over 5 yearsI would prefer throwing exception if view could not be found rather than getting surprise result:
return $"A view with the name {viewName} could not be found";
-
Chan over 5 yearscheck my answer in the post. I pasted a sample code of cshtml.
-
Piotr Kula over 5 yearsThanks- I was looking for something like this to merge a model in HTML using Razor. Not sure why MS wont just make a simpler way of this doing. Although.. its much easier than in MVC 1. Great solution and thanks for sharing the GetView change!
-
Richard Mneyan over 5 yearsYour welcome @ppumkin, back then I spent long time figuring this out, and I needed this badly.
-
gbade_ over 5 yearsI tried your method and now I'm getting this - Executed action Controllers.PortfolioController.PrintStatement in [ERR] An unhandled exception has occurred while executing the request System.NullReferenceException: Object reference not set to an instance of an object. at AspNetCore._Views_Portfolio_PrintStatement_cshtml. in PrintStatement.cshtml:line 248. --------------Prior to that, I was getting the nullreference error at line 0 in the cshtml file.
-
Pharylon over 5 yearsThis doesn't work if your views are in an unusual location. I posted a fix for that in my own answer.
-
Yogi over 5 yearsThis does work, but the issue is the @page directive that marks it as a Razor Page. And Razor Pages work differently than Razor Views. See this other SO solution for a way to add the Model to a Razor Page: stackoverflow.com/a/49275145/943435
-
scgough over 5 yearsThis is nice. I'm having a small issue though. I want to get the HTML string for
ControllerB.Action1
fromControllerA.Action1
. The code seems to successfully render the view to a string but it doesn't seem to execute theControllerB.Action1
method before hand. Is there a way to get this to happen? I am using a dynamic partial view, not a static shared partial view. -
jonmeyer over 5 years@Red 3 changes i would recommend: 1. use GetRequiredService instead of GetService 2. Resolve generic iLogger<> to log when success is false to record the error message 3. Resolve IOptions<HtmlHelperOptions> instead of using new HtmlHelperOptions()
-
c-sharp over 5 yearsHow do you write a unit test for this service? I mean, how do we get the dependencies?
-
Felype over 5 yearsThis is great, though I had to change the interface from receiving an
object model
to Receiving theViewDataDictionary ViewData
instead, and then instead of initializing thevar viewDictionary
as you did, I simply use the ViewData. This way I get the Model, ViewBag, ViewData and everything else and that the controller context had, just as if I was callingView()
-
haugan about 5 yearsI get this error, trying this in .NET Core 2.2 from a Web API controller: "Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.Razor.IRazorViewEngine' while attempting to activate 'xxx.Api.ViewRenderService'." I think I've followed the instructions form Hasan's answer to the point. What could be wrong?
-
Faraz Ahmed almost 5 yearsPreviously I have coded with different technique it works in development but not in IIS, finally your code work in both dev and live.
-
Adrita Sharma almost 5 yearsWhat should be passed in controller in (this Controller controller, string viewName, TModel model, bool partial = false). I am getting compile time error.
-
Red almost 5 years@Adrita-Sharma - it's a controller extension, so you don't pass in your controller, it's a function added to all controllers. So whatever controller produces the view you want as a string, use the implementation code in the answer there.
-
Andrew over 4 yearsThis works great for me. I changed this so that it gets the ActionContext via the IActionContextAccessor interface.
-
gtrak over 4 yearsThis answer has an issue, since it mutates the controllers own model
controller.ViewData.Model = model;
, we have to undo any changes or it breaks the followup view rendering. I wrapped this mutation and rendering in a try-finally to fix it. -
Watson over 4 yearsHow do you let javascript run to completion? Any view returned does not run js - for example: <div id="Test"></div> and document.getElementById("Test").innerHTML = "whatever". Returned content does not change inner div html.
-
Red over 4 yearsThis is run on the server, so no client code will be executed. If you can't refactor to use the model data I think you're out of luck
-
rvnlord over 4 yearsThis is the correct answer
@Dave Glassborow
, because (at least in Core 3.0) if you don't injectHttpContext
and create the default one, you'll getInvalid URI: The format of the URI could not be determined
on executingRenderAsync()
, at least if you got customView/Pages
paths. Thank you. -
Lion about 4 yearsIts important to set
Layout = null;
when rendering PartialViews (e.g. for Ajax calls) with this method. Then it works well! -
DLeh almost 4 yearsIf you are trying to render documents that reference partials with relative paths, set the
actionContext.RouteData.Values["controller"] = controllerName;
, wherecontrollerName
is the base of the doc you want to render. In this example it would beEmail
from theEmail/Invite
path. -
beggarboy almost 4 yearsThis comment sadly, was only half the truth for me. While this code does the actual job of converting razor pages into an html string, the setup around has to fit, otherwise the compiler will complain. Make sure you add
services.AddControllersWithViews();
andservices.AddRazorPages();
to your Startup.cs. Make sure you add the DI correctly aswell below:services.AddTransient<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
AND make sure your project is on .Net Core 3.1. I have a feeling this has problems working on .net core 3. Correct me if I am wrong. Hopefully this helps some1 -
HMZ almost 4 years@beggarboy i have tried it on asp.net core 2.2 as well, and what you just said is pretty standard stuff except that you only need to add
ControllersWithViews
orRazorPages
(according to your preference). -
beggarboy almost 4 yearsYou're not wrong, it is pretty standard stuff, but it's draining enough trying to patch specific things like this together from multiple sources over the web desperately trying to get it to work over the multiple iterations of .net core and the breaking changes inbetween. I have tried 4 approaches before yours, and was always met with missing, unresolvable dependencies or implementation issues, so I thought it would be nice sparing somebody else the hour or two trying to figure out very simple things they might miss :)
-
HMZ almost 4 years@beggarboy I agree, it is a pain sometimes, and this feature could have been easily implemented in the framework since the engine is already here.
-
Jason Ayer almost 4 years@DLeh - I'm attempting this in a ASP.NET Core 3.1 project and my views root directory is at
~/Views
. In my example would I need to addactionContext.RouteData["controller"] = "Views";
? -
Kalpesh Rajai almost 4 yearsI used this solution,, Works fine.. But localization does not work. Any idea? what I am missing ?
-
NickG over 3 yearsWhy am I finding that controller.HttpContext is null when this code is clearly working for everyone else?
-
mohrtan over 3 yearsThanks, Hasan. This got me 98% of the way too. I had to use @philw's adjustment to "GetView" and then had 1 more issue. I couldn't use DefaultHttpContext, so I used the info from this answer to get the current HttpContext.
-
Hasan A Yousef over 3 yearsGlad it helped though it is 4 years old, where .Net Core was at version 1 :)
-
Canada Wan over 3 yearsWorks on Net Core 3.1. Thanks Red!
-
Auspex over 3 yearsBonus points for including your using statements. So many examples assume we're all so familiar with this that it's unnecessary
-
Auspex over 3 yearsAnd I've seen ViewResults with
Content
butContent
is null. I think it doesn't contain what you think it does. -
Jack Miller over 3 yearsWorks but
IHostingEnvironment
is obsolete. Just replace withIWebHostEnvironment
. -
JohnnyJaxs over 3 yearsI like your extension solution. You save me time.
-
Dave B about 3 yearsYour code worked for me except for partial views. Your answer says "as well as returning HTML of partial views via Ajax" but your code is missing a boolean that can toggle between a full view and a partial view. You hardcoded
isMainPage: true
in the methodprivate IView FindView
. In the answer provided by @Red, an optional parameterbool partial = false
can be added to theRenderViewToStringAsync
method. Thepartial
parameter is then passed to theprivate IView FindView
method. In that method swap outisMainPage: true
withisMainPage: !partial
. -
HMZ about 3 years@DaveB I suspect your partial view doesn't have a property
Layout = null
. You can add it to your "Partial view" and just render using the method above or do it the way you mentioned and add a partial view toggle. -
Dave B about 3 years@HMZ You're right, my partial views do not have
Layout = null
so ASP.NET Core was grabbing a layout from the nearestViewStart.cshtml
file. I added the partial view toggle to avoid addingLayout = null
to my partial views. -
Auspex almost 3 yearsIt's too bad you didn't show how
_helper
&_viewEngine
were defined. I'm assuming _helper is from injection ofIHelper
but I'm at a complete loss to figure out your_viewEngine
-
user2096582 almost 3 years_viewEngine comes from the controller DI as "ICompositeViewEngine viewEngine" I then assign it to _viewEngine in the constructor. _helper you are correct it is in the DI constructor as well.
-
Ofer Zelig over 2 yearsGood project. Worth noting that as of now (.NET Core 5.0.400), for the class library that hosts the Razor templates, one needs to create a project using the 'Razor Class Library' template and tick 'Support pages and views' in the wizard, otherwise Razor files can't be created.
-
ooXei1sh over 2 years@HMZ, This won't work with invalid ModelState if you're rendering partial forms with validation errors. Seems like passing in a ModelState is working tho.
modelState: modelState ?? new ModelStateDictionary()
-
Matthew M. over 2 yearsVery elegant solution and helpful. Some minor changes to address nullability in C#9, but otherwise works out of the box.