Skip to content

Commit 97d56e0

Browse files
committed
add a custom mapping recipe
1 parent 9d3a52d commit 97d56e0

File tree

5 files changed

+279
-93
lines changed

5 files changed

+279
-93
lines changed

docs/04.guides/04.cookbooks/02.application-context-set-mapping/page.md

Lines changed: 0 additions & 78 deletions
This file was deleted.

docs/recipes/component-mappings.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
"id": "mappings-component-mappings",
55
"related": [
66
"mappings-how-to-define-a-reg-mapping",
7+
"mappings-custom-tag-mappings",
78
"tag-application",
8-
"function-expandpath",
9-
"cookbook-application-context-set-mapping"
9+
"function-expandpath"
1010
],
1111
"categories": [
1212
"application",
@@ -104,7 +104,7 @@ Component mappings can also be defined in the Application.cfc file, making them
104104
```cfs
105105
// Application.cfc
106106
component {
107-
this.componentpaths = [
107+
this.componentPaths = [
108108
{
109109
"physical": "{lucee-config}/components/",
110110
"archive": "",
@@ -136,7 +136,7 @@ You can package your components into a Lucee Archive (.lar) file and reference i
136136
```cfs
137137
// Application.cfc
138138
component {
139-
this.componentpaths = [
139+
this.componentPaths = [
140140
{
141141
physical: getDirectoryFromPath(getCurrentTemplatePath()) & 'components',
142142
archive: getDirectoryFromPath(getCurrentTemplatePath()) & 'components.lar',
@@ -152,14 +152,16 @@ With `primary` set to "archive", Lucee first checks the .lar file for components
152152

153153
The `inspectTemplate` attribute controls how Lucee checks for changes to your components:
154154

155-
* **never**: Never checks for changes
156-
* **once**: Checks once per request
157-
* **always**: Always checks for changes (can impact performance)
155+
* **never**: Never checks for changes (best for production)
156+
* **once**: Checks once per request (default)
157+
* **always**: Always checks for changes (best for development, can impact performance)
158+
159+
By default, mappings inherit the server-level `inspectTemplate` setting. You can override this per mapping if needed.
158160

159161
```cfs
160162
// Application.cfc
161163
component {
162-
this.componentpaths = [
164+
this.componentPaths = [
163165
{
164166
physical: getDirectoryFromPath(getCurrentTemplatePath()) & 'components',
165167
inspectTemplate: 'once'
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
<!--
2+
{
3+
"title": "Custom Tag Mappings",
4+
"id": "mappings-custom-tag-mappings",
5+
"related": [
6+
"mappings-component-mappings",
7+
"mappings-how-to-define-a-reg-mapping",
8+
"tag-application",
9+
"tag-import",
10+
"import"
11+
],
12+
"categories": [
13+
"application",
14+
"tags",
15+
"server"
16+
],
17+
"description": "How to define and use custom tag mappings in Lucee.",
18+
"keywords": [
19+
"Custom Tag Mapping",
20+
"Custom Tags",
21+
"cfimport",
22+
"Application.cfc",
23+
"Lucee archive",
24+
"taglib"
25+
]
26+
}
27+
-->
28+
29+
# Custom Tag Mappings
30+
31+
Custom tag mappings in Lucee define locations where the server will look for CFML custom tags when you use them in your code.
32+
33+
They provide a way to organize and access your custom tags without having to specify the full path each time or use [[tag-import]] repeatedly.
34+
35+
## Understanding Custom Tag Mappings
36+
37+
Custom tags are reusable CFML templates that you can call like built-in tags. Custom tag mappings tell Lucee where to find these custom tag files.
38+
39+
For example, if you have a custom tag file at:
40+
41+
```
42+
/path/to/your/app/customtags/myTag.cfm
43+
```
44+
45+
With a custom tag mapping pointing to `/path/to/your/app/customtags/`, you can use the tag in your code like:
46+
47+
```coldfusion
48+
<cf_myTag attribute="value">
49+
```
50+
51+
Like other mapping types in Lucee (regular mappings and component mappings), custom tag mappings consist of several parts:
52+
53+
- **physical**: Physical directory where custom tag files are stored
54+
- **archive**: A Lucee archive (.lar file) that contains the custom tags; a .lar file is the same as a .jar file in Java, containing the compiled templates (and optionally the source)
55+
- **primary**: When both "physical" and "archive" are defined, this setting determines where to look first for a custom tag; by default, it looks first in "physical"; possible values are "physical" and "archive"
56+
- **readonly**: Determines if the mapping can be configured in the Lucee admin or not (not needed for mappings defined in Application.cfc)
57+
- **hidden**: Controls visibility in the Lucee admin (not needed for mappings defined in Application.cfc)
58+
- **inspectTemplate**: Controls Lucee's behavior when checking for changes
59+
60+
## Defining Custom Tag Mappings
61+
62+
### In the Lucee Administrator
63+
64+
Custom tag mappings can be defined in the Lucee Server or Web Administrator. Go to the **Archives & Resources / Custom Tag** page.
65+
66+
Mappings defined in the Server Administrator are visible to all web contexts, while mappings defined in the Web Administrator are only visible to the current web context.
67+
68+
The default mapping for custom tags is under `{lucee-config}/customtags/`, where the `{lucee-config}` is a [[configuration-directory-placeholders]] which resolves to your `lucee-server\context` directory.
69+
70+
```cfml
71+
dump( expandPath( "{lucee-config}" ) );
72+
```
73+
74+
### Using CFConfig
75+
76+
Custom tag mappings can be defined in a CFConfig JSON file:
77+
78+
```json
79+
{
80+
"customTagMappings": [
81+
{
82+
"physical": "/path/to/customtags/",
83+
"archive": "",
84+
"primary": "physical",
85+
"inspectTemplate": "always"
86+
}
87+
]
88+
}
89+
```
90+
91+
Remember, the Lucee Admin is a GUI for `.CFConfig.json`.
92+
93+
### In Application.cfc
94+
95+
Custom tag mappings can also be defined in the `Application.cfc` file, making them specific to the current application:
96+
97+
```cfs
98+
// Application.cfc
99+
component {
100+
this.customTagPaths = [
101+
getDirectoryFromPath( getCurrentTemplatePath() ) & "customtags"
102+
];
103+
}
104+
```
105+
106+
**Important**: Unlike `this.mappings` (which uses structs), `this.customTagPaths` takes an **array** because there is no "virtual path" that needs to be defined. Custom tags are found by filename in any of the mapped directories.
107+
108+
## Using Custom Tags
109+
110+
Once you've defined your custom tag mappings, you can use custom tags in your code:
111+
112+
```cfml
113+
<!-- Using the cf_ prefix notation -->
114+
<cf_myCustomTag attribute="value">
115+
116+
<!-- Or with closing tag -->
117+
<cf_myCustomTag attribute="value">
118+
Some content here
119+
</cf_myCustomTag>
120+
```
121+
122+
Custom tags can also be invoked using `cfmodule`:
123+
124+
```cfml
125+
<cfmodule template="myCustomTag.cfm" attribute="value">
126+
```
127+
128+
## Using cfimport for Prefixed Custom Tags
129+
130+
Instead of using the `cf_` prefix, you can import custom tags with a custom prefix using `cfimport`:
131+
132+
```cfml
133+
<cfimport prefix="my" taglib="/customtags/">
134+
135+
<!-- Then use with your custom prefix -->
136+
<my:customTag attribute="value">
137+
```
138+
139+
Or in script syntax:
140+
141+
```cfml
142+
cfimport( prefix="my", taglib="/customtags/" );
143+
```
144+
145+
**Note**: The `import` keyword cannot be used for custom tags—only `cfimport` supports the `prefix` and `taglib` attributes.
146+
147+
The `cfimport` approach is useful when:
148+
149+
- You want to use a more descriptive prefix than `cf_`
150+
- You have custom tags from different sources and want to namespace them
151+
- You want to make it clear where a custom tag comes from in your code
152+
153+
## Advanced Usage
154+
155+
### Using Archives
156+
157+
You can package your custom tags into a Lucee Archive (.lar) file and reference it in your custom tag mapping:
158+
159+
```cfs
160+
// Application.cfc
161+
component {
162+
this.customTagPaths = [
163+
{
164+
physical: getDirectoryFromPath( getCurrentTemplatePath() ) & 'customtags',
165+
archive: getDirectoryFromPath( getCurrentTemplatePath() ) & 'customtags.lar',
166+
primary: 'archive'
167+
}
168+
];
169+
}
170+
```
171+
172+
With `primary` set to "archive", Lucee first checks the .lar file for custom tags. If not found there, it looks in the physical path.
173+
174+
This is particularly useful for:
175+
176+
- Deploying compiled custom tags
177+
- Protecting source code
178+
- Improving performance by reducing file system checks
179+
180+
### InspectTemplate Options
181+
182+
The `inspectTemplate` attribute controls how Lucee checks for changes to your custom tags:
183+
184+
- **auto**: Monitors the filesystem for changes (default)
185+
- **never**: Never checks for changes (best for production)
186+
- **once**: Checks filesystem once per request
187+
- **always**: Slow, checks the filesystem everytime the templates are accessed.
188+
189+
By default, mappings inherit the server-level `inspectTemplate` setting. You can override this per mapping if needed.
190+
191+
```cfs
192+
// Application.cfc
193+
component {
194+
this.customTagPaths = [
195+
{
196+
physical: getDirectoryFromPath( getCurrentTemplatePath() ) & 'customtags',
197+
inspectTemplate: 'never'
198+
}
199+
];
200+
}
201+
```
202+
203+
### Multiple Custom Tag Paths
204+
205+
You can define multiple custom tag paths, and Lucee will search them in order:
206+
207+
```cfs
208+
// Application.cfc
209+
component {
210+
this.customTagPaths = [
211+
getDirectoryFromPath( getCurrentTemplatePath() ) & "customtags",
212+
expandPath( "/shared/customtags" ),
213+
getDirectoryFromPath( getCurrentTemplatePath() ) & "vendor/tags"
214+
];
215+
}
216+
```
217+
218+
When you use a custom tag, Lucee will search each path in order until it finds the matching custom tag file.
219+
220+
## Creating Custom Tags
221+
222+
Custom tags are simply CFML templates that can access special variables:
223+
224+
- `attributes` scope: Contains all attributes passed to the custom tag
225+
- `caller` scope: Allows the custom tag to read and write variables in the calling template
226+
- `thisTag` scope: Contains metadata about the custom tag execution
227+
228+
Example custom tag (`customtags/greeting.cfm`):
229+
230+
```coldfusion
231+
<cfparam name="attributes.name" default="World">
232+
233+
<cfoutput>
234+
Hello, #attributes.name#!
235+
</cfoutput>
236+
```
237+
238+
Usage:
239+
240+
```coldfusion
241+
<cf_greeting name="Lucee">
242+
<!-- Outputs: Hello, Lucee! -->
243+
```
244+
245+
## Best Practices
246+
247+
1. **Use Application.cfc for application-specific tags**: Define custom tag paths in Application.cfc to keep them version-controlled and portable with your application.
248+
249+
2. **Set inspectTemplate appropriately**: Use **auto** or **once** during development, but **never** in production for better performance.
250+
251+
3. **Organize tags by purpose**: Consider organizing custom tags into subdirectories by functionality (e.g., `customtags/forms/`, `customtags/layout/`).
252+
253+
4. **Use cfimport for clarity**: When working with multiple tag libraries, use `cfimport` with descriptive prefixes to make your code more readable.
254+
255+
## Differences from Component and Regular Mappings
256+
257+
| Feature | Regular Mappings | Component Mappings | Custom Tag Mappings |
258+
|---------|------------------|-------------------|-------------------|
259+
| Application.cfc Property | `this.mappings` | `this.componentPaths` | `this.customTagPaths` |
260+
| Data Structure | Struct (with virtual paths) | Array | Array |
261+
| Usage | `cfinclude`, `cffile`, etc. | `new`, `createObject()` | `cf_tagname`, `cfimport` |
262+
| Requires Virtual Path | Yes | No | No |

0 commit comments

Comments
 (0)