<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://baral.me/feed.xml" rel="self" type="application/atom+xml" /><link href="http://baral.me/" rel="alternate" type="text/html" /><updated>2025-07-22T18:14:39+00:00</updated><id>http://baral.me/feed.xml</id><title type="html">Israel Baral</title><subtitle>Israel Baral&apos;s personal website, blog, resume, and portfolio. </subtitle><author><name>Israel Baral</name></author><entry><title type="html">Creating Dynamic UI Elements with HTML Variables in ServiceNow Catalog Items</title><link href="http://baral.me/blog/post-servicenow_html_variable_blog/" rel="alternate" type="text/html" title="Creating Dynamic UI Elements with HTML Variables in ServiceNow Catalog Items" /><published>2025-07-21T15:07:04+00:00</published><updated>2025-07-21T15:07:04+00:00</updated><id>http://baral.me/blog/post-servicenow_html_variable_blog</id><content type="html" xml:base="http://baral.me/blog/post-servicenow_html_variable_blog/"><![CDATA[<h1 id="creating-dynamic-ui-elements-with-html-variables-in-servicenow-catalog-items">Creating Dynamic UI Elements with HTML Variables in ServiceNow Catalog Items</h1>

<p>So I ran into this interesting problem the other day that got me thinking about creative ways to use HTML variables in ServiceNow. You know how sometimes the obvious solution just doesn’t feel quite right? Well, this was one of those times.</p>

<h2 id="the-problem-i-was-trying-to-solve">The Problem I Was Trying to Solve</h2>

<p>We needed to build a contract renewal form where managers could renew existing contracts. Simple enough, right? But here’s the thing - for contractor renewals specifically, the managers wanted to be able to quickly see past contracts so they could check when rates had changed over time.</p>

<p>My first thought was to just throw in a reference field or maybe a plain text link. But honestly? That felt pretty boring and didn’t really fit the user experience we were going for. I wanted something that looked good, felt integrated, and actually helped users get their work done faster.</p>

<h2 id="why-i-didnt-go-the-proper-route">Why I Didn’t Go the “Proper” Route</h2>

<p>Now, before you ask - yes, I could have built a custom widget for this. That would probably be the “correct” ServiceNow approach. But here’s the thing: widgets are inherently complex beasts. You’re talking about server-side scripts, client controllers, HTML templates, CSS files - suddenly what should be a simple dynamic link becomes a whole project.</p>

<p>Plus, custom widgets don’t always play nice with record producers. I’ve seen too many cases where a widget works perfectly in Service Portal but breaks or behaves weirdly when embedded in a catalog item. And let’s be honest - widgets require ongoing maintenance. Every time ServiceNow updates, there’s a chance something could break, and now you’re debugging widget dependencies instead of focusing on actual business logic.</p>

<p>Sometimes the “hacky” solution that works reliably and requires minimal maintenance is actually the pragmatic choice, especially for smaller features like this.</p>

<h2 id="what-i-came-up-with">What I Came Up With</h2>

<p>Here’s where it gets fun. I realized that HTML variables don’t have to be static - you can actually update them dynamically with client scripts. It’s like having a little canvas where you can paint whatever HTML you want, whenever you want.</p>

<p>So I decided to create a dynamic button that would change based on what the user typed in the contractor name field. When they enter a name, boom - a nice-looking button appears that takes them directly to a filtered list of that contractor’s past contracts.</p>

<h2 id="how-i-built-it">How I Built It</h2>

<p>Let me walk you through what I did:</p>

<h3 id="setting-up-the-html-variable">Setting Up the HTML Variable</h3>

<p>First, I created an HTML variable in the catalog item. Nothing fancy here:</p>
<ul>
  <li>Made it read-only (obviously - we don’t want users messing with our HTML)</li>
  <li>Left the name field blank (had to use sn-utils to get around the mandatory field thing)</li>
  <li>Called it <code class="language-plaintext highlighter-rouge">link_to_past_contracts</code></li>
</ul>

<h3 id="the-magic-client-script-time">The Magic: Client Script Time</h3>

<p>This is where the fun happens. I wrote an onChange client script that runs whenever a new contract is selected (which automatically updates the name field):</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">onChange</span><span class="p">(</span><span class="nx">control</span><span class="p">,</span> <span class="nx">oldValue</span><span class="p">,</span> <span class="nx">newValue</span><span class="p">,</span> <span class="nx">isLoading</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">isLoading</span> <span class="o">||</span> <span class="nx">newValue</span> <span class="o">===</span> <span class="dl">''</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
    
    <span class="c1">// Build the URL with the contractor name filter</span>
    <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="s2">`
        &lt;a style="color: #FFFFFF;"
           href="/now/nav/ui/classic/params/target/ast_contract_list.do?sysparm_query=short_descriptionLIKE</span><span class="p">${</span><span class="nx">newValue</span><span class="p">}</span><span class="s2">^ORDERBYDESCstarts"
           target="_blank"&gt;
          </span><span class="p">${</span><span class="nx">newValue</span><span class="p">}</span><span class="s2">'s past contracts
        &lt;/a&gt;
    `</span><span class="p">;</span>
    
    <span class="c1">// Wrap it in a nice-looking button</span>
    <span class="kd">const</span> <span class="nx">button</span> <span class="o">=</span> <span class="s2">`
        &lt;div style="text-align: center;"&gt;
          &lt;button
            sp-ellipsis-tooltip="Past Contracts"
            sp-ellipsis-tooltip-title="Past Contracts"
            name="Past Contracts"
            class="btn btn-primary sc-btn text-overflow-ellipsis ng-scope"
            data-toggle="tooltip"
            data-placement="auto"
            data-container="body"
            title=""&gt;
            &lt;i class="icon-save pull-left cart-icon-margin" aria-hidden="true"&gt;&lt;/i&gt;
            </span><span class="p">${</span><span class="nx">url</span><span class="p">}</span><span class="s2">
          &lt;/button&gt;
        &lt;/div&gt;
    `</span><span class="p">;</span>
    
    <span class="c1">// Update our HTML variable with the shiny new button</span>
    <span class="nx">g_form</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">link_to_past_contracts</span><span class="dl">'</span><span class="p">,</span> <span class="nx">button</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="whats-actually-happening-here">What’s Actually Happening Here</h2>

<p>The cool part here is that because the html field is set to read only the edit bar isn’t visible, and the html appears to be part of the form. Additionally, because the field name is blank, that doesn’t show either so the button doesn’t look like it’s a field at all. The button itself uses ServiceNow’s standard CSS classes, so it looks like it belongs there. Finally by using <code class="language-plaintext highlighter-rouge">target="_blank"</code> it opens in a new tab, so users don’t lose their place in the form.</p>

<p>Here’s what it looks like:</p>

<p><img src="/assets/images/html_variable.png" alt="screenshot of the button" /></p>

<h2 id="why-i-like-this-approach">Why I Like This Approach</h2>

<p>There are a few things that make this solution pretty neat:</p>

<p><strong>It looks native</strong> - Using ServiceNow’s existing CSS classes means the button doesn’t stick out like a sore thumb</p>

<p><strong>It’s responsive</strong> - As soon as someone types a name, the button updates automatically</p>

<p><strong>It’s cleverly hacky</strong> - We’re basically overloading the HTML variable type to do something it wasn’t designed for, which is oddly satisfying</p>

<p><strong>It’s flexible</strong> - You could easily adapt this pattern for all sorts of other scenarios</p>

<h2 id="other-ways-you-could-use-this">Other Ways You Could Use This</h2>

<p>Once I got this working, I started thinking about all the other places this pattern could be useful. Sometimes you need dynamic UI elements but don’t want the overhead of building full custom widgets.</p>

<p>Really, anywhere you want to show contextual information or links that change based on user input without the complexity and maintenance burden of custom widgets.</p>

<h2 id="a-few-things-to-keep-in-mind">A Few Things to Keep in Mind</h2>

<p>If you decide to try this out, here are some things I learned along the way:</p>

<p><strong>Watch out for special characters</strong> - Make sure you’re properly encoding any dynamic values that go into URLs. You don’t want someone’s name with an apostrophe breaking your query.</p>

<p><strong>Test on mobile</strong> - The button styling might need some tweaks for smaller screens.</p>

<p><strong>Don’t go overboard</strong> - This is great for specific use cases, but don’t try to rebuild your entire UI this way.</p>

<p><strong>Think about accessibility</strong> - I included tooltip attributes and proper labels, which is always a good idea.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>I have to say, I was pretty happy with how this turned out. Sometimes the best solutions come from thinking outside the box a little and using platform features in ways they weren’t necessarily designed for.</p>

<p>HTML variables are usually just for static content, but with a little creativity and some client script magic, they can become powerful tools for creating dynamic, interactive experiences. Plus, it was way easier than building a custom widget from scratch.</p>

<p>Have you ever found yourself using ServiceNow features in unexpected ways? I’d love to hear about it - these kinds of creative solutions are often the most interesting and useful ones.</p>]]></content><author><name>Israel Baral</name></author><category term="blog" /><category term="servicenow" /><category term="catalog-items" /><category term="html-variables" /><category term="client-scripts" /><category term="ui-development" /><summary type="html"><![CDATA[Creating Dynamic UI Elements with HTML Variables in ServiceNow Catalog Items]]></summary></entry><entry><title type="html">Calculating Approval Duration in ServiceNow: Community Solutions</title><link href="http://baral.me/blog/post-servicenow-approval-duration-guide/" rel="alternate" type="text/html" title="Calculating Approval Duration in ServiceNow: Community Solutions" /><published>2025-06-09T15:07:04+00:00</published><updated>2025-06-09T15:07:04+00:00</updated><id>http://baral.me/blog/post-servicenow-approval-duration-guide</id><content type="html" xml:base="http://baral.me/blog/post-servicenow-approval-duration-guide/"><![CDATA[<blockquote>
  <h3 id="attribution">Attribution</h3>
  <p>[DISCLAIMER] This is an AI-generated blog post based on a Slack conversation. I’ve created this to preserve and share the valuable knowledge discussed, here are those who contributed:<br />
@eric<br />
@romeo<br />
@vk<br />
Here’s a link to the original discussion (might be dead): <a href="https://sndevs.slack.com/archives/C0E2G2681/p1749156330572709">Slack Discussion</a></p>
</blockquote>

<h1 id="how-to-calculate-approval-duration-in-servicenow">How to Calculate Approval Duration in ServiceNow</h1>

<p>When you need to track how long an approval takes from request to completion in ServiceNow, you have several technical approaches to choose from. This guide walks through the best practices for calculating approval duration based on state changes.</p>

<h2 id="the-challenge">The Challenge</h2>

<p>You want to measure the time it takes for an approval to move from “requested” to “approved” state. The key challenge is capturing the exact timestamp when the state first changes to “requested” and then calculating the duration when it changes to “approved.”</p>

<h2 id="solution-options">Solution Options</h2>

<h3 id="option-1-business-rules-simple-snapshot-approach">Option 1: Business Rules (Simple Snapshot Approach)</h3>

<p><strong>Best for:</strong> Single point-in-time calculations where you only need the final duration after approval is complete.</p>

<p><strong>Implementation:</strong></p>
<ul>
  <li>Create a “Duration” field on your approval table</li>
  <li>Use an <strong>After</strong> Business Rule that triggers when the approval state changes to “approved”</li>
  <li>Calculate the duration between the initial request time and approval time</li>
</ul>

<p><strong>Pros:</strong></p>
<ul>
  <li>Simple to implement</li>
  <li>Low performance impact</li>
  <li>Good if you only need final duration data</li>
</ul>

<p><strong>Cons:</strong></p>
<ul>
  <li>No data available until approval is completed</li>
  <li>Doesn’t track intermediate state durations</li>
</ul>

<h3 id="option-2-metrics-recommended-for-comprehensive-tracking">Option 2: Metrics (Recommended for Comprehensive Tracking)</h3>

<p><strong>Best for:</strong> When you want to track duration in every state and need historical reporting capabilities.</p>

<p><strong>Why Metrics Are Ideal:</strong></p>
<ul>
  <li>Automatically captures state changes without custom fields</li>
  <li>Provides historical data for all state transitions</li>
  <li>Built-in reporting capabilities</li>
  <li>No performance impact from calculated fields</li>
</ul>

<p><strong>Implementation Steps:</strong></p>

<ol>
  <li><strong>Create a Metric Definition:</strong>
    <ul>
      <li>Navigate to <code class="language-plaintext highlighter-rouge">metric_definition</code> table</li>
      <li>Set up metric to track your approval state field</li>
      <li>Configure to capture duration for each state</li>
    </ul>
  </li>
  <li><strong>View Metric Data:</strong>
    <ul>
      <li>Metric instances are stored in <code class="language-plaintext highlighter-rouge">metric_instance</code> table</li>
      <li>Can be displayed as related lists on approval records</li>
      <li>Use database views (like <code class="language-plaintext highlighter-rouge">incident_metric</code>) for reporting</li>
    </ul>
  </li>
  <li><strong>Find Reference Examples:</strong>
    <ul>
      <li>Search existing relationships: <code class="language-plaintext highlighter-rouge">/sys_relationship_list.do?sysparm_query=basic_query_fromLIKEmetric&amp;sysparm_view=</code></li>
      <li>Look at out-of-the-box tables that already use metrics as related lists</li>
    </ul>
  </li>
</ol>

<h3 id="option-3-calculated-fields-not-recommended">Option 3: Calculated Fields (Not Recommended)</h3>

<p><strong>Why to avoid:</strong> Calculated fields trigger on every record update, causing significant performance issues, especially on high-volume tables.</p>

<h3 id="option-4-flows-alternative-custom-approach">Option 4: Flows (Alternative Custom Approach)</h3>

<p><strong>When to use:</strong> If you need custom logic beyond what metrics provide.</p>

<p><strong>Implementation:</strong></p>
<ul>
  <li>Create custom datetime fields to capture state change timestamps</li>
  <li>Use Flow to populate timestamp when state changes to “requested”</li>
  <li>Calculate duration when state changes to “approved”</li>
</ul>

<h2 id="handling-the-requested-timestamp-challenge">Handling the “Requested” Timestamp Challenge</h2>

<p>A common issue is determining when the approval was first “requested,” since records might be created in different states.</p>

<p><strong>Solutions:</strong></p>
<ol>
  <li><strong>Use Metrics:</strong> Automatically handles this by tracking all state changes</li>
  <li><strong>Custom Field + Flow:</strong> Create a “Requested Date” field and populate it via Flow when state first changes to “requested”</li>
  <li><strong>Avoid using Created Date:</strong> Records may be created in states other than “requested”</li>
</ol>

<h2 id="best-practice-recommendation">Best Practice Recommendation</h2>

<p><strong>Use Metrics</strong> for approval duration tracking because they:</p>
<ul>
  <li>Require no custom field development</li>
  <li>Provide comprehensive state duration data</li>
  <li>Offer built-in reporting capabilities</li>
  <li>Scale well with high record volumes</li>
  <li>Give you flexibility for future analytics needs</li>
</ul>

<h2 id="implementation-checklist">Implementation Checklist</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Create metric definition for your approval state field</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Test metric instance generation with state changes</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Set up related list display on approval records (optional)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Create database views for reporting (if needed)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Verify performance with your expected record volumes</li>
</ul>

<p>This approach gives you a robust, scalable solution for tracking approval durations without the performance overhead of calculated fields or the limitations of simple business rules.</p>

<!--   Thursday at 3:45 PM
Quick question - I need to calculate the duration of approval(state changes from approval->approved) for which i create a "duration" type field. Do i use a business rule to calculate the duration or use "calculated value" feature on the field level? (edited) 
17 replies
  Thursday at 3:47 PM
if you just need the 1 snapshot in time after the approval is approved you want a BR (as long as you are Ok with not having any data until it is approved). but a metric would be good if you want to know how long it was in every state
:mvp:  Thursday at 3:49 PM
calculated field may cause performance issues since it will trigger for every update of every record (edited) 
  Thursday at 3:49 PM
got it. So an after or before business rule?
  Thursday at 3:50 PM
why not async?
3:51
or a Flow
  Thursday at 3:55 PM
hmm now i'm starting to think how to get the time when state was "requested". I was thinking to use the createdBy time but the record could be created with state=not requested :confused:
:mvp:  Thursday at 3:57 PM
you can create a custom date time field and populate it when state is requested using a flow.
  Thursday at 3:59 PM
that is a reason that running a Metric on the state field may be your solution
3:59
no custom fields needed to get the data
  Thursday at 4:26 PM
got it. So if i were to create a metric, it needs to be reported of the metric_definition table correct? or can i be shown as a related list on the approval record?
  Thursday at 4:27 PM
metric_definition is the definition, you want the metric_instance and yes, it can be shown as a related list if you want
4:29
most commonly for reporting a database view is used to combine the tables like incident_metric  does
  Thursday at 4:50 PM
thanks. Are there any ootb tables that show metrics as a related list so i can use for reference?
  Thursday at 5:03 PM
/sys_relationship_list.do?sysparm_query=basic_query_fromLIKEmetric&sysparm_view=
  Thursday at 5:38 PM
!pp
APP  Thursday at 5:38 PM
Way to help out
@Romeo
you now have 41 points (1515 total) :star2:
Congrats
@eric
you now have 73 points (3892 total) :first_place_medal:
  Thursday at 5:38 PM
thanks -->]]></content><author><name>Israel Baral</name></author><category term="blog" /><category term="servicenow" /><category term="approvals" /><category term="metrics" /><category term="business-rules" /><category term="ai-generated" /><summary type="html"><![CDATA[Attribution [DISCLAIMER] This is an AI-generated blog post based on a Slack conversation. I’ve created this to preserve and share the valuable knowledge discussed, here are those who contributed: @eric @romeo @vk Here’s a link to the original discussion (might be dead): Slack Discussion]]></summary></entry><entry><title type="html">SP Record Picker Docs</title><link href="http://baral.me/blog/post-sn-record-picker-documentation/" rel="alternate" type="text/html" title="SP Record Picker Docs" /><published>2025-05-20T15:10:04+00:00</published><updated>2025-05-20T15:10:04+00:00</updated><id>http://baral.me/blog/post-sn-record-picker-documentation</id><content type="html" xml:base="http://baral.me/blog/post-sn-record-picker-documentation/"><![CDATA[<blockquote>
  <h3 id="attribution">Attribution</h3>
  <p>[DISCLAIMER] This is an AI-generated blog post based on importing scripts/sn/common/controls/directive.snRecordPicker.js into claude and asking it to generate a document explaining how to use it.</p>
</blockquote>

<h1 id="servicenow-snrecordpicker-directive-documentation">ServiceNow snRecordPicker Directive Documentation</h1>

<h2 id="overview">Overview</h2>

<p>The <code class="language-plaintext highlighter-rouge">snRecordPicker</code> directive is an AngularJS component that provides an enhanced reference field selector for ServiceNow Service Portal applications. It creates a searchable dropdown that allows users to select records from a specified table in ServiceNow.</p>

<h2 id="features">Features</h2>

<ul>
  <li>Dynamic record searching with server-side filtering</li>
  <li>Single or multiple record selection</li>
  <li>Customizable display fields</li>
  <li>Accessibility support</li>
  <li>Configurable query parameters</li>
  <li>Support for default values</li>
  <li>Event handling for value changes</li>
</ul>

<h2 id="directive-usage">Directive Usage</h2>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sn-record-picker</span> 
    <span class="na">table=</span><span class="s">"table_name"</span>
    <span class="na">field=</span><span class="s">"fieldObject"</span>
    <span class="na">default-query=</span><span class="s">"encoded_query"</span>
    <span class="na">startswith=</span><span class="s">"true|false"</span>
    <span class="na">search-fields=</span><span class="s">"comma,separated,fields"</span>
    <span class="na">value-field=</span><span class="s">"field_name"</span>
    <span class="na">display-field=</span><span class="s">"field_name"</span>
    <span class="na">display-fields=</span><span class="s">"comma,separated,fields"</span>
    <span class="na">page-size=</span><span class="s">"20"</span>
    <span class="na">on-change=</span><span class="s">"functionName()"</span>
    <span class="na">sn-disabled=</span><span class="s">"true|false"</span>
    <span class="na">multiple=</span><span class="s">"true|false"</span>
    <span class="na">options=</span><span class="s">"optionsObject"</span>
    <span class="na">placeholder=</span><span class="s">"Placeholder text"</span>
    <span class="na">sn-aria-label=</span><span class="s">"Accessibility label"</span>
    <span class="na">multiple-value-delimiter=</span><span class="s">","</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sn-record-picker&gt;</span>
</code></pre></div></div>

<h2 id="parameters">Parameters</h2>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Type</th>
      <th>Required</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">table</code></td>
      <td>String</td>
      <td>Yes</td>
      <td>The table name to query records from</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">field</code></td>
      <td>Object</td>
      <td>No</td>
      <td>The field object that will store the selected value(s)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">defaultQuery</code></td>
      <td>String</td>
      <td>No</td>
      <td>Default encoded query to filter results</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">startswith</code></td>
      <td>Boolean</td>
      <td>No</td>
      <td>If true, search uses STARTSWITH instead of CONTAINS</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">searchFields</code></td>
      <td>String</td>
      <td>No</td>
      <td>Comma-separated list of fields to search</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">valueField</code></td>
      <td>String</td>
      <td>No</td>
      <td>Field to use as the value (default: ‘sys_id’)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">displayField</code></td>
      <td>String</td>
      <td>No</td>
      <td>Field to use for display</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">displayFields</code></td>
      <td>String</td>
      <td>No</td>
      <td>Comma-separated list of additional fields to display</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">pageSize</code></td>
      <td>Number</td>
      <td>No</td>
      <td>Number of records per page (default: 20)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">onChange</code></td>
      <td>Function</td>
      <td>No</td>
      <td>Function to call when selection changes</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">snDisabled</code></td>
      <td>Boolean</td>
      <td>No</td>
      <td>If true, the control is disabled</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">multiple</code></td>
      <td>Boolean</td>
      <td>No</td>
      <td>If true, allows multiple selections</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">options</code></td>
      <td>Object</td>
      <td>No</td>
      <td>Additional configuration options</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">placeholder</code></td>
      <td>String</td>
      <td>No</td>
      <td>Placeholder text to display when empty</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">snAriaLabel</code></td>
      <td>String</td>
      <td>No</td>
      <td>Accessibility label for screen readers</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">multipleValueDelimiter</code></td>
      <td>String</td>
      <td>No</td>
      <td>Delimiter for multiple values (default: ‘,’)</td>
    </tr>
  </tbody>
</table>

<h2 id="field-object-structure">Field Object Structure</h2>

<p>The <code class="language-plaintext highlighter-rouge">field</code> parameter expects an object with the following properties:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nl">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">field_name</span><span class="dl">"</span><span class="p">,</span>      <span class="c1">// Name of the field</span>
  <span class="nx">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">selected_value</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// Selected value(s) (sys_id by default)</span>
  <span class="nx">displayValue</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Display Value</span><span class="dl">"</span> <span class="c1">// Display value(s) for the selected item(s)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For multiple selections, <code class="language-plaintext highlighter-rouge">value</code> contains comma-separated values and <code class="language-plaintext highlighter-rouge">displayValue</code> contains tokenized display values.</p>

<h2 id="options-object">Options Object</h2>

<p>The <code class="language-plaintext highlighter-rouge">options</code> parameter accepts the following properties:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nl">cache</span><span class="p">:</span> <span class="kc">true</span><span class="o">|</span><span class="kc">false</span><span class="p">,</span>    <span class="c1">// Whether to cache API results</span>
  <span class="nx">allowClear</span><span class="p">:</span> <span class="kc">true</span><span class="o">|</span><span class="kc">false</span> <span class="c1">// Whether to allow clearing the selection</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="events">Events</h2>

<p>The directive emits the following events:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">field.change</code>: Emitted when the field value changes</li>
  <li><code class="language-plaintext highlighter-rouge">field.change.[fieldname]</code>: Field-specific change event</li>
  <li><code class="language-plaintext highlighter-rouge">select2.ready</code>: Emitted when the select2 initialization is complete</li>
</ul>

<h2 id="examples">Examples</h2>

<h3 id="basic-usage">Basic Usage</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sn-record-picker</span> 
    <span class="na">table=</span><span class="s">"incident"</span> 
    <span class="na">field=</span><span class="s">"data.selectedIncident"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sn-record-picker&gt;</span>
</code></pre></div></div>

<h3 id="advanced-configuration">Advanced Configuration</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sn-record-picker</span> 
    <span class="na">table=</span><span class="s">"sys_user"</span> 
    <span class="na">field=</span><span class="s">"data.selectedUser"</span>
    <span class="na">search-fields=</span><span class="s">"name,email,employee_number"</span>
    <span class="na">display-field=</span><span class="s">"name"</span>
    <span class="na">display-fields=</span><span class="s">"email,title,department.name"</span>
    <span class="na">default-query=</span><span class="s">"active=true"</span>
    <span class="na">page-size=</span><span class="s">"50"</span>
    <span class="na">on-change=</span><span class="s">"c.userSelected()"</span>
    <span class="na">multiple=</span><span class="s">"true"</span>
    <span class="na">options=</span><span class="s">"{cache: true, allowClear: true}"</span>
    <span class="na">placeholder=</span><span class="s">"Select users..."</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sn-record-picker&gt;</span>
</code></pre></div></div>

<h3 id="within-client-controller">Within Client Controller</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span><span class="p">(</span><span class="nx">$scope</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">c</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
  
  <span class="nx">$scope</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">selectedUser</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">user_field</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">value</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span>
      <span class="na">displayValue</span><span class="p">:</span> <span class="dl">''</span>
    <span class="p">}</span>
  <span class="p">};</span>
  
  <span class="nx">c</span><span class="p">.</span><span class="nx">userSelected</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Selected user: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">selectedUser</span><span class="p">.</span><span class="nx">displayValue</span><span class="p">);</span>
    <span class="c1">// Perform additional actions with the selected value</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="accessibility-features">Accessibility Features</h2>

<p>The directive includes several accessibility enhancements:</p>

<ul>
  <li>ARIA attributes for screen reader support</li>
  <li>Keyboard navigation</li>
  <li>Focus management</li>
  <li>Accessible labels for removal actions</li>
  <li>Screen reader announcements for state changes</li>
</ul>

<h2 id="internal-implementation-details">Internal Implementation Details</h2>

<p>The directive uses the Select2 library to create the enhanced dropdown interface and makes REST API calls to the ServiceNow instance to fetch records. It handles query building, result formatting, and value management internally.</p>

<h2 id="notes-and-best-practices">Notes and Best Practices</h2>

<ol>
  <li>Always set a meaningful <code class="language-plaintext highlighter-rouge">displayField</code> to improve user experience</li>
  <li>Use <code class="language-plaintext highlighter-rouge">searchFields</code> to optimize search performance</li>
  <li>Use <code class="language-plaintext highlighter-rouge">defaultQuery</code> to pre-filter available options</li>
  <li>For complex displays, use <code class="language-plaintext highlighter-rouge">displayFields</code> to show additional information</li>
  <li>Consider enabling caching for frequently accessed tables</li>
  <li>For reference fields that may contain many records, adjust <code class="language-plaintext highlighter-rouge">pageSize</code> accordingly</li>
  <li>Use the <code class="language-plaintext highlighter-rouge">onChange</code> callback for dependent field logic</li>
  <li>Set appropriate <code class="language-plaintext highlighter-rouge">placeholder</code> text to guide users</li>
</ol>

<h2 id="troubleshooting">Troubleshooting</h2>

<ul>
  <li>If selections are not being saved, ensure the <code class="language-plaintext highlighter-rouge">field</code> object has the correct structure</li>
  <li>If search results are unexpected, check the <code class="language-plaintext highlighter-rouge">searchFields</code> and <code class="language-plaintext highlighter-rouge">defaultQuery</code> parameters</li>
  <li>For performance issues, consider increasing <code class="language-plaintext highlighter-rouge">pageSize</code> or enabling caching</li>
  <li>If display values are not appearing correctly, verify the <code class="language-plaintext highlighter-rouge">displayField</code> exists in the table</li>
</ul>

<h2 id="dependencies">Dependencies</h2>

<ul>
  <li>AngularJS</li>
  <li>Select2 library</li>
  <li>ServiceNow REST API</li>
  <li>ServiceNow UI Framework components</li>
</ul>]]></content><author><name>Israel Baral</name></author><category term="blog" /><category term="servicenow" /><category term="service portal" /><category term="angularJS" /><category term="directive" /><category term="record-picker" /><category term="ai-generated" /><summary type="html"><![CDATA[Attribution [DISCLAIMER] This is an AI-generated blog post based on importing scripts/sn/common/controls/directive.snRecordPicker.js into claude and asking it to generate a document explaining how to use it.]]></summary></entry><entry><title type="html">SP Date Picker Docs</title><link href="http://baral.me/blog/post-sp-datepicker-docs/" rel="alternate" type="text/html" title="SP Date Picker Docs" /><published>2025-05-19T15:10:04+00:00</published><updated>2025-05-19T15:10:04+00:00</updated><id>http://baral.me/blog/post-sp-datepicker-docs</id><content type="html" xml:base="http://baral.me/blog/post-sp-datepicker-docs/"><![CDATA[<blockquote>
  <h3 id="attribution">Attribution</h3>
  <p>[DISCLAIMER] This is an AI-generated blog post based on importing scripts/app.$sp/directive.spDatePicker.js into claude and asking it to generate a document explaining how to use it.</p>
</blockquote>

<h1 id="servicenow-spdatepicker-directive-documentation">ServiceNow spDatePicker Directive Documentation</h1>

<h2 id="overview">Overview</h2>

<p>The <code class="language-plaintext highlighter-rouge">spDatePicker</code> directive is an AngularJS component that provides a comprehensive date and time picker interface for ServiceNow Service Portal applications. It extends the functionality of a standard HTML input field with calendar and time selection capabilities, complete with internationalization, accessibility features, and keyboard navigation support.</p>

<h2 id="basic-usage">Basic Usage</h2>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sp-date-picker</span> 
    <span class="na">field=</span><span class="s">"fieldObject"</span>
    <span class="na">sn-disabled=</span><span class="s">"false"</span>
    <span class="na">sn-include-time=</span><span class="s">"true"</span>
    <span class="na">sn-change=</span><span class="s">"onDateChange(newValue)"</span>
    <span class="na">sn-max-date=</span><span class="s">"maxDate"</span>
    <span class="na">sn-min-date=</span><span class="s">"minDate"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sp-date-picker&gt;</span>
</code></pre></div></div>

<h2 id="attributes">Attributes</h2>

<table>
  <thead>
    <tr>
      <th>Attribute</th>
      <th>Type</th>
      <th>Required</th>
      <th>Default</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">field</code></td>
      <td>Object</td>
      <td>Yes</td>
      <td>-</td>
      <td>The field object containing date information and validation properties</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sn-disabled</code></td>
      <td>Boolean</td>
      <td>No</td>
      <td><code class="language-plaintext highlighter-rouge">false</code></td>
      <td>When true, disables the date picker input</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sn-include-time</code></td>
      <td>Boolean</td>
      <td>No</td>
      <td><code class="language-plaintext highlighter-rouge">false</code></td>
      <td>When true, includes time selection (hours, minutes, seconds)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sn-change</code></td>
      <td>Function</td>
      <td>No</td>
      <td>-</td>
      <td>Callback function triggered when the date value changes</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sn-max-date</code></td>
      <td>Date/String</td>
      <td>No</td>
      <td>-</td>
      <td>Maximum selectable date</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">sn-min-date</code></td>
      <td>Date/String</td>
      <td>No</td>
      <td>-</td>
      <td>Minimum selectable date</td>
    </tr>
  </tbody>
</table>

<h2 id="field-object-properties">Field Object Properties</h2>

<p>The <code class="language-plaintext highlighter-rouge">field</code> object should contain the following properties:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="nl">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">field_name</span><span class="dl">"</span><span class="p">,</span>           <span class="c1">// Field identifier</span>
    <span class="nx">label</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Date Field</span><span class="dl">"</span><span class="p">,</span>          <span class="c1">// Display label for accessibility</span>
    <span class="nx">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">2024-01-15</span><span class="dl">"</span><span class="p">,</span>         <span class="c1">// Current field value</span>
    <span class="nx">placeholder</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Select date</span><span class="dl">"</span><span class="p">,</span>   <span class="c1">// Placeholder text</span>
    <span class="nx">isMandatory</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{},</span>   <span class="c1">// Function returning if field is required</span>
    <span class="nx">isInvalidDateFormat</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>   <span class="c1">// Flag for format validation errors</span>
    <span class="nx">isInvalid</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>            <span class="c1">// General validation error flag</span>
    <span class="nx">stagedValue</span><span class="p">:</span> <span class="dl">""</span>              <span class="c1">// Temporarily stored value during editing</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="features">Features</h2>

<h3 id="date-and-time-selection">Date and Time Selection</h3>
<ul>
  <li><strong>Date Only</strong>: Select year, month, and day</li>
  <li><strong>Date and Time</strong>: Additionally select hours, minutes, and seconds</li>
  <li><strong>Multiple Views</strong>: Switch between day, month, and year selection views</li>
</ul>

<h3 id="keyboard-navigation">Keyboard Navigation</h3>

<p>The directive supports extensive keyboard navigation:</p>

<h4 id="date-navigation">Date Navigation</h4>
<ul>
  <li><strong>Arrow Keys</strong>: Navigate between dates/months/years
    <ul>
      <li>Left/Right: Previous/Next day</li>
      <li>Up/Down: Previous/Next week</li>
      <li>Page Up/Down: Previous/Next month</li>
      <li>Alt + Page Up/Down: Previous/Next year</li>
    </ul>
  </li>
</ul>

<h4 id="time-navigation">Time Navigation</h4>
<ul>
  <li><strong>Arrow Keys</strong>: Adjust time values
    <ul>
      <li>Up/Down: Increment/Decrement minutes</li>
      <li>Page Up/Down: Increment/Decrement hours</li>
    </ul>
  </li>
</ul>

<h4 id="special-keys">Special Keys</h4>
<ul>
  <li><strong>Alt + Up/Down</strong>: Toggle between date and time picker</li>
  <li><strong>Enter</strong>: Select current highlighted option</li>
  <li><strong>Space</strong>: Close picker (apply selection)</li>
  <li><strong>Escape</strong>: Cancel and close picker</li>
  <li><strong>Tab</strong>: Navigate through focusable elements</li>
</ul>

<h3 id="accessibility-features">Accessibility Features</h3>

<ul>
  <li><strong>ARIA Labels</strong>: Full screen reader support</li>
  <li><strong>Live Regions</strong>: Announce changes to screen readers</li>
  <li><strong>Keyboard Navigation</strong>: Complete keyboard accessibility</li>
  <li><strong>Focus Management</strong>: Proper focus handling for modal interactions</li>
  <li><strong>Tooltips</strong>: Descriptive tooltips for all interactive elements</li>
</ul>

<h3 id="internationalization">Internationalization</h3>

<ul>
  <li>Supports multiple locales through moment.js</li>
  <li>Translatable date formats and messages</li>
  <li>Locale-specific date formatting</li>
  <li>RTL (Right-to-Left) language support</li>
</ul>

<h3 id="validation">Validation</h3>

<ul>
  <li><strong>Format Validation</strong>: Ensures dates match expected format</li>
  <li><strong>Range Validation</strong>: Respects min/max date constraints</li>
  <li><strong>Real-time Feedback</strong>: Immediate validation on input change</li>
  <li><strong>Error States</strong>: Visual and accessible error indicators</li>
</ul>

<h2 id="usage-examples">Usage Examples</h2>

<h3 id="basic-date-picker">Basic Date Picker</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sp-date-picker</span> <span class="na">field=</span><span class="s">"vm.dateField"</span><span class="nt">&gt;&lt;/sp-date-picker&gt;</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Controller</span>
<span class="nx">vm</span><span class="p">.</span><span class="nx">dateField</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">start_date</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">label</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Start Date</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">value</span><span class="p">:</span> <span class="dl">'</span><span class="s1">2024-01-15</span><span class="dl">'</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="date-and-time-picker">Date and Time Picker</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sp-date-picker</span> 
    <span class="na">field=</span><span class="s">"vm.dateTimeField"</span>
    <span class="na">sn-include-time=</span><span class="s">"true"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sp-date-picker&gt;</span>
</code></pre></div></div>

<h3 id="with-constraints-and-change-handler">With Constraints and Change Handler</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sp-date-picker</span> 
    <span class="na">field=</span><span class="s">"vm.dateField"</span>
    <span class="na">sn-min-date=</span><span class="s">"vm.minDate"</span>
    <span class="na">sn-max-date=</span><span class="s">"vm.maxDate"</span>
    <span class="na">sn-change=</span><span class="s">"vm.handleDateChange(newValue)"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sp-date-picker&gt;</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Controller</span>
<span class="nx">vm</span><span class="p">.</span><span class="nx">minDate</span> <span class="o">=</span> <span class="nx">moment</span><span class="p">().</span><span class="nx">format</span><span class="p">(</span><span class="dl">'</span><span class="s1">YYYY-MM-DD</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">vm</span><span class="p">.</span><span class="nx">maxDate</span> <span class="o">=</span> <span class="nx">moment</span><span class="p">().</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="dl">'</span><span class="s1">year</span><span class="dl">'</span><span class="p">).</span><span class="nx">format</span><span class="p">(</span><span class="dl">'</span><span class="s1">YYYY-MM-DD</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">vm</span><span class="p">.</span><span class="nx">handleDateChange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">newValue</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Date changed to:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">newValue</span><span class="p">);</span>
    <span class="c1">// Handle date change logic</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="disabled-state">Disabled State</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;sp-date-picker</span> 
    <span class="na">field=</span><span class="s">"vm.dateField"</span>
    <span class="na">sn-disabled=</span><span class="s">"vm.isReadOnly"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/sp-date-picker&gt;</span>
</code></pre></div></div>

<h2 id="configuration">Configuration</h2>

<h3 id="date-formats">Date Formats</h3>

<p>The directive uses ServiceNow’s global date format settings:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">g_user_date_format</code>: User’s preferred date format</li>
  <li><code class="language-plaintext highlighter-rouge">g_user_date_time_format</code>: User’s preferred date-time format</li>
  <li>Falls back to system defaults if user preferences not set</li>
</ul>

<p>Supported format tokens (converted to moment.js format):</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Y/y</code> → Year</li>
  <li><code class="language-plaintext highlighter-rouge">M</code> → Month</li>
  <li><code class="language-plaintext highlighter-rouge">D/d</code> → Day</li>
  <li><code class="language-plaintext highlighter-rouge">H/h</code> → Hour</li>
  <li><code class="language-plaintext highlighter-rouge">m</code> → Minute</li>
  <li><code class="language-plaintext highlighter-rouge">s</code> → Second</li>
  <li><code class="language-plaintext highlighter-rouge">A/a</code> → AM/PM</li>
</ul>

<h3 id="global-settings">Global Settings</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Enable/disable date translation</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">NOW</span><span class="p">.</span><span class="nx">sp</span><span class="p">.</span><span class="nx">enableDateTranslation</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

<span class="c1">// Enable/disable date validation</span>
<span class="nx">g_datepicker_validation_enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="events">Events</h2>

<h3 id="dpchange">dp.change</h3>
<p>Fired when the selected date changes:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">element</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">dp.change</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">New date:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span><span class="p">.</span><span class="nx">date</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Old date:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span><span class="p">.</span><span class="nx">oldDate</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h3 id="dpshowdphide">dp.show/dp.hide</h3>
<p>Fired when the picker opens or closes:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">element</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">dp.show</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Picker opened</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">element</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">dp.hide</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Picker closed</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h2 id="styling">Styling</h2>

<p>The directive uses Bootstrap classes and ServiceNow-specific CSS:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* Main container */</span>
<span class="nc">.sp-date-input-group</span> <span class="p">{</span> <span class="p">}</span>
<span class="nc">.input-group</span> <span class="p">{</span> <span class="p">}</span>

<span class="c">/* Input field */</span>
<span class="nc">.form-control</span> <span class="p">{</span> <span class="p">}</span>

<span class="c">/* Picker button */</span>
<span class="nc">.datepickericon</span> <span class="p">{</span> <span class="p">}</span>

<span class="c">/* Error states */</span>
<span class="nc">.has-error</span> <span class="p">{</span> <span class="p">}</span>

<span class="c">/* Format info */</span>
<span class="nc">.sp-date-format-info</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>

<h2 id="browser-support">Browser Support</h2>

<ul>
  <li>Chrome (full support)</li>
  <li>Safari (with polite aria-live regions)</li>
  <li>Firefox</li>
  <li>Internet Explorer 11+ (with touch event handling)</li>
  <li>Mobile browsers (responsive design)</li>
</ul>

<h2 id="dependencies">Dependencies</h2>

<ul>
  <li>AngularJS 1.x</li>
  <li>moment.js</li>
  <li>Bootstrap 3.x</li>
  <li>ServiceNow’s spAriaUtil service</li>
  <li>ServiceNow’s i18n service</li>
  <li>jQuery (for DOM manipulation)</li>
</ul>

<h2 id="error-handling">Error Handling</h2>

<p>The directive handles various error states:</p>

<ol>
  <li><strong>Invalid Date Format</strong>: Shows format warning message</li>
  <li><strong>Out of Range</strong>: Prevents selection of dates outside min/max bounds</li>
  <li><strong>Parse Errors</strong>: Gracefully handles malformed date strings</li>
  <li><strong>Network Issues</strong>: Handles dependency loading failures</li>
</ol>

<h2 id="performance-considerations">Performance Considerations</h2>

<ul>
  <li>Lazy loading of locale-specific dependencies</li>
  <li>Debounced scroll event handlers</li>
  <li>Efficient DOM manipulation with jQuery</li>
  <li>Memory cleanup on directive destruction</li>
</ul>

<h2 id="security-notes">Security Notes</h2>

<ul>
  <li>All user input is validated and sanitized</li>
  <li>XSS protection through Angular’s built-in mechanisms</li>
  <li>No direct DOM innerHTML usage for user content</li>
</ul>

<h2 id="migration-notes">Migration Notes</h2>

<p>When upgrading from older versions:</p>
<ol>
  <li>Check date format configurations</li>
  <li>Verify accessibility attributes</li>
  <li>Test keyboard navigation</li>
  <li>Validate internationalization settings</li>
</ol>

<h2 id="troubleshooting">Troubleshooting</h2>

<h3 id="common-issues">Common Issues</h3>

<ol>
  <li><strong>Date not displaying</strong>: Check that <code class="language-plaintext highlighter-rouge">field.value</code> is in correct format</li>
  <li><strong>Validation errors</strong>: Verify date format matches user locale</li>
  <li><strong>Accessibility issues</strong>: Ensure proper ARIA labels are set</li>
  <li><strong>Mobile rendering</strong>: Check viewport meta tag configuration</li>
</ol>

<h3 id="debug-mode">Debug Mode</h3>

<p>Enable debug logging:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// In browser console</span>
<span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="dl">'</span><span class="s1">sp.datepicker.debug</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">true</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="related-components">Related Components</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">spFormField</code>: Base form field component</li>
  <li><code class="language-plaintext highlighter-rouge">spAriaUtil</code>: Accessibility utilities</li>
  <li><code class="language-plaintext highlighter-rouge">spDatePickerUtil</code>: Date picker utility functions</li>
  <li><code class="language-plaintext highlighter-rouge">select2EventBroker</code>: Handles select2 integration events</li>
</ul>]]></content><author><name>Israel Baral</name></author><category term="blog" /><category term="servicenow" /><category term="service portal" /><category term="angularJS" /><category term="directive" /><category term="datepicker" /><category term="ai-generated" /><summary type="html"><![CDATA[Attribution [DISCLAIMER] This is an AI-generated blog post based on importing scripts/app.$sp/directive.spDatePicker.js into claude and asking it to generate a document explaining how to use it.]]></summary></entry><entry><title type="html">ServiceNow Row Count Hacks: Tips from the Community</title><link href="http://baral.me/blog/post-snutil-rows-hack/" rel="alternate" type="text/html" title="ServiceNow Row Count Hacks: Tips from the Community" /><published>2025-03-20T17:10:04+00:00</published><updated>2025-03-20T17:10:04+00:00</updated><id>http://baral.me/blog/post-snutil-rows-hack</id><content type="html" xml:base="http://baral.me/blog/post-snutil-rows-hack/"><![CDATA[<blockquote>
  <h3 id="attribution">Attribution</h3>
  <p>[DISCLAIMER] This is an AI-generated blog post based on a Slack conversation. I’ve created this to preserve and share the valuable knowledge discussed, here are those who contributed:<br />
@nick.c-137<br />
@wiz0floyd<br />
@arnoudkooi<br />
Here’s a link to the original discussion (might be dead): <a href="https://sndevs.slack.com/archives/CKB5Q8DNK/p1742489712036399?thread_ts=1742405573.778519&amp;cid=CKB5Q8DNK">Slack Discussion</a></p>
</blockquote>

<h2 id="the-problem-displaying-more-records-on-a-single-page">The Problem: Displaying More Records on a Single Page</h2>

<p>If you’ve worked with ServiceNow, you’ve likely encountered the need to view more records on a single page than the default setting allows. While ServiceNow limits how many records display per page for performance reasons, sometimes you need to see more data at once for analysis or troubleshooting purposes.</p>

<p>The solution lies in a URL parameter called <code class="language-plaintext highlighter-rouge">sysparm_force_row_count</code>, but as one community member noted, “I always forget the name of that parameter for some reason.” This seemingly small challenge sparked a collaborative conversation that revealed several clever approaches to solve this common problem.</p>

<h2 id="solution-1-the-bookmarklet-approach">Solution 1: The Bookmarklet Approach</h2>

<p>One of the first solutions shared was a JavaScript bookmarklet that could be saved in your browser:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">javascript</span><span class="p">:</span> <span class="k">void</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> 
  <span class="kd">var</span> <span class="nx">loc</span> <span class="o">=</span> <span class="nb">String</span><span class="p">(</span><span class="nx">top</span><span class="p">.</span><span class="nx">location</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">loc</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">'</span><span class="s1">service-now.com</span><span class="dl">'</span><span class="p">)</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span> <span class="o">||</span> <span class="nx">loc</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">'</span><span class="s1">servicenowservices.com</span><span class="dl">'</span><span class="p">)</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> 
    <span class="nx">top</span><span class="p">.</span><span class="nx">location</span> <span class="o">+=</span> <span class="dl">'</span><span class="s1">&amp;sysparm_userpref_rowcount=</span><span class="dl">'</span> <span class="o">+</span> <span class="nb">Number</span><span class="p">(</span><span class="o">%</span><span class="nx">s</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">})()</span>
</code></pre></div></div>

<p>This bookmarklet checks if you’re on a ServiceNow instance and then appends the necessary parameter to display more rows. The contributor mentioned they had it set up as a custom search engine in their browser for quick access.</p>

<h2 id="solution-2-custom-query-part-switches">Solution 2: Custom Query Part Switches</h2>

<p>Another approach uses ServiceNow’s custom query part switches, a feature some users weren’t even aware existed:<br />
Open the extension from your toolbar, scroll down and click here on the settings tab
<img src="/assets/images/slashcommand_rows2.png" alt="screenshot of where to find the switch editor in sn-utils" /></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"r200"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"200 rows"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&amp;sysparm_userpref_rowcount=200"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"querypart"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This configuration allows you to apply the row count parameter to any list or slash command. The beauty of this approach is its simplicity and integration with ServiceNow’s native functionality.</p>

<h2 id="solution-3-the-custom-slash-command">Solution 3: The Custom Slash Command</h2>

<p>Building on these ideas, the original poster created a custom slash command called <code class="language-plaintext highlighter-rouge">/frc $0</code> (for “force row count”) that combines the best of both approaches:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">javascript</span><span class="p">:</span> <span class="k">void</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> 
  <span class="kd">var</span> <span class="nx">loc</span> <span class="o">=</span> <span class="nb">String</span><span class="p">(</span><span class="nx">top</span><span class="p">.</span><span class="nx">location</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">loc</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">'</span><span class="s1">service-now.com</span><span class="dl">'</span><span class="p">)</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span> <span class="o">||</span> <span class="nx">loc</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">'</span><span class="s1">servicenowservices.com</span><span class="dl">'</span><span class="p">)</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> 
    <span class="nx">top</span><span class="p">.</span><span class="nx">location</span> <span class="o">+=</span> <span class="dl">'</span><span class="s1">&amp;sysparm_force_row_count=$0</span><span class="dl">'</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">})()</span>
</code></pre></div></div>

<p>This command allows for the input of any custom number. For example, typing <code class="language-plaintext highlighter-rouge">/frc 317</code> would force the row count to display 317 records. For convenience, they also created switches for common values like <code class="language-plaintext highlighter-rouge">-r200</code> and <code class="language-plaintext highlighter-rouge">-r1000</code>.</p>

<p><img src="/assets/images/slashcommand_rows.png" alt="screenshot of the slashcommand in sn-utils" /></p>

<h2 id="refinements-and-best-practices">Refinements and Best Practices</h2>

<p>The community discussion didn’t stop at just sharing solutions—they also discussed potential improvements:</p>

<ol>
  <li>
    <p><strong>Removing Redundant Checks</strong>: For slash commands used only within ServiceNow, the domain checking code can be eliminated.</p>
  </li>
  <li>
    <p><strong>Parameter Duplication</strong>: A suggestion was made to check if the parameter already exists in the URL and replace it rather than appending a duplicate, which would prevent issues when using the command multiple times.</p>
  </li>
</ol>

<p>As one member reflected, “This ended up being way simpler than I thought,” highlighting how community collaboration often leads to elegant solutions for common challenges.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This conversation demonstrates the power of community knowledge sharing in solving everyday technical challenges. What began as a simple question about remembering a parameter name evolved into multiple practical solutions that can save ServiceNow administrators and developers valuable time.</p>

<p>Whether you prefer bookmarklets, custom query parts, or slash commands, these approaches provide convenient ways to view more records at once in ServiceNow—proving that sometimes the most helpful tools are the ones that solve small but persistent frustrations in our daily workflows.</p>

<!-- For various reasons I occasionally have the need to add sysparm_force_row_count=NUMBER to the URL to display like 2 or 300 records in a list all on one page. The problem is, I always forget the name of that parameter for some reason lol.Something like this should be doable in a slash command using JS yeah? Just parse the URL and find a spot to add the new parameter. It seems pretty simple but wanted to make sure I wasn’t underthinking it before I decided to throw something together. Thanks!
27 replies
:coffee_parrot:  Yesterday at 12:36 PM
I've got it in a bookmarklet
12:36
Slash command could work easily enough too
  Yesterday at 12:37 PM
Sheeiiittt
If you don’t mind sharing it, might get me started quickly? Haha
:coffee_parrot:  Yesterday at 12:37 PM
Yeah. Out of pocket, will try to remember when I'm at my desk
  Yesterday at 12:37 PM
All good man, no rush at all
12:37
Appreciate you
:coffee_parrot:  Yesterday at 12:38 PM
Actually might have it synced to my phone
12:38

javascript: void(function(){ var loc = String(top.location);%20 if (loc.indexOf('service-now.com') > -1 || loc.indexOf('servicenowservices.com') > -1) { top.location += '&sysparm_userpref_rowcount=' + Number(%s);}})()

  Yesterday at 12:38 PM
Hah, I’m stuck in a couple meetings anyway, don’t go to any extra trouble for me, won’t be able to do anything with it yet anyway
:coffee_parrot:  Yesterday at 12:38 PM
I have it saved as a custom search engine
  Yesterday at 12:39 PM
Ignore what I said you’re the best lol
@wiz0floyd
++
APP  Yesterday at 12:39 PM
Good going
@wiz0floyd
you now have 77 points (2618 total) :second_place_medal:
  Yesterday at 12:54 PM
A custom querypart switch should work
  Yesterday at 12:56 PM
I was unaware we could even make our own switches? Lol
  Yesterday at 1:00 PM

{
    "r200": {
        "description": "200 rows",
        "value": "&sysparm_userpref_rowcount=200",
        "type": "querypart"
    }
}

1:02
You can apply it to current list, or in any slash command. It doesnt support variables at this point
This file was deleted.
1:05
CleanShot 2025-03-19 at 19.04.53@2x.png 
CleanShot 2025-03-19 at 19.04.53@2x.png
  Yesterday at 1:05 PM
Well shit, this is super simple. Thanks guys!
!pp
APP  Yesterday at 1:05 PM
Way to help out
@wiz0floyd
you now have 78 points (2619 total) :second_place_medal:
Good going
@arnoudkooi
you now have 8 points (550 total) :sparkles:
  Yesterday at 1:13 PM
So I made /frc $0 to take custom input then made a switch for -r200 and -r1000That should pretty much cover any use case I’d ever have. This ended up being way simpler than I thought. You guys are the greatest
  Yesterday at 1:18 PM
whats the /frc $0 do?
  Yesterday at 1:32 PM
It’s mostly just wiz’s script to allow me to put in a custom number for the force_row_count param.Specifically this:

javascript: void(function(){ var loc = String(top.location);%20 if (loc.indexOf('service-now.com') > -1 || loc.indexOf('servicenowservices.com') > -1) { top.location += '&sysparm_force_row_count=$0';}})()

(edited)
1:32
So like /frc 317 will force the row count on the page to 317 (edited) 
:coffee_parrot:  Yesterday at 1:50 PM
You can probably drop all of the checking if it's a servicenow instance if you're using it in a slash command. I needed that when I was on a locked down computer and couldn't add plugins to my browser.
  Yesterday at 2:33 PM
Hah, I think I’m going to swap it out a bit and check if the parameter is already in the URL and replace it if so.
Because right now it just adds to the end and if I try to change it on a URL that already has the parameter it won’t work.Also that situation probably almost never happens so I’m not sure if I care haha
:coffee_parrot:  Yesterday at 2:42 PM
Could happen if you use it twice in a row
  Yesterday at 6:48 PM
This is excellent.   I would use this so much -->]]></content><author><name>Israel Baral</name></author><category term="blog" /><category term="servicenow" /><category term="sn-utils" /><category term="slashcommand" /><category term="ai-generated" /><summary type="html"><![CDATA[Attribution [DISCLAIMER] This is an AI-generated blog post based on a Slack conversation. I’ve created this to preserve and share the valuable knowledge discussed, here are those who contributed: @nick.c-137 @wiz0floyd @arnoudkooi Here’s a link to the original discussion (might be dead): Slack Discussion]]></summary></entry><entry><title type="html">How to expand a drive in ESXi with Linux</title><link href="http://baral.me/blog/post-esxi/" rel="alternate" type="text/html" title="How to expand a drive in ESXi with Linux" /><published>2023-10-06T19:50:04+00:00</published><updated>2023-10-06T19:50:04+00:00</updated><id>http://baral.me/blog/post-esxi</id><content type="html" xml:base="http://baral.me/blog/post-esxi/"><![CDATA[<p>To extend an eagerly zeroed thick drive in ESXi do the following:</p>

<ol>
  <li>Enable the shell.</li>
  <li>Confirm the vm is turned off or the disk is not active on any vm</li>
  <li>Login to the shell in your favorite terminal</li>
  <li>use the following command: <code class="language-plaintext highlighter-rouge">vmkfstools -X NumberG/T -d eagerzeroedthick /vmfs/volumes/Path/to/your/volume/volume_name.vmdk</code></li>
  <li>Once completed verify you’ve properly extended the drive with this command: <code class="language-plaintext highlighter-rouge">vmkfstools -D /vmfs/volumes/Path/to/your/volume/volume_name.vmdk</code></li>
  <li>Back in the web console, remove the drive from the vm, and then re-add it. It should now show the correct size.</li>
</ol>

<p><a href="https://tutoexpress.com/index.php/vmware-extend-an-eager-zeroed-thick-disk-keeping-its-format/">Source</a></p>

<p>To be able to use this extra space in linux you also need to resize the partition and file system in linux. To achieve that do the following:</p>

<ol>
  <li>Firstly, confirm the disk is not mounted, if you have the disk mounted automatically on boot you’ll need to either unmount it with the command: <code class="language-plaintext highlighter-rouge">sudo umount /dev/sdX</code> OR if that doesn’t work you should just disable automount entirely here: <code class="language-plaintext highlighter-rouge">/etc/fstab</code> and then restart the machine to unmount
    <ul>
      <li>Either <code class="language-plaintext highlighter-rouge">sudo umount /dev/sdX</code> or <code class="language-plaintext highlighter-rouge">sudo vi /etc/fstab</code></li>
    </ul>
  </li>
  <li>Enter command: <code class="language-plaintext highlighter-rouge">sudo parted /dev/sdX print</code>
    <ul>
      <li>Record the partition number of the partition you want to extend</li>
    </ul>
  </li>
  <li>To automatically extend the partition to take up all available space:
    <ul>
      <li>enter command: <code class="language-plaintext highlighter-rouge">sudo growpart /dev/sdX PartitionNumber</code></li>
    </ul>
  </li>
  <li>To begin extending the filesystem, first we need to do a quick diskcheck:
    <ul>
      <li>enter command: <code class="language-plaintext highlighter-rouge">sudo e2fsck -fy /dev/sdXPN</code></li>
    </ul>
  </li>
  <li>Once the disk check is completed and all optimizations have been completed, we can now extend the filesystem with the following command:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">sudo resize2fs /dev/sdXPN</code></li>
    </ul>
  </li>
</ol>

<p><a href="https://access.redhat.com/articles/1196353">Source</a></p>

<p>Congratulations, your disk should now be extended and usable by the OS.</p>]]></content><author><name>Israel Baral</name></author><category term="blog" /><category term="esxi" /><category term="ubuntu" /><category term="parted" /><summary type="html"><![CDATA[To extend an eagerly zeroed thick drive in ESXi do the following:]]></summary></entry></feed>