TemplatedWidget Class

A widget that generates a complicated HTML markup in its constructor or other methods might lead to a class with much spaghetti code that is hard to maintain. Besides, as markup lies in program code, it might be difficult to customize output.

public class MyComplexWidget : Widget
{
	public MyComplexWidget(jQueryObject div)
    	: base(div)
    {
		var toolbar = J("<div>")
        	.Attribute("id", this.UniqueName + "_MyToolbar")
            .AppendTo(div);

        var table = J("<table>")
        	.AddClass("myTable")
            .Attribute("id", this.UniqueName + "_MyTable")
            .AppendTo(div);

        var header = J("<thead/>").AppendTo(table);
        var body = J("<tbody/>").AppendTo(table);
        ...
        ...
        ...
    }
}

Such problems can be avoided by using HTML templates. For example, lets add the following template into the HTML page:

<script id="Template_MyComplexWidget" type="text/html">
<div id="~_MyToolbar">
</div>
<table id="~_MyTable">
	<thead><tr><th>Name</th><th>Surname</th>...</tr></thead>
    <tbody>...</tbody>
</table>
</script>

Here, a SCRIPT tag is used, but by specifying its type as "text/html", browser won't recognize it as a real scriptto execute.

By making use of TemplatedWidget, lets rewrite previous spaghetti code block:

public class MyComplexWidget : TemplatedWidget
{
	public MyComplexWidget(jQueryObject div)
    	: base(div)
    {
    }
}

When this widget is created on an HTML element like following:

<div id="SampleElement">
</div>

You'll end up with such an HTML markup:

<div id="SampleElement">
    <div id="MySamples_MyComplexWidget1_MyToolbar">
    </div>
    <table id="MySamples_MyComplexWidget1_MyTable">
        <thead><tr><th>Name</th><th>Surname</th>...</tr></thead>
        <tbody>...</tbody>
    </table>
</div>

TemplatedWidget automatically locates the template for your class and applies it to the HTML element.

TemplatedWidget ID Generation

If you watch carefully, in our template we specified ID for descendant elements as ~_MyToolbar and ~_MyTable.

But when this template is applied to the HTML element, resulting markup contained ID's of MySamples_MyComplexWidget1_MyToolbar and MySamples_MyComplexWidget1_MyTable instead.

TemplatedWidget replaces prefixes like ~_ with the widget's UniqueName and underscore ("_") (this.idPrefix contains the combined prefix).

Using this strategy, even if the same widget template is used in a page for more than one HTML element, their ID's won't conflict with each other as they will have unique ID's.

TemplatedWidget.ByID Method

As TemplateWidget appends a unique name to them, the ID attributes in a widget template can't be used to access elements after widget creation.

Widget's unique name and an underscore should be prepended to the original ID attribute in the template to find an element:

public class MyComplexWidget : TemplatedWidget
{
	public MyComplexWidget(jQueryObject div)
    	: base(div)
    {
    	J(this.uniqueName + "_" + "Toolbar").AddClass("some-class");
    }
}

TemplatedWidget's ByID method can be used instead:

public class MyComplexWidget
{
	public MyComplexWidget(jQueryObject div)
    	: base(div)
    {
    	ByID("Toolbar").AddClass("some-class");
    }
}

TemplatedWidget.GetTemplateName Method

In the last sample MyComplexWidget located its template automatically.

TemplatedWidget makes use of a convention to find its template (convention based programming). It inserts Template_ prefix before the class name and searches for a SCRIPT element with this ID attribute (Template_MyComplexWidget) and uses its HTML content as a template.

If we wanted to use another ID like following:

<script id="TheMyComplexWidgetTemplate" type="text/html">
	...
</script>

An error like this would be seen in the browser console:

Can't locate template for widget 'MyComplexWidget' with name 'Template_MyComplexWidget'!

We might fix our template ID or ask the widget to use our custom ID:

public class MyComplexWidget
{
	protected override string GetTemplateName()
    {
    	return "TheMyComplexWidgetTemplate";
    }
}

TemplatedWidget.GetTemplate Method

GetTemplate method might be overriden to provide a template from another source or specify it manually:

public class MyCompleWidget
{
	protected override string GetTemplate()
    {
    	return $('#TheMyComplexWidgetTemplate').GetHtml();
    }
}

Q.GetTemplate Method and Server Side Templates

Default implementation for TemplatedWidget.GetTemplate method calls GetTemplateName and searches for a SCRIPT element with that ID.

If no such SCRIPT element is found, Q.GetTemplate is called with the same ID.

An error is thrown if neither returns a result.

Q.GetTemplate method provides access to templates defined on the server side. These templates are compiled from files with .template.cshtml extension in ~/Views/Template or ~/Modules folders or their subfolders.

For example, we could create a template for MyComplexWidget in a server side file like ~/Views/Template/SomeFolder/MyComplexWidget.template.cshtml with the following content:

<div id="~_MyToolbar">
</div>
<table id="~_MyTable">
	<thead><tr><th>Name</th><th>Surname</th>...</tr></thead>
    <tbody>...</tbody>
</table>

Template file name and extension is important while its folder is simply ignored.

By using this strategy there would be no need to insert widget templates into the page markup.

Also, as such server side templates are loaded on the first use (lazy loading) and cached in the browser and the server, page markup doesn't get polluted with templates for widgets that we might never use in a specific page. Thus, server side templates are favored over inline SCRIPT templates.