Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

Adding Custom Action Buttons to the HRM Todos Summary Widget

divyashah
Giga Guru

Hi Community,

We all know that the standard Employee Center / HRSD To-do widget architecture is powerful, but it can be overwhelming to customize. The hierarchy usually looks something like this:

hrm-todos-summary -> hrm-todos-line-item -> task-configuration (or specific approval widgets).

Recently, I had a requirement to add "Request Clarification" and "Reject with Comments" buttons. The standard approach often involves cloning multiple child widgets or creating complex UI macros for the task configuration.

However, I found a cleaner way to achieve this by modifying the parent Custom HRM Todos Summary widget directly. This keeps the logic centralized and avoids the "rabbit hole" of nested widget communication.

Here is how I achieved it.

 

The Concept

 

Instead of trying to inject buttons into the child widgets, we can inject them into the ng-repeat loop of the parent widget. We use CSS to visually align them with the existing footer of the child widget, and we handle the logic (Modals + Server Updates) entirely in the parent.

 

1. The HTML Template

 

In the hrm-todos-summary HTML, inside the tab-pane where the todos are rendered, I injected a custom footer div immediately after the child widget line.

Location: Inside the ng-repeat="todoLine in c.todoDisplayed[tab.name]"

HTML
 
<div ng-if="c.todoDisplayed[tab.name].length && todoLine.isOpen" class="task-content" ng-repeat="todoLine in c.todoDisplayed[tab.name]">
  
  <div ng-if="todoLine.todoLineWidget">
    <sp-widget widget="todoLine.todoLineWidget"></sp-widget> 
    
    <div class="panel-footer" ng-if="!todoLine.isCompleted">
      <button class="btn btn-default" ng-click="c.requestClarification(todoLine)">${Request Clarification}</button>
      <button class="btn btn-default" ng-click="c.rejectClarification(todoLine)">${Reject}</button>
    </div>
    </div>
  
  <div class="loading-indicator" ng-if="!todoLine.todoLineWidget">
    <span class="fa fa-spinner fa-spin" aria-hidden="true" name="spinner" spin="true"></span>
    ${Loading}...
  </div>
</div>

 

2. The Client Script (Handling Modals)

 

I added functions to handle the button clicks. These functions use spModal to capture mandatory comments before sending the data to the server.

JavaScript
 
c.requestClarification = function (todo) {
    if (!todo || !todo.sysId || !todo.tableName) return;

    spModal.prompt("Please enter clarification comments:", "").then(function (commentText) {
        if (!commentText) return; // Handle cancel

        if (!commentText.trim()) {
            spUtil.addErrorMessage("Clarification comments are mandatory.");
            return;
        }

        c.data.action = "requestClarification";
        c.data.todo_sys_id = todo.sysId;
        c.data.todo_table = todo.tableName;
        c.data.comment_text = commentText.trim();

        c.server.update().then(function (response) {
            spUtil.addInfoMessage("Clarification requested successfully.");
        });
    });
};

// Similar logic added for c.rejectClarification

 

3. The Server Script (Backend Logic)

 

Instead of passing events, we handle the database updates directly in the parent widget's server script. This example updates the Approval record state and pushes the comments to the related RITM.

JavaScript
 
if (input && input.action == 'requestClarification') {
    var grTodo = new GlideRecordSecure(input.todo_table);
    if (grTodo.get(input.todo_sys_id)) {
        // 1. Update Approval State
        grTodo.setValue('state', 'awaiting_clarification'); // Ensure this choice exists
        grTodo.update();

        // 2. Update Linked RITM with Comments
        var ritmSysId = grTodo.getValue('document_id');
        if (ritmSysId) {
            var ritmGR = new GlideRecord('sc_req_item');
            if (ritmGR.get(ritmSysId)) {
                ritmGR.comments = input.comment_text;
                ritmGR.update();
            }
        }
    }
}

 

4. The CSS (The "Magic" Alignment)

 

To make these buttons look like they belong to the child widget, I used negative margins to pull the footer up. This prevents double borders and wasted whitespace.

CSS
 
/* Target the custom footer */
.task-content > div > .panel-footer {
    text-align: right;
    border-top: none;
    padding-top: 0.15rem;
    /* Adjust these margins based on your theme */
    margin-right: 10.15rem; 
    margin-top: -5rem; 
    background-color: transparent;
}

/* Ensure buttons display inline */
.task-content > div > .panel-footer .btn {
  display: inline-block;
  float: none;
  margin-left: 5px;
}

 

Why this approach?

 

  1. Simplicity: You don't need to modify the task-configuration table or clone the standard approval widgets.

  2. Context: You have immediate access to the todoLine object in the parent loop.

  3. Performance: It reduces the number of event listeners required between parent and child widgets.

I hope this helps anyone struggling with the EC To-do widget hierarchy!

 

0 REPLIES 0