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:
<#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:
<#import "/lib/example.ftl" as e> Some Web page... <@e.copyright date="1999-2002"/> ${e.mail}
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:
<#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:
<#import "/lib/example.ftl" as e> <#assign mail="other@example.com"> <@e.copyright date="1999-2002"/> ${e.mail} ${mail}
<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:
<#import "/lib/example.ftl" as e> ${e.mail} <#assign mail="other@example.com" in e> ${e.mail}
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:
<#macro copyright date> <p>Copyright (C) ${date} ${user}. All rights reserved.</p> </#macro>
Assuming user
is "John
Doe":
<#import "/lib/my_test.ftl" as my> User is: ${user} <@my.copyright date="1999-2002"/>
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.
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:
<#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}
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.