sarah_bioni
ServiceNow Employee
ServiceNow Employee

Overview

This article demonstrates how to implement a custom pop-up modal inside a web view (iframe) in Now Mobile or Agent Mobile to prompt users for an in-app review on the Apple App Store or Google Play Store.

This approach is helpful for gathering feedback and improving app visibility directly from within the mobile experience. 

Of course, the pop-up can also be done with a mobile event OOTB, but for those pages that use a web view, a widget implementation is required.

 

Prerequisites

To embed a portal page with widgets inside Now Mobile, follow this guide:
🔗 https://www.servicenow.com/community/mobile-apps-platform-articles/how-to-display-a-portal-page-with...


Step-by-Step Implementation

1. Create a Widget with a Modal Popup

Use the following HTML and AngularJS snippet in your widget:

<div class="feedback-modal" ng-show="c.showFeedbackModal">
  <div class="feedback-modal-content">
    <p>Are you enjoying our app?</p>
    <div class="feedback-buttons">
      <button class="btn btn-default" ng-click="c.closeModal()">No</button>
      <button class="btn btn-primary" ng-click="c.redirectToStore()">Yes</button>
    </div>
  </div>
</div>

 

2. Client Script Logic

c.showFeedbackModal = false;

setTimeout(function() {
$scope.$apply(function() {
c.data.action = "getPreference";
c.server.update().then(function() {
if (c.data.showPopup) {
c.showFeedbackModal = true;
}
});
});
}, 2000); // Show after 2 seconds

c.redirectToStore = function() {
c.data.navUser = navigator.userAgent;

if (/iPhone|iPad|iPod/i.test(navigator.userAgent) && /snMobile/i.test(navigator.userAgent)) {
c.data.action = "redirectiOS";
} else if (/Android/i.test(navigator.userAgent) && /snMobile/i.test(navigator.userAgent)) {
c.data.action = "redirectAndroid";
}

c.server.update().then(function() {
window.open(c.data.redirectStore, "_blank");
c.showFeedbackModal = false;
});
};

c.closeModal = function() {
c.data.action = "getSNFeedback";
c.server.update().then(function() {
$location.url(c.data.dynamicPath);
c.showFeedbackModal = false;
});
};

 

3. Server Script Logic

Handle user preferences and redirection logic:

var futureDate = new GlideDate();
futureDate.addDaysUTC(365);
var actualDate = new GlideDate();

data.redirectStore = "";

 

if (input && input.action === 'getPreference') {
// Check if user has already responded
var grPreference = new GlideRecord('sys_user_preference');
grPreference.addEncodedQuery('user=' + gs.getUserID() + '^name=inreview.accepted');
grPreference.query();
if (grPreference.next()) {
var grPreferenceFB = new GlideRecord('sys_user_preference');
grPreferenceFB.addEncodedQuery('user=' + gs.getUserID() + '^name=inreview.rejected');
grPreferenceFB.query();
if (grPreferenceFB.next()) {
var upDate = grPreferenceFB.getValue('value');
data.showPopup = (upDate == actualDate);
}
} else {
data.showPopup = true;
grPreference.initialize();
grPreference.setValue('user', gs.getUserID());
grPreference.setValue('name', 'inreview.accepted');
grPreference.setValue('value', true);
grPreference.insert();
}
} else if (input && input.action === 'getSNFeedback' && gs.isMobile()) {
// Save rejection preference
var grPreferenceInsert = new GlideRecord('sys_user_preference');
grPreferenceInsert.addEncodedQuery('user=' + gs.getUserID() + '^name=inreview.rejected');
grPreferenceInsert.query();
if (!grPreferenceInsert.next()) {
grPreferenceInsert.initialize();
grPreferenceInsert.setValue('user', gs.getUserID());
grPreferenceInsert.setValue('name', 'inreview.rejected');
grPreferenceInsert.setValue('value', futureDate);
grPreferenceInsert.insert();
} else {
grPreferenceInsert.setValue('value', futureDate);
grPreferenceInsert.update();
}

//This step occurs if the user clicks "No"; they are then sent to the internal assessment within the app.

var deepLinkGen2 = new global.MobileDeepLinkGenerator('Request');
data.dynamicPath = deepLinkGen2.getUniversalLink('/mesp?id=me_take_assessment&type_id=');
} else if (input && input.action === 'redirectiOS') {
cancelReview();
data.redirectStore = "https://apps.apple.com/app/{APP ID}?action=write-review"; //replace {APP ID} with the specific app id into the Apple Store.
} else if (input && input.action === 'redirectAndroid') {
cancelReview();
data.redirectStore = "https://play.google.com/store/apps/details?id={APP ID}"; //replace {APP ID} with the specific app id into the Apple Store.
}

function cancelReview() {
var grPreferenceCancel = new GlideRecord('sys_user_preference');
grPreferenceCancel.addEncodedQuery('user=' + gs.getUserID() + '^name=inreview.rejected');
grPreferenceCancel.query();
if (grPreferenceCancel.next()) {
grPreferenceCancel.setValue('value', "");
grPreferenceCancel.update();
}
}

 

Final Notes

  • This solution ensures the pop-up only appears once per user unless reset.
  • It uses sys_user_preference to track feedback status.
  • The redirection is handled securely and contextually based on the device type.

 

Version history
Last update:
4 hours ago
Updated by:
Contributors