Namespaces

When you run templates, you have a (possibly empty) set of variables that you have created with assign and macro and function directives (see in the previous chapter). A set of template-made variables like that is called a namespace. In simple cases you use only one namespace, the main namespace. Whenever you define a variable in the main template (macros and functions are also variables, mind you), or in templates include-d in it, that's where the variable are created. The key property of a namespace is that the variable name uniquely identifies a value in it (i.e, you can't have multiple variables in it with the same name in the same namespace).

Sometimes you want to build reusable collection of macros, functions, and other variables, which we call a library. It's important that a library can use its own namespace, to avoid accidental name clashes. Consider, you may have many names in that library, and you intend to use the library in many templates, maybe even reuse it in several projects. It becomes impractical to keep track of where the library used in another template accidentally hides variables from the data-model, or what names you shouldn't assign to in the template to avoid overwriting the variables of the library. If you have multiple libraries used in the same template, this becomes even harder to track. So you should use a separate namespace for the variables of each library.

Creating a library

Here's a simple library, which contains a copyright macro and a mail string:

Template
<#macro copyright date>
  <p>Copyright (C) ${date} Someone. All rights reserved.</p>
</#macro>

<#assign mail = "user@example.com">

Save this into the lib/example.ftl file (inside the directory where you store the templates). Then create a template, let's say, some_web_page.ftl, and use the library in it:

Template
<#import "/lib/example.ftl" as e>

Some Web page...
<@e.copyright date="1999-2002"/>
${e.mail}
Output
Some Web page...
  <p>Copyright (C) 1999-2002 Someone. All rights reserved.</p>
user@example.com

Note the import directive above, and the subsequent usage of the "e" variable. import is similar to the perhaps already familiar include directive, but it will create an empty namespace and will run lib/example.ftl in that namespace. So lib/example.ftl will find itself in a clean world, where only the variables of the data-models are visible (and the globals), and will create its two variables (copyright and mail) in this clean namespace. But you will need to access those two variables from another namespace (the main namespace), thus, the import directive creates a hash variable (e in this case) to access the namespace it has created . That variable is in the namespace that the import-ing template uses, and acts as a window to the namespace of the imported library.

To demonstrate that the two namespaces are separate, consider the example below. Replace lib/example.ftl with this:

Template
<#macro copyright date>
  <p>Copyright (C) ${date} Someone. All rights reserved.
  <br>Email: ${mail}</p>
</#macro>

<#assign mail = "user@example.com">

and some_web_page.ftl with this:

Template
<#import "/lib/example.ftl" as e>
<#assign mail="other@example.com">
<@e.copyright date="1999-2002"/>
${e.mail}
${mail}
Output
  <p>Copyright (C) 1999-2002 Someone. All rights reserved.
  <br>Email: user@example.com</p>
user@example.com
other@example.com

As you can see, the mail variable assigned in some_web_page.ftl is separate from the mail variable assigned in the imported library.

Writing the variables of imported namespaces

Sometimes you want to create or replace a variable in an imported namespace. You can do that with the assign directive and its namespace parameter:

Template
<#import "/lib/example.ftl" as e>
${e.mail}
<#assign mail="other@example.com" in e>
${e.mail}
Output
user@example.com
other@example.com

Namespaces and data-model

The variables of the data-model are visible from everywhere. For example, if you have a variable called user in the data-model, lib/example.ftl will access that, exactly like some_web_page.ftl does:

Template
<#macro copyright date>
  <p>Copyright (C) ${date} ${user}. All rights reserved.</p>
</#macro>

Assuming user is "John Doe":

Template
<#import "/lib/my_test.ftl" as my>
User is: ${user}
<@my.copyright date="1999-2002"/>
Output
User is: John Doe
  <p>Copyright (C) 1999-2002 John Doe. All rights reserved.</p>

Don't forget that the variables in the namespace (the variables you create with assign, macro, and function directives) have precedence over the variables of the data-model when you are in that namespace. So generally, if a library is interested in a data-model variable, it doesn't assign to the same name.

Note:

In some unusual applications you want to create variables in the template that are visible from all namespaces, exactly like the variables of the data-model. While templates can't change the data-model, it's possible to achieve similar effect with the global directive; see the reference.

The life-cycle of namespaces

A namespace is identified by the path used in the import directive (after it was normalized to an absolute path). If you try to import with equivalent paths for multiple times, it will create the namespace and run the template for only the first invocation of import. The later import-s with equivalent paths will just assign the same namespace to the variable specified after the as keyword. For example:

Template
<#import "/lib/example.ftl" as e>
<#import "/lib/example.ftl" as e2>
<#import "/lib/example.ftl" as e3>
${e.mail}, ${e2.mail}, ${e3.mail}
<#assign mail="other@example.com" in e>
${e.mail}, ${e2.mail}, ${e3.mail}
Output
user@example.com, user@example.com, user@example.com
other@example.com, other@example.com, other@example.com

As you access the same namespace through e, e2, and e3, the email has changed in all of them at once. The practical importance of this is that when you import the same library in multiple templates, only one namespace will be initialized and created for the library, which will be shared by all the importing templates.

Note that namespaces are not hierarchical; it doesn't mater what namespace are you in when import creates another namespace. For example, when you import namespace N2 while you are in name space N1, N2 will not be inside N1. N1 just gets the same N2 that you get if you import N2 when you are in the main namespace.

Each template processing job has its own private set of namespaces. Each template processing job is a separate universe that exists only for the short period while the main template is rendered, and then it vanishes with all its populated namespaces. Thus, whenever we say that "import is called for the first time", we always mean the first time within the lifespan of a single template processing job.

Auto-importing

When you have to import the same libraries again and again in many templates, know that the Java programmers (or whoever is responsible for configuring FreeMarker) can specify auto-imports, which are imports that are automatically done in all templates. Auto imports can also be configured to be "lazy" (since FreeMarker 2.3.25), which means that they are only done when the imported library is actually used in the template. See the Java API documentation for more details: Configuration.setAutoImports, Configuration.setLazyAutoImports.