2012-07-25 6 views
33

Ich möchte eine PartialView auf eine HTML-Zeichenfolge rendern, so dass ich es auf eine SignalR Ajax Anfrage zurückgeben kann.Rendern MVC PartialView in SignalR Antwort

Etwas wie:

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> 

Mein Problem ist, dass ich nicht scheinen kann, eine PartialView außerhalb eines Controllers zu rendern. Ist es sogar möglich, eine PartialView außerhalb eines Controllers zu rendern? Es wäre sehr schön, wenn man die genialen HTML-Fähigkeiten von Razor nutzen könnte.

Gehe ich das alles falsch? Gibt es eine andere Art und Weise?

+1

auch interessiert ... Ideen? Können Sie Teiltöne außerhalb von Controllern darstellen? –

+0

Es ist kein bequemer Weg, um fortzufahren. Es ist besser, nur kson-Daten zurückzugeben und dann eine clientseitige Vorlage zu erstellen, die sich bereits auf der Seite befindet. Das tempate wiederum kann mit einer Teilansicht gerendert werden –

Antwort

12

Hier ist das, was ich in Controllern für Ajax verwenden, änderte ich es ein bisschen so kann es von Methode statt Controller aufgerufen werden, Methode returnView rendert Ihre Ansicht und gibt HTML-Zeichenfolge zurück, so dass Sie es mit JS/jQuery einfügen können in die Seite, wenn Sie es auf Client-Seite folgendes Datum:

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

ich es nicht auf einem Hub-Test hat aber sollte es funktionieren.

+0

Ich benutze eine ähnliche Technik, um verschiedene Ansichten als Strings darzustellen, um sie als E-Mail-Nachrichten zu senden, und es hat sich bis jetzt als effektiv erwiesen. Die einzige Sache ist, es könnte schwierig werden, es von außerhalb des Controllers aufgrund des ControllerContext im Parameter aufzurufen. –

+0

@ Zweiundvierzig Ich denke, es sollte gut sein, da ich vor dem Aufruf dieser Methode instace Controller initiieren. Sie können die Instanz von ControllerContext von Grund auf sehen sehen. Es wäre schön, wenn jemand es testen würde, um es mich wissen zu lassen, da ich im Urlaub bin und es nirgendwo testen kann. – formatc

+0

Hmmm. Dies sah zunächst so aus, als ob es den Trick machen würde, aber wenn ich vollständig getestet habe, stoße ich auf Probleme: Keine Routendaten verfügbar (da die Anfrage nicht über das MVC-Routing geroutet wird). Fake-Routing hinzugefügt, um dies zu beheben. Wenn jetzt versucht wird, eine Teilansicht zu finden, wird NULLReferenz-Ausnahme erhalten ... es fühlt sich an, als ob dieser Ansatz stromaufwärts schwimmt. Ich werde einige der anderen Optionen (wie Razor-Template-Engines) untersuchen und sehen, wo mich das hinbringt. Ich werde diesen Beitrag aktualisieren, wenn ich mehr erfahren habe. – James

-1

Basierend auf den Antworten auf asimilar Frage unten geliefert, würde ich

Html.Partial (partialViewName)

Es gibt einen MvcHtmlString mit empfiehlt, die Sie als den Inhalt der Lage nutzen sollten Ihrer SignalR-Antwort. Ich habe das jedoch nicht getestet.

Stack-Überlauf Frage: Is it possible to render a view outside a controller?

+0

Der Typ des "HTML" -Objekts ist "HTMLHelper". Um dies zu instanziieren, benötigen Sie einen ViewContext. Um einen ViewContext zu instanziieren, benötigen Sie einen ControllerContext ... zurück zum selben Problem. Kein Controller. Danke fürs Hinschauen! – James

1

Haben Sie sich Gedanken über eine Rasierklinge Template-Engine wie http://razorengine.codeplex.com/ mit? Sie können es nicht Teilansichten analysieren verwenden, aber man kann es Rasierapparat Vorlagen zu analysieren verwenden, die Teilansichten auf fast ähnlich sind.

+0

Interessant, danke. Siehe Kommentar zu Paolo's Post unten. Ich werde vielleicht weiter testen, aber es scheint einige potenzielle Probleme mit diesem Projekt zu geben. – James

4

Wahrscheinlich die beste Wahl ist, RazorEngine zu verwenden, wie Wim vorschlägt.

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); 
    } 
} 
+1

Sieht interessant aus. Diese Bibliothek hat jedoch Probleme mit der gleichzeitigen Verwendung, die in einer Webumgebung nicht gut funktioniert. Siehe diesen Thread für Details: http://stackoverflow.com/questions/6444277/using-razorengin-to-parse-razor-templates-concurrently. Der Autor dieser Engine hat eine Neufassung gemacht, um diese Probleme anzugehen, aber es scheint keine Bewegung in diesem Projekt für eine Weile gegeben zu haben - könnte jetzt ein totes Projekt sein: https://github.com/Antaris/RazorEngine – James

+1

@James RazorEngine v3 (das aktuelle [NuGet-Paket] (http://nuget.org/packages/RazorEngine)) sollte im Multi-Thread-Szenario gut funktionieren. Sie müssen lediglich einen 'TemplateService' instanziieren, anstatt die statische Methode' Razor.Parse' aufzurufen. Sehen Sie [diesen Beitrag] (http://www.fidelitydesign.net/?p=473) für weitere Details. –

0

Wie über die RazorEngineHost und RazorTemplateEngine verwenden. Ich habe dieses nice article gefunden, wonach Sie suchen könnten. Es geht darum, Razor außerhalb von ASP.NET (MVC) zu hosten.

3

Weiter zu der Antwort von @ user1010609 oben, habe ich mich auch damit durchgekämpft und bin mit einer Funktion gelandet, die die gerenderte PartialView mit einem Controller-Namen, Pfad zur Ansicht und zum Modell zurückgibt.

Berücksichtigt die Tatsache, dass Sie keine Steuerung haben und daher keiner der üblichen Zustände aus einem SignalR-Ereignis stammt.

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

Sie würden es mit etwas Ähnliches rufen:

RenderPartialView("MyController", "~/Views/MyController/_partialView.cshtml", model);