Custom Tags
JNTemplate makes it easy to define custom tags. This document assumes you have some basic knowledge of IL. A custom tag requires the following four objects:
- A tag class, which must implement the
ITaginterface. - A tag parsing delegate:
Func<TemplateParser, Nodes.TokenCollection, Nodes.ITag> - A tag compiling delegate:
Func<ITag, CompileContext, System.Reflection.MethodInfo> - A tag type inference delegate:
Func<ITag, CompileContext, Type> guessFunc
Below we implement a custom tag written as ${:text}. Its job is to output the text after the colon directly. Use this demo to get familiar with the process of defining your own JNTemplate tag.
Define the tag class
Define a class that implements ITag. For convenience we recommend also inheriting from the tag base class Tag, and you can define any additional parameters you need.
public class TestTag : Tag, ITag
{
/// <summary>
/// Defines a property Document that holds the text after the colon
/// </summary>
public string Document { get; set; }
}Define the parsing delegate
This delegate tells the engine how to parse your tag.
Func<TemplateParser, Nodes.TokenCollection, Nodes.ITag> action = (p, tc) => {
//check whether this is our custom TestTag
if (tc.Count == 2 && tc.First.Text == ":")
{
return new TestTag
{
Document = tc[1].Text
};
}
//otherwise return NULL
return null;
}TIP
If the current collection does not match your custom tag condition, just return null.
Define the compiling delegate
This delegate tells the engine how to compile the tag you defined. This step requires some IL knowledge.
Func<ITag, CompileContext, MethodInfo> action1 = (tag, c) => {
var t = tag as TestTag;
var mb = c.CreateReutrnMethod<TestTag>(typeof(string));//define a method
var il = mb.GetILGenerator();//get the IL generator
il.Emit(OpCodes.Ldstr, t.Document); //push Document onto the stack as a string reference
il.Emit(OpCodes.Ret);//return the result
return mb.GetBaseDefinition();//generate the method
}Define the inference delegate
This delegate tells the engine what type the tag returns.
//TestTag returns a string, so no inference is needed
Func<ITag, CompileContext, Type> action2 = (tag, c) => typeof(string);Register the tag
Once everything above is defined, you can register your tag with the Engine.Register<ITag>(...) method.
Engine.Register<TestTag>(action,action1,action2);Output
At this point the custom tag is complete. Run the following code to see it in action:
var templateContent = "${:hello}";
var template = Engine.CreateTemplate(templateContent);
var render = template.Render();
Assert.Equal("hello", render);WARNING
Note: custom tags are an advanced feature of the engine. If you are not familiar enough with the engine, we do not recommend using it — an incorrect implementation can degrade the performance of the whole engine.