Tom W Hall
Tom W Hall

Reputation: 5283

Preserving Razor syntax highlighting in Knockout.js templates

My web application is ASP.NET MVC4 with Razor and Knockout.js. The view model hierarchy is becoming rather deep and complicated and I am moving more towards using nested Knockout templates for the corresponding views (each with a for/each for its child templates, etc) rather than monolithic slabs of HTML.

Something that annoys me is that when using the Knockout template definition syntax of <script type="text/html" id="scary-template"> the contents loses its Razor syntax highlighting in Visual Studio because it is interpreted as script. The templates are complex enough without the text all being plain black as well.

Some approaches I have considered are using Html.Raw to output the opening and closing script tags, or HtmlHelper extensions like Html.BeginKOTemplate(id) .

I'm interested to hear how other ASP.NET MVC folks approach this. Am I being pedantic? In any case the next developer who needs to pick this solution up will probably thank me for caring.

Upvotes: 1

Views: 1556

Answers (5)

Aran Mulholland
Aran Mulholland

Reputation: 23935

Use Razors partial views to overcome this problem.

<script type="text/html" id="launchTemplate">
   @Html.Partial("Templates/_LaunchView", null)
</script>

The above syntax allows you to keep your templates in a separate file. In that file you will have the full razor syntax highlighting (not to mention reformatting)

Upvotes: 0

daedalus28
daedalus28

Reputation: 1637

Personally, I use the knockout external template engine to load templates dynamically from other files. This has its own set of advantages and disadvantages, but a nice side effect is that the templates can be stored in .html files and do not need to wrapped at all - preserving all the syntax highlighting. Instead of just using the id of the script tag, the external template engine will look for files in whatever directory you tell it with the template name (such as mytemplate.html) as well.

In my experience, this also tends to make the project more organized and helps fosters code reuse between projects. The only down side is the added latency from downloading more files - which could actually increase performance through lazy loading since it will only load templates when they're needed (and will not make multiple requests for the same template)

Upvotes: 1

Tohid
Tohid

Reputation: 6679

Just a quick suggestion, separate JavaScript and Razor like I described here: https://stackoverflow.com/a/11109799/538387

Upvotes: 0

Tom W Hall
Tom W Hall

Reputation: 5283

I've gone with creating an HtmlHelper extension which returns a KnockoutTemplate class which implements IDisposable:

public static KnockoutTemplate KnockoutTemplate(this HtmlHelper htmlHelper, string templateID)
    {
        return new KnockoutTemplate(templateID, htmlHelper.ViewContext.Writer);
    }

public class KnockoutTemplate: IDisposable
{
    private TextWriter _textWriter;
    private bool _disposed;

    public KnockoutTemplate(string templateID, TextWriter textWriter)
    {
        _textWriter = textWriter;

        _textWriter.WriteLine(String.Format("<script type=\"text/html\" id=\"{0}\">", templateID));
    }

    public void EndTemplate()
    {
        Dispose(true);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            _disposed = true;
            _textWriter.WriteLine("</script>");
        }
    }
}

Used like so:

@using (Html.KnockoutTemplate("sum-assured-template"))
{
<div>Template markup...</div>
}

On further thought I didn't like the idea that with a div, the markup of the template itself would be interpreted as a template instance by the browser, so I've stuck with the script block approach for now, but at least I can change all instances in one place later if I want.

Upvotes: 1

Michael Best
Michael Best

Reputation: 16688

I don't use Visual Studio, but I have some suggestions that could work for you.

Starting with Knockout 2.1, you can define a template using the textarea tag, which will support syntax highlighting in many editors (including two I use, phpDesigner and Notepad++). To make sure the textarea doesn't actually display on the page, it must have the display:none style (or be in a container with that style).

<textarea id="mytemplate" style="display:none">
    <div>template content</div>
</textarea>

or

<div style="display:none">
    <textarea id="mytemplate">
        <div>template content</div>
    </textarea>
</div>

You can also use any tag such as div to define a template. But this adds some overhead. First, the template content will be parsed (and possibly modified) by the browser. Second, it will be parsed by Knockout unless you instruct it otherwise. You can do this by passing a container node to applyBindings that doesn't contain the template nodes, or by creating a custom binding that bypasses bindings for the template (see allowBindings here). You will still need the display:none style, of course.

<div style="display:none" data-bind="allowBindings:false">
    <div id="mytemplate">
        <div>template content</div>
    </div>
</div>

Upvotes: 5

Related Questions