Render ViewComponent inside html string - c#

Lets say I have a ViewComponent named MyComponent.
As of ASP.NET Core 1.1 I can render this ViewComponent by writing this inside a razor view .cshtml page:
<vc:my-component></vc:my-component>
I want to do something like this:
#{string myHtml = "<vc:my-component></vc:my-component>";}
#(new HtmlString(myHtml))
I installed and tried RazorEngine by doing this, but this did not work:
string template = "<vc:my-component></vc:my-component>";
var result = Engine.Razor.RunCompile(template, "messageTemplateKey", null, new { Name = "some model data" });
ViewBag.result = result;
then in the .chtml file:
#(new HtmlString(ViewBag.result))
The backstory to this is that I've created a ViewComponent with some logic on how to handle image files, now I want to search and replace all img tags in some html with my ViewComponent. Before I do that I need to make sure this even works.

Check the code at this url on github:
https://gist.github.com/pauldotknopf/b424e9b8b03d31d67f3cce59f09ab17f
Code
public class HomeController : Controller {
public async Task<string> RenderViewComponent(string viewComponent, object args) {
var sp = HttpContext.RequestServices;
var helper = new DefaultViewComponentHelper(
sp.GetRequiredService<IViewComponentDescriptorCollectionProvider>(),
HtmlEncoder.Default,
sp.GetRequiredService<IViewComponentSelector>(),
sp.GetRequiredService<IViewComponentInvokerFactory>(),
sp.GetRequiredService<IViewBufferScope>());
using (var writer = new StringWriter()) {
var context = new ViewContext(ControllerContext, NullView.Instance, ViewData, TempData, writer, new HtmlHelperOptions());
helper.Contextualize(context);
var result = await helper.InvokeAsync(viewComponent, args);
result.WriteTo(writer, HtmlEncoder.Default);
await writer.FlushAsync();
return writer.ToString();
}
}}

This is what I ended up doing:
I moved the view code from the Default.cshtml for MyComponent to a partial view (_myPartial).
Then I used a IViewRenderService inspired by this post.
Then I could render and get my html string after passing in the viewmodel by doing this:
var result = ViewRenderService.RenderToString("_myPartial", viewModel);

Related

Render Razor View Page as string from WebApi Core

I have mock cshtml that looks like this:
#model TaskManager.Models.ViewModels.ProjectRaportViewModel
#{
var name = Model.Project.Name;
<h3>#name</h3>
}
<br />
<4h>Hello World!</4h>
I want to return that view(or any) as string but as html rendered by Razor (so i think, razor will change C# things to html tags etc).
I have a controller that looks like this:
[HttpGet("{projectId}")]
public IActionResult GetRaportFromProject([FromRoute] int projectId)
{
var html = this.pdfService.RenderViewToHtml(projectId);
return this.Content(html, "text/html", Encoding.UTF8);
}
And in pdfService i have this method:
public string RenderViewToHtml(int projectId)
{
this.projectService.GetItem(projectId);
var raportHtml = what to do next?
}
My question is, how to render cshtml to html by Razor in service class and then return it as string from WebApi endpoint?
If you use .Net Core, there is a NuGet package you can easily install: RazorEngine.NetCore
The documentation is good but here is the short version:
Include the library:
using RazorEngine;
using RazorEngine.Templating;
Get your razor html
string razorView= "Hello #Model.Name, welcome to RazorEngine!";
If it's in a file you could use File.ReadAllText
string razorView = await File.ReadAllTextAsync(filePath);
and compile:
string result = Engine.Razor.RunCompile(razorView, "templateKey", null, null);
And if you want to inject some object, pass it in the 4th argument

Can not Call PartialView with Parameter in Mvc [duplicate]

This question already has answers here:
In MVC how can I use the controller to render a partial view only for approved users?
(3 answers)
Closed 4 years ago.
I'm Trying to call Partial View Into view i want to call parameterized method which is returing the partial view so how to call it.
My code is below.
View
#{Html.RenderPartial("~/Views/Appoinment/GetALLAppoinmentMedicine.cshtml",
new List<HMS.Models.AppointmentViewModel> { new HMS.Models.AppointmentViewModel() },
new ViewDataDictionary { { "aid", Model.AppoinmentIDP} });}
Controller
public ActionResult GetALLAppoinmentMedicine(int aid)
{
var idParam = new SqlParameter
{
ParameterName = "APPIDP",
Value = aid
};
var SpResult = DB.Database.SqlQuery<AppointmentViewModel>("exec uspAppoinmentMedicine_GetAll #APPIDP", idParam).ToList<AppointmentViewModel>();
IEnumerable<AppointmentViewModel> Result = SpResult.Select(s => new AppointmentViewModel
{
MadicineName = s.MadicineName,
PotencyName = s.PotencyName,
SizeofPills = s.SizeofPills,
Dose = s.Dose,
DoseType = s.DoseType,
Repitation = s.Repitation,
Quantity = s.Quantity,
Duration = s.Duration
});
return View(Result);
}
Try to use #{ Html.RenderAction("ChildAction","Home", new {param="abc"}):
It invokes the specified child action method and renders the result inline in the parent view.
Hope this will work.
You can pass parameter or model as per your need..
dont forget to put the reference to the model in partial view
eg.#Html.Partial("_SomePartial", Model)
The return type of Html.RenderAction is void that means it directly render the responses in View where return type of Html.Action is MvcHtmlString you can catch its render view in the controller and modified it also by using following method
#{Html.RenderAction("GetALLAppoinmentMedicine", "ControllerName",new {uid= 1})}
If you want to use Ajax.ActionLink, replace your Html.ActionLink with:
#Ajax.ActionLink(
"Partial",
"GetALLAppoinmentMedicine",
"ControllerName",
new AjaxOptions { aid = 1 }
)
and of course you need to include a holder in your page where the partial will be displayed:
<div id="myresult"></div>
Also don't forget to include:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")"
type="text/javascript"></script>
in your main view in order to enable Ajax.* helpers. And make sure that unobtrusive javascript is enabled in your web.config (it should be by default):

Generate view without controller action

In my ASP.NET MVC project I have a controller that one action just result view. This Action does not get any argument and just return a CSHTML page. This page could not be partial.
Does anybody know a better way to generate view - I mean can I generate view without controller action?
//Edit - sample codes
Right now in my UserPanelController i have an action ChangeSettings
[HttpGet]
public ActionResult ChangeSettings()
{
return View("Configuration");
}
So if i want to get a configuration View i have to do request to controller from for example navigation:
<nav>
<div class="nav-wrapper">
Logo
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li>Sass</li>
<li>Components</li>
<li>Konrad</li>
</ul>
</div>
</nav>
Can i get a ConfigurationView without my controller action?
Maybe I'm a bit too late but what you are probably looking for is this:
#{ Html.RenderPartial("_PartialViewName"); }
Notice that you can use a model in your view as well, by passing it directly from the view that is calling the partial one.
You can render a view yourself by calling the following method
private static string RenderPartialViewToString(Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.ToString();
}
}
This will give you html string containing your rendered view. I've used this before to render HTML for emails where the email content changed from user to user.
Hope this helps.
If it is static content, you can add an .html file inside your project. The web server will serve it.
To make sure the MVC routing will not interfer you can add something like this to your routes:
routes.IgnoreRoute("{file}.html");

Get the Current page html and set a hidden field (MVC4)

I am trying to capture the Current page Html and was wondering to see how can i set a hiddenfield using the razor syntax
Filed i want to set
#Html.Hidden("pageHtml")
i am currently using javascript to get the page html but the same thing i want to try using #Html attribute or something
var html1 = $('#wrapper').html();
var encodedString = escape(html1);
$('#pageHtml').val(encodedString);
or if someone can help me finding a way to get the current view html in the controller action
Please make some suggestions
First, create a Controller Extension method ViewToString as below:
This will extend System.Web.Mvc.Controller to have a new method called ViewToString and it will render Your views HTML into a StringReader
namespace YourProject.ControllerExtensions
{
using System;
using System.IO;
using System.Web.Mvc;
public static class ControllerExtend
{
public static StringReader ViewToString(this Controller controller, string viewName, object model)
{
#region MyRegion
ControllerContext context = controller.ControllerContext;
if (context == null)
{
throw new ArgumentNullException("context");
}
if (model != null)
{
controller.ViewData.Model = model;
}
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
var viewContext = new ViewContext(context, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(context, viewResult.View);
//return sw.GetStringBuilder().ToString();
return new StringReader(sw.ToString());
}
#endregion
}
}
}
Now, call the above method in your Controller Action to access to your Views HTML and pass Your View Name and Your Model Name ( If you have no model, pass null):
public ActionResult YourControllerAction()
{
//....
StringReader htmlView = this.ViewToString("YourViewName", YourModelObject);
//Now htmlView containing the HTML of Your View
//and you can pass it to your Email function .....
// ...
}
htmlView will contain the HTML from Your view ...
Hope this would of some help ...
So this is how we are doing it finally...(our page does not contain any secure data so we have gone for this)
Added a hidden field to hold the html
Use the javascript method to set the hidden field with the encoded page html
var html1 = $('#wrapper').html();
var encodedString = escape(html1);
Changed the controller to take the hidden field as a parameter. (to pass the encoded html)
Unescaped the html using
var html = System.Uri.UnescapeDataString(pageHtml);

Render MVC PartialView into SignalR response

I would like to render a PartialView to an HTML string so I can return it to a SignalR ajax request.
Something like:
SignalR Hub (mySignalHub.cs)
public class mySignalRHub: Hub
{
public string getTableHTML()
{
return PartialView("_MyTablePartialView", GetDataItems()) // *How is it possible to do this*
}
}
Razor PartialView (_MyTablePartialView.cshtml)
#model IEnumerable<DataItem>
<table>
<tbody>
#foreach (var dataItem in Model)
{
<tr>
<td>#dataItem.Value1</td>
<td>#dataItem.Value2</td>
</tr>
}
</tbody>
</table>
HTML (MySignalRWebPage.html)
<Script>
...
//Get HTML from SignalR function call
var tableHtml = $.connection.mySignalRHub.getTableHTML();
//Inject into div
$('#tableContainer).html(tableHtml);
</Script>
<div id="tableContainer"></div>
My problem is that I can't seem to render a PartialView outside of a Controller. Is it even possible to render a PartialView outside of a Controller? It would be very nice to still be able to leverage the awesome HTML generating abilities that come with Razor.
Am I going about this all wrong? Is there another way?
Here, this is what I use in Controllers for ajax, I modified it a bit so it can be called from method instead of controller, method returnView renders your view and returns HTML string so you can insert it with JS/jQuery into your page when you recive it on client side:
public static string RenderPartialToString(string view, object model, ControllerContext Context)
{
if (string.IsNullOrEmpty(view))
{
view = Context.RouteData.GetRequiredString("action");
}
ViewDataDictionary ViewData = new ViewDataDictionary();
TempDataDictionary TempData = new TempDataDictionary();
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(Context, view);
ViewContext viewContext = new ViewContext(Context, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
//"Error" should be name of the partial view, I was just testing with partial error view
//You can put whichever controller you want instead of HomeController it will be the same
//You can pass model instead of null
private string returnView()
{
var controller = new HomeController();
controller.ControllerContext = new ControllerContext(HttpContext,new System.Web.Routing.RouteData(), controller);
return RenderPartialToString("Error", null, new ControllerContext(controller.Request.RequestContext, controller));
}
I didn't test it on a Hub but it should work.
Probably the best choice is to use RazorEngine, as Wim is suggesting.
public class mySignalRHub: Hub
{
public string getTableHTML()
{
var viewModel = new[] { new DataItem { Value1 = "v1", Value2 = "v2" } };
var template = File.ReadAllText(Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
#"Views\PathToTablePartialView\_MyTablePartialView.cshtml"));
return Engine.Razor.RunCompile(template, "templateKey", null, viewModel);
}
}
Further to the answer provided by #user1010609 above, I struggled through this as well and have ended up with a function that returns the rendered PartialView given a controller name, path to the view and model.
Takes account of the fact you don't have a controller and hence none of the usual state as coming from a SignalR event.
public static string RenderPartialView(string controllerName, string partialView, object model)
{
var context = new HttpContextWrapper(System.Web.HttpContext.Current) as HttpContextBase;
var routes = new System.Web.Routing.RouteData();
routes.Values.Add("controller", controllerName);
var requestContext = new RequestContext(context, routes);
string requiredString = requestContext.RouteData.GetRequiredString("controller");
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
var controller = controllerFactory.CreateController(requestContext, requiredString) as ControllerBase;
controller.ControllerContext = new ControllerContext(context, routes, controller);
var ViewData = new ViewDataDictionary();
var TempData = new TempDataDictionary();
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialView);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
You would call it with something similar to:
RenderPartialView("MyController", "~/Views/MyController/_partialView.cshtml", model);
Have you thought about using a razor template engine like http://razorengine.codeplex.com/ ?
You can't use it to parse partial views but you can use it to parse razor templates, which are almost similar to partial views.
How about using the RazorEngineHost and RazorTemplateEngine. I found this nice article that might be what you're looking for. It's about hosting Razor outside of ASP.NET (MVC).
Based on the answers supplied to asimilar question below, I would suggest using
Html.Partial(partialViewName)
It returns an MvcHtmlString, which you should able to use as the content of your SignalR reponse. I have not tested this, however.
Stack Overflow Question: Is it possible to render a view outside a controller?

Categories