ABP Framework provides a simple, yet efficient text template system. Text templating is used to dynamically render contents based on a template and a model (a data object):
TEMPLATE + MODEL => RENDERED CONTENT
It is very similar to an ASP.NET Core Razor View (or Page):
RAZOR VIEW (or PAGE) + MODEL => HTML CONTENT
### Example
Here, a simple template:
````
Hello {{model.name}} :)
````
You can define a class with a `Name` property to render this template:
````csharp
public class HelloModel
{
public string Name { get; set; }
}
````
If you render the template with a `HelloModel` with the `Name` is `John`, the rendered output is will be:
````
Hello John :)
````
Template rendering engine is very powerful;
* It is based on the [Scriban](https://github.com/lunet-io/scriban) library, so it supports **conditional logics**, **loops** and much more.
* Template content **can be localized**.
* You can define **layout templates** to be used as the layout while rendering other templates.
* You can pass arbitrary objects to the template context (beside the model) for advanced scenarios.
Before rendering a template, you should define it. Create a class inheriting from the `TemplateDefinitionProvider` base class:
````csharp
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition("Hello") //template name: "Hello"
.WithVirtualFilePath(
"/Demos/Hello/Hello.tpl", //template content path
isInlineLocalized: true
)
);
}
}
````
*`context` object is used to add new templates or get the templates defined by depended modules. Used `context.Add(...)` to define a new template.
*`TemplateDefinition` is the class represents a template. Each template must have a unique name (that will be used while you are rendering the template).
*`/Demos/Hello/Hello.tpl` is the path of the template file.
*`isInlineLocalized` is used to declare if you are using a single template for all languages (`true`) or different templates for each language (`false`). See the Localization section below for more.
### The Template Content
`WithVirtualFilePath` indicates that we are using the [Virtual File System](Virtual-File-System.md) to store the template content. Create a `Hello.tpl` file inside your project and mark it as "**embedded resource**" on the properties window:
![hello-template](images/hello-template.png)
Example `Hello.tpl` content is shown below:
````
Hello {{model.name}} :)
````
The [Virtual File System](Virtual-File-System.md) requires to add your files in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class:
PascalCase property names (like `UserName`) is used as camelCase (like `userName`) in the templates.
## Localization
It is possible to localize a template content based on the current culture. There are two types of localization options described in the following sections.
### Inline localization
Inline localization uses the [localization system](Localization.md) to localize texts inside templates.
#### Example: Reset Password Link
Assuming you need to send an email to a user to reset her/his password. Here, the template content:
`L` function is used to localize the given key based on the current user culture. You need to define the `ResetMyPassword` key inside your localization file:
````json
"ResetMyPassword": "Click here to reset your password"
````
You also need to declare the localization resource to be used with this template, inside your template definition provider class:
> If you define the [default localization resource](Localization.md) for your application, then no need to declare the resource type for the template definition.
Instead of a single template that uses the localization system to localize the template, you may want to create different template files for each language. It can be needed if the template should be completely different for a specific culture rather than simple text localizations.
`ITemplateRenderer` service uses the current culture (`CultureInfo.CurrentUICulture`) if not specified. If you need, you can specify the culture as the `cultureName` parameter:
`model` is a dynamic object. This is using to put dynamic data into template. For more information, please look at [Scriban Documentation](https://github.com/lunet-io/scriban).
> Example: If you try to render content with _"es-MX"_ it will search your template with _"es-MX"_ culture, when it fails to find, it will try to render _"es"_ culture content. If still can't find it will render the default culture content that you defined.
When you want to get your stored template content you can use `ITemplateContentProvider`.
`ITemplateContentProvider` has one method that named `GetContentOrNullAsync` with two different overriding, and it returns you a string of template content or null. (**without rendering**)
-`templateName` (_string_) or `templateDefinition` (_`TemplateDefinition`_)
-`cultureName` (_string_)
-`tryDefaults` (_bool_)
-`useCurrentCultureIfCultureNameIsNull` (_bool_)
### Usage
First parametres of `GetContentOrNullAsync` (`templateName` or `templateDefinition`) are required, the other three parametres can be null.
If you want to get exact culture content, set `tryDefaults` and `useCurrentCultureIfCultureNameIsNull` as a `false`. Because the `GetContentOrNullAsync` tries to return content of template.
> Example Scenario
> If you have a template content that culture "`es`", when you try to get template content with "`es-MX`" it will try to return first "`es-MX`", if it fails it will return "`es`" content. If you set `tryDefaults` and `useCurrentCultureIfCultureNameIsNull` as `false` it will return `null`.
`Get` and `GetOrNull` requires a string parameter that name of template definition. `Get` will throw error when it is not exist but `GetOrNull` returns `null`.
`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering** if that is exist in your resource or must return `null`.