Templates in Service-now are a great feature that can save you and other users a lot of time. The out-of-box template features are documented here. As I’ve worked with customers that used templates in their implementations, I’ve noticed a few things that I think make templates in Service-now more flexible and easier to work with. Most of these things deal with the way that templates are accessed, secured, and applied. This article shows how you can apply the same customizations to your Service-now implementation that I use for my clients. This entire customization has also been packaged into an ‘Advanced Templates’ update set to save time in implementing.
This customization includes the following features:
- Improved template selection interface through the use of reference field popup
- Context-sensitive template filtering available by using a reference qualifier
- Improved Templates security setup
1) Create your ‘Template’ fields
You’ll need to create a field called ‘Template (u_template)’ on any table that you want to apply templates to. The field needs to be a reference field that references the ‘Template (sys_template)’ table. In my setup, this requires the creation of a ‘Template’ field on the following 3 tables.
- Task (task)
- Configuration item (cmdb_ci)
- Knowledge(kb_knowledge)
Because the ‘Template’ table is a system table it won’t display if you try to create the reference field from the ‘Personalize form’ or ‘Personalize list’ UI. There is a system property that you can change (glide.ui.permitted_tables) to make this table visible, but I’ve found that it’s easier to simply create the dictionary entries directly rather than having to change the system property.

2) Add ‘Template’ fields to their respective forms
Because this solution relies on an ‘onChange’ client script to identify the template, you’ll need to add the ‘Template’ field to the form of any table that you want to apply templates to. We’ll create a UI Policy in the next step to hide the field, but it does need to be a part of the form.
3) Create a UI Policy to hide the ‘Template’ field
You probably won’t want your users to even see the ‘Template’ field, but it still needs to be on the form. You can create a UI Policy to hide the field by creating a UI Policy on each table with settings as shown in the screen shot below. I created 3 UI Policies with the ‘Inherit’ checkbox selected — one for ‘task’, ‘cmdb_ci’, and ‘kb_knowledge’.

4) Create an onChange client script to apply the template
You’ll need an onChange client script that looks for a change to the ‘Template’ field and then applies the template record that gets populated. Here are the settings you’ll want for your client script. Again you’ll need a client script for each table that you will be using templates on. I used 3 client scripts with the ‘Inherited’ checkbox checked — one for ‘task’, ‘cmdb_ci’, and ‘kb_knowledge’.
Name: ApplyTemplate
Active: True
Global: True
Type: onChange
Table: Task
Inherited: True
Field name: Template
Script:
if(isLoading)
return;
try{
var template = g_form.getValue('u_template');
if(template != ''){
//Apply the selected template
applyTemplate(template);
//Wait for the template to be applied and then clear the template field
window.setTimeout(clearTemplate,2000);
}
}
catch(e){
//alert(e);
}
}
function clearTemplate(){
g_form.setValue('u_template', '');
}
5) Modify the ‘template_context’ UI Macro
Note that you may choose just to create a UI action button as described below rather than modifying this macro. In some ways, the UI action button may be better so that you don’t modify the macro and lose future updates to it.
Change the ‘XML’ field value on the ‘template_context’ UI Macro to this…
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<!--
We need to get the list of templates that can be applied so that the context submenu of
available templates can be built. This context menu is used for each section of a form.
The code below gets the list of templates and saves them in a ChoiceList as value=sys_id
and label=name. Since this operation can be costly when there are lots of templates and
we use the exact same list for each section of the form, we build the ChoiceList one time
and then save it in the 'jvar_templates' variable to be used for the other sections.
The jvar_templates variable is cleared in form.xml after it has been used.
Note: it would be better to build this list on-demand, as it is not used in the 99% case,
but doing that requires some changes to the way that the context menus work.
-->
<j2:if test="$[empty(jvar_templates)]">
<g2:evaluate var="jvar_templates" jelly="true" object="true">
<!-- generate a query that is
table='incident' and active=true and user=null and group=null
OR
table='incident' and active=true and user=me or user=one of my groups
Only way to do this currently is with an encoded query. The NQ (new query)
indicates the OR condition.
-->
if (templates_list == null) {
if (typeof GlideChoiceList != 'undefined')
var templates_list = new GlideChoiceList();
else
var templates_list = new Packages.com.glide.choice.ChoiceList();
var tt = new GlideRecord('sys_template');
var common = 'table=' + ${ref}.getTableName() + '^active=true';
var global = 'global=true';
var mine = 'user=javascript:gs.getUserID()^ORgroup=javascript:getMyGroups()';
var query = common + "^" + global + "^NQ" + common + "^" + mine;
tt.addQuery(query);
tt.orderBy('name');
tt.setWorkflow(false);
tt.query();
while (tt.nextRecord()) {
var roles = tt.roles;
if (jelly.jvar_session.hasRole(roles))
templates_list.add(tt.sys_id + '', tt.name + '');
}
}
templates_list;
</g2:evaluate>
</j2:if>
<script>
var mTemplate = new GwtContextMenu('context_templatesub${jvar_section_id}');
mTemplate.clear();
var mApplyTemplates = new GwtContextMenu('context_applytemplatesub${jvar_section_id}');
mApplyTemplates.clear();
</script>
<j2:if test="$[!jvar_templates.isEmpty()]">
<script>
mTemplate.addHref("${gs.getMessage('Apply Template')}", "openTemplateSelector()");
function openTemplateSelector(){
//Check to make sure the 'u_template' field is present on the form
if(g_form.getControl('u_template')){
//Get the table name
var tbl = g_form.getTableName() + '.u_template';
var tblDisp = 'sys_display.' + tbl;
var e = gel(tblDisp);
if (!e.ac){
new AJAXReferenceCompleter(e, tbl, 'null', 'true');
}
reflistOpen(tbl, 'u_template', 'sys_template', 'null', 'false', 'true');
}
else{
alert("There is no 'Template' field on the form.\nPlease add the 'Template [u_template]' field to the form to use templates.");
}
}
</script>
</j2:if>
<j2:if test="$[${ref}.isNewRecord() == false]">
<j2:if test="$[jvar_session.hasRole('template_editor')]">
<input type="hidden" name="sysverb_save_as_template" id="sysverb_save_as_template"/>
<script>
mTemplate.addHref("${gs.getMessage('Save as Template')}", "TemplateRecord.save(document.getElementById('sysverb_save_as_template'))");
</script>
<j2:if test="$[jvar_session.hasRole('admin')]">
<input type="hidden" name="sysverb_save_all_as_template" id="sysverb_save_all_as_template"/>
<script>
mTemplate.addHref("${gs.getMessage('Save All as Template')}", "TemplateRecord.save(document.getElementById('sysverb_save_all_as_template'))");
</script>
</j2:if>
</j2:if>
</j2:if>
<g2:evaluate var="jvar_personalize_templates" expression="gs.hasRole('template_editor,template_editor_group,template_editor_global')"/>
<j2:if test="$[jvar_personalize_templates == true ]">
<script>
mTemplate.addHref("${gs.getMessage('Edit Templates')}", "showList('sys_template', 'table.active', '${ref}.true');");
</script>
</j2:if>
<j2:if test="$[jvar_personalize_templates == true || !jvar_templates.isEmpty() ]">
<script>
gcm.addMenu("${gs.getMessage('Templates')}", mTemplate);
</script>
</j2:if>
</j:jelly>
6) Secure your template records
Create a business rule on the ‘Global’ table. This business rule will be an advanced reference qualifier for the ‘Template’ field.
Name: filterTemplates
Table: Global
Active: True
Script:
var answer = '';
//Optional switch statement can be used to apply reference qualifiers on a per-table basis
/*switch (current.getTableName()) {
case 'incident':
answer = answer + 'templateLIKEcmdb_ci=' + current.cmdb_ci + '^';
break;
default: answer = '';
}*/
var common = 'table=' + current.getTableName() + '^active=true';
answer = answer + common;
return answer;
}
Modify the ‘SNC Template Query’ business rule so that it has the following script
function roTemplates(){
//Enforce row-level read permissions for system templates
if(gs.getSession().isInteractive()){
var answer = '';
//Check to see if the user can read all templates
if(!gs.hasRole('template_editor_global')){
//Check to see if the user can read some templates
if(gs.hasRole('template_editor_group') || gs.hasRole('template_editor')){
//User can read templates for themselves, their groups, or global
answer = 'global=true^ORuser=javascript:gs.getUserID()^ORgroup=javascript:getMyGroups()';
current.addEncodedQuery(answer);
}
//Else user cannot read any templates
else{
current.addEncodedQuery('active=true^active=false');
}
}
}
}
Modify the ‘sys_template’ write and delete ACL rules by removing any ‘Requires role’ related list entries and adding the following script
if(gs.hasRole('template_editor_global')){
answer=true;
}
else{
if(gs.hasRole('template_editor') && current.user == gs.getUserID()){
answer = true;
}
if(gs.hasRole('template_editor_group') && (current.user == gs.getUserID() || gs.getUser().isMemberOf(current.group))){
answer = true;
}
}
—OPTIONAL—
Some users would rather have a more prominent option for selecting templates to apply. It is very simple to create a UI Action button to add to your forms. This has been added one to the ‘task’, ‘cmdb_ci’, and ‘kb_knowledge’ tables in the update set. The UI Actions should have the following settings…
Name: Apply Template
Table: Task
Active: True
Client: True
Form Button: True
onClick: templatePop()
Condition: gs.hasRole(‘template_editor’)
Script:
//Get the table name
var tbl = g_form.getTableName() + '.u_template';
var tblDisp = 'sys_display.' + tbl;
var e = gel(tblDisp);
if (!e.ac){
new AJAXReferenceCompleter(e, tbl, 'null', 'true');
}
reflistOpen(tbl, 'u_template', 'sys_template', 'null', 'false', 'true');
}
Related Links:
- Download: Advanced Templates
- Supporting Documentation: Installing an update set on your instance
Great!!! This is really useful when Service desk is working a lot with templates.
I confirm that we had to flush the system cache to have the script working.
Thanks
Glad to hear that it worked for you. Any feedback you have would be great.
As with all of the other info you have provided… Awesome! Mark it was nice to meet you at Knowledge10. Heard that we all passed the ITIL v3 foundation bridge exam! Congrats to us all!
Johnny Quinonez
Tulare County
This looks great! Have implemented on our DEV system and is working perfect.
I have a question though, can using this Update Set in any way affect our instance in the future, for example as new releases come out could it ‘break’ anything or cause us to lose new functionality? My thoughts are that it wouldn’t but I would love to be sure! As we gain support through someone other than Service-Now, I want to understand if it would or not as if it did break we would probably be relying on their support to fix it if we could not resolve the problem ourselves.
The only place where there may be a risk of something breaking when you upgrade would be the update to the UI macro described in step 5 above. This is a risk with any updates to a UI macro in the system however. If this is a concern to you, you could use the UI action approach described above instead and revert the UI macro back to its original state by importing the macro from another instance that doesn’t have this modification and deleting any ‘sys_update_xml’ records that correspond to that macro.
Thanks for this Mark, I was wondering why you would want to clear the Template field after it has been applied?
That’s not an absolute necessity, but it’s just not something that makes sense to store with the record since multiple templates could be applied to a single ticket. You can change that behavior if you want.
Using the global Business Rule I’m trying to filter the returned templates along the lines of
templateLIKEcmdb_ci = x OR templateLIKEcmdb_ci = null
But doesn’t work.
I’m guessing this is because the template record has lots of lines of text of which one will be
cmdb_ci = x
and the query being run is actually looking at all lines in the template field.
Any ideas how i would do the NOT LIKE bit?
The best way to investigate this is to go to your ‘sys_template’ table, personalize the list layout to include the ‘Template’ field, and see how templates are actually represented in the database. What you’ll see is an encoded query string like this…
Notice how the ‘cmdb_ci’ portion is represented with the sys_id. This makes it easy to query for templates containing a specific CI, but not so easy to exclude templates because you can’t predict what the random sys_id value will be to exclude. You may have to play around with the query a bit in the regular filter builder in a template list to see what is possible. The ‘NOT LIKE’ operator would be applied like this…
Working with templates, is there any way to capture current data, and apply it to the template, so data isn’t lost? I’ve tried setting the field like this: Field Name = javascript:(current.work_notes + ‘ /n’ + “extra information.”);
@John, I’m not aware of any good way to do that. Templates simply replace what’s in a given field.
Hi Mark,
Can you expand upon what the following lines are doing;
var e = gel(tblDisp);
if (!e.ac)
I’ve searched the wiki but haven’t been able to find an explanation of what gel is actually doing (it appears to be returning the value).
On this basis I would have assumed that e.ac would be the ac value on the record referenced by e (the template), but there doesn’t appear to be an ‘ac’ field on that table…
That’s all client-side script so it doesn’t reference any record specifically. ‘gel’ is a ServiceNow shorthand for the standard javascript ‘document.getElementByID’. In the script above, I had to make sure that the ‘u_template’ field was set up correctly to apply the template. The ‘gel’ line simply gets the element for that field from the form. Sometimes you’ll see me use the prototype equivalent instead, which is ‘$’.
Thanks Mark.
So what is e.ac?
e.ac is just the autocomplete attribute for that field. The script just checks for its existence before adding information that pertains to it.
Mark. I am working on applying this update now and am having a problem with the UI button working for ITIL users. Everything works fine for admins, but ITIL users get the following error when trying to use the button: There is no ‘Template’ on the form. Please add the ‘Template [u_template]’ field to the form to use template. I have double-checked that that field has been added the form. Any ideas?
That message will appear if the field is not on the form, but it might also appear of the user doesn’t have permissions to read from and write to the ‘u_template’ field. It sounds like you’ll probably need to double-check your security to ensure that itil users have the appropriate permissions defined.
Hi Mark, like the other individuals state, great article !!!
I have turned on in our development instance, but have been trying to filter the return list of templates.
Is it possible to send a parameter (like sysparm_query=name=SomeText) into the popup window, which then would filter the template results?
I might be missing some, but when a global filter, I didn’t see how to retrieve value(s) record, in our case incident record.
Appreciate you input and time,
Adam
Adam,
Thanks for your comment. One of the nice things about this solution is that it leverages an ordinary reference field (u_template). As such, the best way to filter available templates is to set up a reference qualifier on that field to display just the templates you need.
Mark, thanks for quick reply.
I am using the out-of-box UI Action called, Apply Template – which you have outlined above.
The reference field works great out of the box, because we only get templates created for each module (incident, change, problem, etc).
Just curious if the logic can be expanded to filter the popup list by default with an extra field, like Name = SomeText
Was trying to read up on reflistOpen method but didn’t find much information.
function templatePop(){
//Get the table name
var tbl = g_form.getTableName() + ‘.u_template’;
var tblDisp = ‘sys_display.’ + tbl;
var e = gel(tblDisp);
if (!e.ac){
new AJAXReferenceCompleter(e, tbl, ‘null’, ‘true’);
}
reflistOpen(tbl, ‘u_template’, ‘sys_template’, ‘null’, ‘false’, ‘true’);
}
I only know of 2 ways to filter it. The first is using a reference qualifier. The other method (which would work globally) would be to use a ‘before query’ business rule.
We implemented the ‘Apply Template’ UI Action button several weeks ago. Recently, though, users working from the ITIL view are unable to use the action button. They are able to apply templates by right-clicking on the header; however, when they click on the template and choose a template from the popup, nothing happens.
The button works fine from the default view, but not from the ITIL view. Has anyone noticed that?
Only recommendation I can give is to make sure you have the template field on that form and that your client script is executing. You might have some other client script that is interfering with the ‘onChange’ client script to apply the template when the ‘u_template’ field value changes.
This is a very useful article indeed!
Could someone please tell me what do the parameters on these functions stand for?
1) AJAXReferenceCompleter(e, tbl, ‘null’, ‘true’);
2) reflistOpen(tbl, ‘u_template’, ‘sys_template’, ‘null’, ‘false’, ‘true’);
I want to do something similar but for other fields, other table and with more than one column to be shown in the pop-up.
Thanks a lot!!!!!
Hi Servicenowguru,
is it possible to share my templates with my fellow team members and vice versa, or will each person have to create their own?
You should be able to by using the ‘Group’ field on the template record.
Thanks Mark, I really appreciated the prompt feedback. I was able to locate the Group field you mentioned, however it’s greyed out for us. Maybe it’s a rights issue, we will contact our manager to see. Thanks again.
Hi,
Is it possible to add this function to the Incident table? I have replicated all of the functions with the relevant changes, but when I clikc the Apply Template button nothing happens?
It is possible. Usually if there’s a problem, it’s that you haven’t added a ‘Template’ field to your form.
When I’m viewing your articles that have popup images, and click on the image the screen is being darkened over the top of the images rather than behind the images. It looks like you might need to adjust your CSS layering for the div overlay that you’re using.
I haven’t tried this with anything other than Chrome on a Mac but I’d be glad to if that would be helpful.
Earl
Any work around using this with Data Lookup, so that it nullifies dataLookup and the template wins out.
I don’t know of any workaround for that unfortunately. The data lookup functionality doesn’t leave you with much client-side flexibility unfortunately.
Hi Chris,
I’ve just used the following approach to override data look-ups.
If you add a true/false field to the task table, and a corresponding one to the matcher table, then you can ensure that the existing data look-ups only apply when the new field is set to false.
If this override field was set to true in the template then the lookup wouldn’t match and would retain the template value.
Great idea David! I always hate to create new fields for workarounds like this, but I think this is probably about as good of a solution as any to overcome the limitation.
We are in the process of implementing Service-Now. Is there a trick to have a word attachment come in when a Change Task Template is used. I am able to attach the word document to the template itself, but it does not come through when the user choses to create a Change task from the template.
The problem you’re seeing is that ServiceNow templates don’t allow for linking of more than one table. They also don’t handle attachments. I typically handle this by creating a ‘Copy’ UI action for changes, which essentially turns all of your change requests into a template. The solution is documented here…
https://servicenowguru.wpengine.com/system-ui/ui-actions-system-ui/copy-ui-action-change-requests-part-2/
At Crossfuze, we’ve implemented an enhanced version of this script with our change management product…along with several hundred additional updates to deal with common issues like this with a change management implementation. Feel free to contact us if you’d like more information or would like to see a demo.
Any reason why you didn’t use newValue in this line of the client script?
var template = g_form.getValue(‘u_template’);
vs.
var template = newValue;
or even remove that line completely and use:
applyTemplate(newValue);
No reason really other than I found a solution that worked and went with it :). Your method would work as well and would eliminate a couple of lines of code. If you’re going for ultimate optimization though you would probably still test to see if ‘newValue’ was empty before calling a potentially redundant ‘clearTemplate’ function. In the end any impact from any of these changes wouldn’t be noticeable.
Is there any way i can apply Template from server side ?
In short i want to create multiple task from server Side and apply specific template while creating using Script include
The wiki describes what you’re looking for. Any further questions about the function described there should be posted on the ServiceNow community.
https://wiki.servicenow.com/index.php?title=GlideRecord#applyTemplate
Hi Mark,
Really helpful article thank you.
Could you tell me if it is possible to use variables in the templates? So if my template sets some customer notes, but I want to include the customer name from the record or details of their department, would that be possible?
Thanks
It’s not unfortunately.
Hi Mark,
I have just tried to deploy this into our Eureka Dev environment. However, when I choose my template it populates the Incident form for about 1 second and then is overwritten so that some (not all) the fields I have pre-populated are blanked out, including the Template field. Any ideas?
The fact that it populates the form (even for 1 second) indicates that the template application is working. It sounds like you’ve got some other script or data lookup applying changes to the fields on your form. ServiceNow provides data lookups to calculate incident priority and manage ticket assignments for example. These would overwrite any other field changes so that’s the place I would look.
Hi Mark,
I have a template on the Problem table for new problems. Do you think there is a way to apply a value to the Close Notes field in a template, given that the Close Notes field is hidden until the state = resolved? I am guessing that unless the field is on the form, no template value can be applied.
Article was an interesting read.
Thanks, Mark S.
You’re right that a template won’t apply if a field is hidden. The trick to making this work for a hidden field is to put the field in a form section and show/hide the entire form section rather than the field itself.
Mark – we had ServiceNow run a health check (ACE Overview) on our instance and the Apply Template UI action came up as an issue. It said that the UI Action ‘Apply Template’ is using a DOM manipulation technique ‘gel()’ 1 time on line 5. Avoid DOM manipulation if possible as it can cause a maintainability issue when browsers are updated. Instead use the GlideForm API or consider a different approach. Can you comment on this or suggest a change that would bring it within what they consider to be best practice. It was designated as critical but that is basedon SN best practices and not necessarily an indication of the impact on the instance. Thanks.
Hey Sharon, thanks for reaching out. Don’t get me started on the SN ACE health check. 🙂 Just be aware that it generally lacks the necessary context to really give you a good idea of whether or not a particular configuration should be in place. It is true that ‘gel’ (or any DOM manipulation) puts you at a higher risk of having an upgrade issue related to that particular configuration. What the ACE report doesn’t explain is that there’s a whole spectrum of risk associated with any configuration in ServiceNow. If SN truly considered ‘gel’ to be a critical issue, I’d ask them why they allow the call to be made at all (it’s actually part of their code, not mine). The reason why is that it is helpful (and often times necessary) to accomplish a certain thing. That’s the case here. If there were a better workaround, I’d love to use it but the only one would be to use the out-of-box template functionality instead of the solution I’m providing here. Admittedly, that is a lot better since I created this solution, but this solution has been effective…without any upgrade issues…for over 7 years now.
Ultimately, you just need to decide whether the risk (potential for an upgrade issue that makes only this particular button break) outweighs the cost (having a less-than-optimal template management and application experience for all of your users) of a particular configuration. This configuration could break during a future upgrade, but the SN track record during with their own code during an upgrade would indicate that you’re just as likely to experience some issue with the core SN code anyway. Hopefully that helps. I think you could be successful either way, and it doesn’t bother me whether you use this solution or not, but I’ve always been annoyed at the way that SN presents that ACE report as the gospel truth of good SN implementation practices when the people presenting the information in that report have no idea why a particular configuration was put in the system in the first place.
Hi Mark,
Any suggestion or recommendation for UI14/UI16 ServiceNow environment where “Toggle Template Bar” is enabled and template selection is available at the footer. Can we still use this solution.
Thanks
My community post was directed here. However I’m just looking for a way to know if a template was applied to an incident. Can you tell me how I can do this as I’m not sure I am following you solution will meet my needs.