I am trying to dynamically load partial view contents in one of my views via a SignalR Hub to update client side data. For this I have to render the Partial view as a string in the SignalR Hub and send this string to the client side. I know how to render a partial view as a string inside a controller, but how would I do this outside of a controller?
Rendering Partial view as a string inside SignalR Hub
868 Views Asked by Robbinb1993 AtThere are 3 best solutions below
On
SignalR gives us the oppotunity to get rid of Razor Pages and use Plain Javascript to render you Html Async. So In case you want to render a template of html just write a template of function which returns the html code you want
function messageMeTemplate(user, message) {
return `
<div class="d-flex justify-content-end ms-auto">
<p class="p-3 bg-primary shadow-1-strong text-white rounded-3" style="max-width: 533px">
${message}
<small class="float-end mt-4">${user}</small>
</p>
<img src="https://mdbootstrap.com/img/new/avatars/1.jpg" class="rounded-circle ms-2" style="width: 30px; height: 30px" alt="" />
</div>
`;
}
Also you can write in a section with script in razor page of this command to load you the partial view in a div you want to take it for Javascript.
$('#AREA_PARTIAL_VIEW').load('@Url.Action("Insert_Partial_View","Customers")');
Both of the examples you need a template to render it. I would like to mention that we talk about SignalR Core.
On
This is the solution I came up with, thanks to Fei Han.
RazorPartialToStringRenderer class:
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 interface IRazorPartialToStringRenderer
{
Task<string> RenderPartialToStringAsync<TModel>(string partialName, TModel model);
}
public class RazorPartialToStringRenderer : IRazorPartialToStringRenderer
{
private IRazorViewEngine _viewEngine;
private ITempDataProvider _tempDataProvider;
private IServiceProvider _serviceProvider;
public RazorPartialToStringRenderer(
IRazorViewEngine viewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
public async Task<string> RenderPartialToStringAsync<TModel>(string partialName, TModel model)
{
var actionContext = GetActionContext();
var partial = FindView(actionContext, partialName);
using (var output = new StringWriter())
{
var viewContext = new ViewContext(
actionContext,
partial,
new ViewDataDictionary<TModel>(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary())
{
Model = model
},
new TempDataDictionary(
actionContext.HttpContext,
_tempDataProvider),
output,
new HtmlHelperOptions()
);
await partial.RenderAsync(viewContext);
return output.ToString();
}
}
private IView FindView(ActionContext actionContext, string partialName)
{
var getPartialResult = _viewEngine.GetView(null, partialName, false);
if (getPartialResult.Success)
{
return getPartialResult.View;
}
var findPartialResult = _viewEngine.FindView(actionContext, partialName, false);
if (findPartialResult.Success)
{
return findPartialResult.View;
}
var searchedLocations = getPartialResult.SearchedLocations.Concat(findPartialResult.SearchedLocations);
var errorMessage = string.Join(
Environment.NewLine,
new[] { $"Unable to find partial '{partialName}'. The following locations were searched:" }.Concat(searchedLocations));
throw new InvalidOperationException(errorMessage);
}
private ActionContext GetActionContext()
{
var httpContext = new DefaultHttpContext
{
RequestServices = _serviceProvider
};
return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
}
}
Hub and HubMethods classes:
using System;
using System.Threading.Tasks;
using System.Timers;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication
{
public class EBSHubMethods
{
private readonly IHubContext<EBSHub> _hubContext;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly Timer timer = null;
public EBSHubMethods(IHubContext<EBSHub> hubContext, IServiceScopeFactory serviceScopeFactory)
{
_hubContext = hubContext;
_serviceScopeFactory = serviceScopeFactory;
timer = new Timer();
timer.Interval = 10000;
timer.Elapsed += OnTimedEvent;
timer.Start();
}
private async Task<string> GetEBSListViewAsString()
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var _renderer = scope.ServiceProvider.GetService<IRazorPartialToStringRenderer>();
string viewAsString = await _renderer.RenderPartialToStringAsync("~/Views/EBS/_EBSPartial.cshtml", BaseStations);
return viewAsString;
}
}
private async void OnTimedEvent(Object source, ElapsedEventArgs e)
{
await _hubContext.Clients.All.SendAsync("UpdateEBSList", await GetEBSListViewAsString());
}
public async void SendEBSList(string connectionId)
{
await _hubContext.Clients.Client(connectionId).SendAsync("UpdateEBSList", await GetEBSListViewAsString());
}
}
public class EBSHub : Hub
{
private readonly EBSHubMethods _EBSHubMethods = null;
public EBSHub(EBSHubMethods EBSHubMethods)
{
_EBSHubMethods = EBSHubMethods;
}
public override async Task OnConnectedAsync()
{
_EBSHubMethods.SendEBSList(Context.ConnectionId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await base.OnDisconnectedAsync(exception);
}
}
}
And in the Startup.cs file we register the Hub, and add the EBSHubMethods as a singleton service and the RazorPartialToStringRenderer as a transient service:
services.AddSingleton<EBSHubMethods>();
services.AddTransient<IRazorPartialToStringRenderer, RazorPartialToStringRenderer>();
If you'd like to render your view/partial view as a html string within your hub method, you can refer to this blog with example that demonstrates how to render a Partial View to string.
And I did a test with the example code
RazorPartialToStringRendererin my SignalR app, which work well for me.Code referenced from above blog
Test Result