- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
8 hours ago
How to Programmatically Toggle the Now Assist Chatbot in Employee Center
Add an open/close toggle directly in the EC header — using the platform's own internal event lifecycle. No OOB code modified.
sp_header_footer).How It Works
The toggle reuses the same internal mechanisms as the native FAB sparkle button (open) and chevron-down close button (close). No OOB code is modified.
Open Sequence
c.hideChat = false on the Dialog Widget's scopeCREATE_WINDOW + OPEN_WINDOWClose Sequence
#close-windowCLOSE_WINDOW lifecycleNOW_ASSIST_DIALOG#CLOSED syncs stateEvents & DOM Structure
Events Reference
| Event | Direction | Description |
|---|---|---|
NOW_ASSIST_DIALOG#OPENED |
Output | Fired when chat opens |
NOW_ASSIST_DIALOG#CLOSED |
Output | Fired when chat closes |
NOW_ASSIST_DIALOG#PINNED |
Output | Fired when chat is pinned/unpinned |
SN_WINDOW#WINDOW_OPENED |
Internal | Dispatched to content element on open |
SN_WINDOW#WINDOW_CLOSED |
Internal | Dispatched to content element on close |
SN_WINDOW_MANAGER#OPEN_WINDOW |
Action | Internal open action |
SN_WINDOW_MANAGER#CLOSE_WINDOW |
Action | Internal close action |
Shadow DOM Hierarchy
Angular Wrapper
#sn-na-dw-wrapper is controlled by the Dialog Widget's controller. c.hideChat must be false before the FAB is clickable.
<div class="chat-wrapper" id="sn-na-dw-wrapper"
ng-class="{'hidden': c.hideChat, 'chat-pinned': !c.hideResizer}">
Console Testing Scripts
Paste into Chrome DevTools to validate before applying widget changes.
3.1 — Open
function openNowAssist() {
// Unhide wrapper via Angular scope
var wrapper = document.getElementById('sn-na-dw-wrapper');
var s = angular.element(wrapper).scope();
while (s && !(s.c && s.c.hasOwnProperty('hideChat'))) {
s = s.$parent;
}
s.c.hideChat = false;
if (!s.$$phase && !s.$root.$$phase) s.$apply();
// Wait for FAB, then click
setTimeout(function() {
var app = document.querySelector('now-assist-full-page-wrapper-app');
var dialog = app.shadowRoot.querySelector('now-assist-dialog');
var sparkle = dialog.shadowRoot.querySelector('sn-nass-sp-sparkle-icon');
var circular = sparkle.shadowRoot.querySelector('now-button-circular');
circular.shadowRoot.querySelector('button').click();
console.log('✓ Now Assist OPENED');
}, 300);
}
openNowAssist();
3.2 — Close
function closeNowAssist() {
var app = document.querySelector('now-assist-full-page-wrapper-app');
var dialog = app.shadowRoot.querySelector('now-assist-dialog');
var winMgr = dialog.shadowRoot.querySelector('sn-window-manager');
var snWin = winMgr.shadowRoot.querySelector('sn-window');
var closeBtn = snWin.shadowRoot.querySelector('now-button#close-window');
closeBtn.shadowRoot.querySelector('button').click();
console.log('✓ Now Assist CLOSED');
}
closeNowAssist();
3.3 — Diagnostic
var app = document.querySelector('now-assist-full-page-wrapper-app');
console.log('1. app:', !!app);
var dialog = app?.shadowRoot?.querySelector('now-assist-dialog');
console.log('2. dialog:', !!dialog, 'opened:', dialog?.hasAttribute('opened'));
var sparkle = dialog?.shadowRoot?.querySelector('sn-nass-sp-sparkle-icon');
console.log('3. sparkle:', !!sparkle);
var circular = sparkle?.shadowRoot?.querySelector('now-button-circular');
console.log('4. FAB btn:', !!circular?.shadowRoot?.querySelector('button'));
var wrapper = document.getElementById('sn-na-dw-wrapper');
var s = angular.element(wrapper).scope();
while (s && !(s.c?.hasOwnProperty('hideChat'))) { s = s.$parent; }
console.log('5. scope:', !!s, 'hideChat:', s?.c?.hideChat);
Widget Implementation
Three changes to the Employee Center Header (sp_header_footer).
ng-if="data.showSparkle" — only renders when Now Assist is enabled on the portal.4.1 — Client Script
Add after hideSearchWidgetForNASS, before c.evaluateExperienceFeedbackDrawerVisibility:
/* ── Now Assist Header Toggle ── */
c.nowAssistOpen = false;
function getDialogScope() {
var wrapper = document.getElementById('sn-na-dw-wrapper');
if (!wrapper) return null;
var s = angular.element(wrapper).scope();
while (s && !(s.c && s.c.hasOwnProperty('hideChat'))) {
s = s.$parent;
}
return s;
}
function safeApply(scope) {
if (!scope.$$phase && !scope.$root.$$phase) scope.$apply();
}
function clickFab() {
var app = document.querySelector('now-assist-full-page-wrapper-app');
if (!app || !app.shadowRoot) return false;
var dialog = app.shadowRoot.querySelector('now-assist-dialog');
if (!dialog || !dialog.shadowRoot) return false;
var sparkle = dialog.shadowRoot.querySelector('sn-nass-sp-sparkle-icon');
if (!sparkle || !sparkle.shadowRoot) return false;
var circular = sparkle.shadowRoot.querySelector('now-button-circular');
if (!circular || !circular.shadowRoot) return false;
var btn = circular.shadowRoot.querySelector('button');
if (btn) { btn.click(); return true; }
return false;
}
function clickCloseButton() {
var app = document.querySelector('now-assist-full-page-wrapper-app');
if (!app || !app.shadowRoot) return false;
var dialog = app.shadowRoot.querySelector('now-assist-dialog');
if (!dialog || !dialog.shadowRoot) return false;
var winMgr = dialog.shadowRoot.querySelector('sn-window-manager');
if (!winMgr || !winMgr.shadowRoot) return false;
var snWin = winMgr.shadowRoot.querySelector('sn-window');
if (!snWin || !snWin.shadowRoot) return false;
var closeBtn = snWin.shadowRoot.querySelector('now-button#close-window');
if (!closeBtn || !closeBtn.shadowRoot) return false;
var btn = closeBtn.shadowRoot.querySelector('button');
if (btn) { btn.click(); return true; }
return false;
}
c.toggleNowAssist = function() {
var dlgScope = getDialogScope();
if (!dlgScope) return;
if (c.nowAssistOpen) {
clickCloseButton();
c.nowAssistOpen = false;
} else {
dlgScope.c.hideChat = false;
safeApply(dlgScope);
$timeout(function() {
clickFab();
c.nowAssistOpen = true;
}, 300);
}
};
c._syncNowAssistState = function() {
var app = document.querySelector('now-assist-full-page-wrapper-app');
if (!app) return;
app.addEventListener('NOW_ASSIST_DIALOG#OPENED', function() {
c.nowAssistOpen = true;
try { safeApply($scope); } catch (e) {}
});
app.addEventListener('NOW_ASSIST_DIALOG#CLOSED', function() {
c.nowAssistOpen = false;
try { safeApply($scope); } catch (e) {}
});
};
$timeout(c._syncNowAssistState, 2000);
/* ── End Now Assist Header Toggle ── */
4.2 — Template (HTML)
Add before <!-- Esc Notifications Bell --> inside the desktop header-menu div:
<!-- Now Assist Toggle -->
<div class="gt-menu-item na-header-toggle"
ng-if="data.showSparkle">
<a href="javascript:void(0)"
id="na-header-toggle-btn"
ng-click="c.toggleNowAssist()"
aria-label="{{c.nowAssistOpen
? 'Close Now Assist' : 'Open Now Assist'}}"
data-toggle="tooltip"
data-placement="bottom"
data-original-title="{{c.nowAssistOpen
? 'Close Now Assist' : 'Open Now Assist'}}">
<span class="na-toggle-icon" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="16"
height="16" fill="currentColor">
<path d="M12.87 3.52c.2-.77 1.28-.77
1.48 0l.5 1.9a3.3 3.3 0 0 0 2.33
2.33l1.9.5c.77.2.77 1.28 0 1.48l-1.9
.5a3.3 3.3 0 0 0-2.33 2.34l-.5
1.89c-.2.77-1.28.77-1.48 0l-.5-1.9a3.3
3.3 0 0 0-2.33-2.33l-1.9-.5c-.77-.2
-.77-1.28 0-1.48l1.9-.5a3.3 3.3 0
0 0 2.33-2.33l.5-1.9z"/>
</svg>
</span>
<span ng-bind-html="'${Now Assist}'"
aria-hidden="true"></span>
</a>
</div>
4.3 — CSS (SCSS)
/* ── Now Assist Header Toggle ── */
.na-header-toggle {
a {
display: flex;
align-items: center;
gap: 6px;
}
}
.na-toggle-icon {
display: inline-flex;
align-items: center;
svg { fill: $navbar-inverse-link-color; }
}
.na-header-toggle a:hover .na-toggle-icon svg {
fill: $navbar-inverse-link-hover-color;
}
/* ── End Now Assist Header Toggle ── */
Why This Is Safe
- No OOB code modified — Dialog Widget, sn-window-manager, and all Now Experience components untouched.
- Same code paths — Open uses FAB's native click. Close uses
#close-window's native click. - Gated visibility — Only renders when Now Assist is enabled (
ng-if="data.showSparkle"). - Safe Angular —
safeApplychecks$$phasebefore$apply. - Upgrade resilient — If shadow DOM changes, toggle returns
falsewithout errors.
Troubleshooting
| Symptom | Resolution |
|---|---|
| Button not visible | Verify data.showSparkle is true. |
| First click unresponsive | Window not yet created. FAB click handles it. Increase $timeout to 500 if needed. |
| Open does nothing | Run diagnostic (3.3). Verify each shadow DOM node is non-null. |
| Close does nothing | #close-window only exists when chat is open. |
| $rootScope:inprog | safeApply prevents this. Use exact code from 4.1. |
| nowWindowManager undefined | Expected in Service Portal. Code doesn't depend on it. |
sn-window-manager source review. All paths validated on a live instance.Now Assist Self-Service · Employee Center · Service Portal
