Render MVC PartialView into SignalR response - c#

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?

Related

Render ViewComponent inside html string

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);

How can I pass the results returned by a method from controller to the view

I want to use the results returned by a method (cursor in my case) from the controller in my view,
in order to draw a graph from the data returned.
I wrote a piece of code but I have not found how to pass data to the view.
//controller
[HttpPost]
public async System.Threading.Tasks.Task<ActionResult> drawgraph(Inputmodel m)
{
List<Client> client = new List<Client>();
var collection = db.GetCollection<Client>("Client");
var builder = Builders<Client>.Filter;
var beginDate = Convert.ToDateTime(m.date_begin).Date;
var endDate = Convert.ToDateTime(m.date_end).Date;
var filter = builder.Gte("date", beginDate) & builder.Lt("date", endDate.AddDays(1)) & builder.Eq("field2", m.taux);
var cursor = await collection.DistinctAsync<double>("field2",filter);
return View(cursor);
}
//view
#{
var myChart = new Chart(width:600,height: 400)
.AddTitle("graphique")
.AddSeries(chartType: "Line")
.DataBindTable(dataSource: cursor, xField: "date", yField:"field2") //here I want to use the returnet result by drawgraph in the controller
.Write();
}
You must strongly type your view to access the model :
#model YourModelTypeHere #*type of the cursor variable*#
//view
#{
var myChart = new Chart(width:600,height: 400)
.AddTitle("graphique")
.AddSeries(chartType: "Line")
.DataBindTable(dataSource: Model, xField: "date", yField:"field2") //here I want to use the returnet result by drawgraph in the controller
.Write();
}
And then you can use Model property of the web view page to get your model.
Please see this article.
You could use
in the controller
ViewBag.MyData = cursor;
//in the view:
#ViewBag.MyData
To acceess data from controller.
But the best practice is use strongly typed views.

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);

ASP.NET Razor: How do I render a Razor Partial to string? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to Render Partial View into a String
I am looking to render a Partial in my C# code to string, does anyone know how I can achieve this? The partial is just a good old partial that sits in the Partials directory and will not be used by anything else.
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter()) {
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
Also refer: http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/
You can Use HTML.Partial which returns an MvcHtmlString, now on that you can use a ToString() method and so you get a string. Do not confuse with HTML.RenderPartial();
Html.Partial or Html.RenderPartial with MVC3?

Categories