James Grim
James Grim

Reputation: 41

RenderPartial() in T4 Template

I am wondering if its possible to take an existing partial view's logic and reuse it for dynamic email generation, within a preprocessor template?

When looking through the T4ToolKit's intellisense options for

<#@ import namespace="System.Web.Mvc" #>

Mvc's namespace does not appear, is it possible to include the namespace and call

Html.RenderPartial("viewName", this.Model)

from within a preprocessor template?

i.e.

<#@ template language="C#" #>
This is a header
<#= Html.RenderPartial("<%PATH%>/MyPartialRazerView", this.Model) #>
This is a Footer
<#+
  public MyType Model { get; set; }
#>

so I may programmatically access my template, reuse a view's display logic and build, say an email on the fly (I know the Email line is nonsense, just short hand for simplicity)

var template = MyTemplate(){ Model = MyViewModel };

Email.Send(emailAddress, title, template.TransformText(), null) etc..

TIA

Upvotes: 4

Views: 533

Answers (1)

famousgarkin
famousgarkin

Reputation: 14116

This can be done for sure, but you need to use a different extension methods than RenderPartial, because these write directly to the response. I tried Partial extension methods, which return the MvcHtmlString which works fine in the template. This is my test T4 runtime template, RuntimeTextTemplate1.tt:

<#@ template language="C#" #>
<#@ import namespace="System.Web.Mvc.Html" #>
LogOnPartial:
<#= Html.Partial("_LogOnPartial") #>

Then you also need to jump some ASP.NET MVC hoops to get the actual HtmlHelper instance into your template.

I created a partial class, to add the Html property to the template and instantiate the HtmlHelper and to provide a constructor:

public partial class RuntimeTextTemplate1
{
    public HtmlHelper Html
    {
        get;
        private set;
    }

    public RuntimeTextTemplate1(ViewContext viewContext, IViewDataContainer viewDataContainer)
    {
        Html = new HtmlHelper(viewContext, viewDataContainer);
    }
}

A HtmlHelper needs a ViewContext and IViewDataContainer to be created and those in turn have another dependencies. I provided what is needed from the controller using some dummy classes:

public class HomeController : Controller
{
    public ActionResult TemplateTest()
    {
        var viewContext = new ViewContext(ControllerContext, new DummyView(), ViewData, TempData, TextWriter.Null);
        var template = new RuntimeTextTemplate1(viewContext, new ControllerViewDataContainer(this));
        return Content(template.TransformText());
    }
}

public class DummyView : IView
{
    public void Render(ViewContext viewContext, TextWriter writer)
    {
        // Do nothing
    }
}

public class ControllerViewDataContainer : IViewDataContainer
{
    private Controller controller;

    public ViewDataDictionary ViewData
    {
        get { return controller.ViewData; }
        set { }
    }

    public ControllerViewDataContainer(Controller controller)
    {
        this.controller = controller;
    }
}

And I successfully get the template output out of it.

So while this can be done, it depends on your specific situation, how exactly you need to use it, how your view is parametrized, and how you can assemble the required classes together to get to the HtmlHelper instance.

In the end, you may find that making the template the primary source of the required output, and using this in your views and outside them is easier than the other way around.

Upvotes: 0

Related Questions