Compiling a spark expression within a string

565 Views Asked by At

I have an ASP.NET MVC2 application within which I am trying to use the Spark View Engine to render from an input string, e.g.:

"!{Html.ActionLink(\"A Link\", \"Index\")} Followed by some text"

I run into problems when trying to utilize HtmlHelpers. The spark compiler returns the error 'The name 'Html' does not exist in the current context'. The full method is below:

    public ActionResult Index()
    {
        var templates = new InMemoryViewFolder();
        var engine = new SparkViewEngine() { ViewFolder = templates };

        var stringResult = new StringBuilder();
        stringResult.AppendLine("!{Html.ActionLink(\"A Link\", \"Index\")} Followed by some text");

        templates.Add("string.spark", stringResult.ToString());

        var descriptor = new SparkViewDescriptor().AddTemplate("string.spark");
        var view = engine.CreateInstance(descriptor);
        var result = new StringBuilder();
        StringWriter sw = new StringWriter(result);
        view.RenderView(sw);

        return Content(result.ToString());
    }

I have had success if I create an Index.spark file within the Views folder, using System.Web.Mvc.ViewEngines, and utilize HtmlHelpers from there, so I am guessing there is something I am missing with the setup of my SparkViewEngine within the Index method above.

I cannot put these contents into a file as I will be assembling them at runtime.

Thank you in advance!

3

There are 3 best solutions below

0
On

You need to add namespaces/assemblies to Spark engine, it doesn't automatically add System.Web and others (you construct it manually and have to specify all config manually, it won't read web.config). Also you'll also need to provide HttpContext, because HtmlHelper won't work without it. Sorry can't remember now in details how this all should be done, refer here how to setup assemblies/namespaces.

But it will be much easier if you provide a view data (view model) instead. Either pass base url as parameter, or pass a function (method of view model, Func<>) that view will invoke to construct required URL. Another way, specify master template for you in-memory engine that will re-define all the entities, i.e. will set Html to your custom MemoryHtmlHelper which will contain same methods such as ActionLink, but will work without HttpContext.

0
On

I was able to find a workable solution (pitfalls yet to be discovered). I used the existing SparkViewEngine from Mvc.ViewEngines and temporarily replace its ViewFolder property with an InMemoryViewFolder. It did the trick:

    public ActionResult Index()
    {
        SparkViewFactory f = (SparkViewFactory)ViewEngines.Engines.First(x => x is SparkViewFactory);
        InMemoryViewFolder templates = new InMemoryViewFolder();
        templates.Add("string.spark", "${Html.ActionLink(\"Testing\", \"Index\")} Html Helpers");

        IViewFolder savedViewFolder = f.Engine.ViewFolder;
        f.Engine.ViewFolder = templates;
        SparkViewDescriptor descriptor = new SparkViewDescriptor().AddTemplate("string.spark");
        var view = (SparkView)f.Engine.CreateInstance(descriptor);

        f.Engine.ReleaseInstance(view);
        f.Engine.ViewFolder = savedViewFolder;

        return View(view, null);            
    }
0
On

I'd be careful manipulating the registered Spark view engine in this case. I'm not sure what side effects you'll see by swapping in and out a different ViewFolder.

I'm doing something similar to render dynamic views - although it's in an HtmlHelper - so I have access to the ViewContext:

public static void RenderDynamic(this HtmlHelper html)
{
    string dynamicViewContent = BuildDynamicSparkView();  //build your dynamic content here
    var descriptor = new SparkViewDescriptor().AddTemplate("sample.spark");

    var inMemoryViewFolder = new InMemoryViewFolder();
    inMemoryViewFolder.Add("sample.spark", dynamicViewContent);

    var sparkViewEngine = new SparkViewEngine();
    sparkViewEngine.ViewFolder = inMemoryViewFolder;

    var view = (SparkView)sparkViewEngine.CreateInstance(descriptor);
    view.Render(html.ViewContext, html.ViewContext.Writer);

    sparkViewEngine.ReleaseInstance(view);
}