Thi Notes
AboutNotesBlogTopicsToolsReading
About|Sketches |Cooking |Cafe icon Support Thi

OneSignal (with Angular) — a notification service for websites

OneSignal (with Angular) — a notification service for websites

Anh-Thi Dinh
API & Services
Angular
☝
OneSignal's AI Chatbot is extremely helpful. While the official documentation often contains outdated or incorrect information, the chatbot consistently provides accurate and reliable solutions. I recommend consulting it rather than relying solely on the official documentation.

References

  • The official documentation is inadequate. Following their guides didn't work for me on the first attempt!
  • Dashboard
  • Developer Guide
  • Server SDK Reference
  • Package @onesignal/node-onesignal
  • Local Testing
  • Users, OneSignal ID, External ID
  • Aliases
  • Pricing
  • Competitors (not yet evaluated): CleverTap.

Remarks

  • Ensure the integrated file OneSignalSDKWorker.js is accessible via http://localhost:<port>/<subfolder>/OneSignalSDKWorker.js
    • Note that you will only see the above file content (importScripts….). Only the service worker can fetch and load the content of OneSignalSDK.sw.js.
  • After modifying anything on the OneSignal Dashboard or in your code, clear your browser's cache, reset notification permissions, and reload the page.
  • Be aware of caching issues when changing the location of OneSignalSDKWorker.js. The service may continue working due to browser caching even after relocating the file. To verify it's truly working, perform a hard refresh, wait a bit, or test with a fresh browser/profile.
  • Should use Soft Prompt (display an explanation before asking for the notification permission from the browser) ← Soft prompts are recommended by browsers and help maximize opt-in rates while protecting your domain reputation.
  • Receiving Notifications When the Browser is Closed ← based on which kind of browsers on which kind of OS, it may be different.
  • Web push: Notifications not shown - OneSignal ← troubleshooting

TTL (Time To Live)

TTL determines how long push services retain messages when devices are offline - it works universally with a default of 3 days for all platforms. However, the ability to actually receive those stored notifications when reopening the browser depends on the specific browser and OS combination
Source.
For example:
  • On macOS
    • Use Safari
      • Close browser → new messages come → still receive and show.
      • Close browser and turn off computer → new messages come → …. → re-open computer → receive notif and show.
    • Use other browsers:
      • Close browser → new messages come → not receive any thing.
      • Re-open browser → not receive anything.
  • On Windows: inverse of macOS.

Handle spaming problem

  • Problem: OneSignal has a TTL (time to live) parameter which means when users are offline and return within the TTL period (default is 3 days), they receive all accumulated messages at once—creating a spam-like experience. How can we configure it to send only the most recent messages?
  • Ideas:
    • Using web_push_topic ← This parameter allows you to group notifications by topic—such as a chat room or conversation—so that new notifications with the same web_push_topic value will replace previous ones for that topic, preventing users from receiving multiple notifications for the same chat.
    • Create a new tag to handle the consecutive messages.

Auto disappear notification popup on Windows after 5s

By default, most browsers automatically dismiss notification popups (bottom right) on Windows after 5 seconds. However, when using the default parameters of OneSignal.init(), the notification persists until the user clicks "Close." To restore the default 5-second auto-dismiss behavior, use this setting:

Very simple sample

  • With just a HTML file.
  • Sample Github repository

With Angular (or Web SDK)

  • Angular Web SDK setup (Though incomplete, it provides useful information)
  • Sample Github repository
  • Implementation notes:
    • Use http protocol with ng serve
    • Enable "Local Testing" in the OneSignal settings page
  • Firefox: it works like a charm.
  • Brave: Not working because Brave blocks the service worker file from functioning. You'll see errors like
  • Chrome: Works well. On MacOS, enable notifications for Chrome in the system settings. Allow both "Google Chrome" and "Google Chrome Helper (Alerts)" permissions. Without these settings, Chrome notifications won't appear (except for the initial Hello notification).
  • Safari: should clear the cache and:
    • Safari → Settings → Websites → Notifications → allow all
    • Safari → Settings for localhost… → make sure
      • We can click the small icon on the left of the address bar to show these options.
      If it's still not working, you can create a new profile (Safari → Manage Profiles…) and test again.

Check the permission

Stop receiving notification

  • Note that logout() doesn't prevent users from receiving notifications—it only makes them anonymous (resets the OneSignal ID to a new anonymous user). If you want to stop notifications completely, use optOut() and when needed again, use optIn().
    • Note that, we cannot use login() after using outOut()
  • If you want to stop receiving notification when you are already in the page, use foregroundWillDisplay ← ⚠️ Update: this idea (event.preventDefault() is only working on mobile, not web). The event is well usable but the preventing feature isn’t working!
    • Read more: Developers: Code blocking or removing notifications
      For browsers that don't support event.preventDefault(), consider using tags: set an "is_on_page" tag when the user is actively viewing the page, and remove it when they navigate away. On the backend, configure your notification filters to only send messages to users who don't have the "is_on_page" tag.

Clear browser data

⚠️
Make sure to clear and reset all your browser data each time you change something in the settings (on the Dashboard or in your code).
  • Safari: Safari menu → Preferences (or Settings) → Privacy tab → "Manage Website Data" → "Remove All”
    • Or: Safari menu → "Clear History..." → choose time range → "Clear History”
    • Enable developer setting: Safari → Preferences (or Settings) → Advanced → tick “Show features for web developers”
    • For developer cache: Develop menu → "Empty Caches" (enable Develop menu in Preferences → Advanced first)
    • Unregister service worker: Safari → Develop menu → Web Inspector → Go to "Storage" tab → Expand "Service Workers" in left sidebar → Select your service worker → Click "Unregister" to remove it completely
  • Chrome:
    • Chrome menu (⋮) → Settings → Privacy and security → "Clear browsing data" / “Delete browsing data” → Advanced → select all → Delete data.
    • Storage section: F12 → Application tab → local storage, session storage, indexedDB, cookies, cache storage → right click → clear / delete.
    • Service workers section: F12 → Application tab → Service workers → See all registrations → Find “OneSignal” and then unregister / stop.
    • Disable caching: F12 → Network → check “Disable cache” to prevent caching during development sessions.
  • Firefox:
    • Firefox menu (☰) → Settings → Privacy & Security → Scroll to "Cookies and Site Data" → "Clear Data..."
    • F12 → Storage → … → right click and Delete All
    • Service worker: F12 → Application → Service Workers → … → Unregister
      • Or type in address bar: about:serviceworkers
 

Users (Subscription ID, External ID, OneSignal ID)

  • Official doc.
  • Subscription ID vs OneSignal ID? When a new device connects, both a subscription ID and OneSignal ID are automatically created.
    • If this device includes an External ID, the OneSignal ID will be changed to match the one associated with that External ID.
  • OneSignal ID vs External ID?
    • OneSignal ID: Automatically generated, read-only, and can change based on existing External ID.
    • External ID: Set by developers.
    • When using login(external_id), if the external_id already exists, the SDK switches to that user. Any anonymous data collected before login is discarded, not merged. Ref.
  • Anonymous vs Identified users:
    • Anonymous users (no External ID): Each subscription receives its own OneSignal ID and is treated as a separate user.
    • Identified users (with External ID): All subscriptions merge under a single OneSignal ID.
  • Best practice: Always call OneSignal.login(external_id) to set the user context to the provided external_id. You can call this method anytime to link the user, as the SDK handles this flexibly.
  • Different browsers calling OneSignal.login("user_id_1") will create different subscriptions but use the same external_id "user_id_1".
  • To check the correspondence between External ID and OneSignal ID, go to Dashboard → Audience → Users.
  • Note that logout() doesn't prevent users from receiving notifications—it only makes them anonymous (resets the OneSignal ID to a new anonymous user). If you want to stop notifications completely, use optOut() and when needed again, use optIn().
    • Note that, we cannot use login() after using outOut()

Debugging

Set log level

Test focus() notification

To test notification behavior when focusing on a page, use the "Specific Time" option in "Delivery Schedule" (Messages → Push). Without this setting, notifications trigger immediately, making it difficult to switch browser tabs quickly enough to test different focus states.

Debug the service worker

  1. Open Chrome Developer Tools (F12) and go to the Console tab
  1. Run: OneSignal.User.PushSubscription.id to get your subscription ID
  1. Visit chrome://serviceworker-internals and search for your site's scope
  1. Click "Inspect" on your service worker and run OneSignalWorker.log.trace(); in the console

Check message delivery

  1. Open chrome://gcm-internals and click "Start Recording"
  1. Send a test message and check if you see "Data msg received" in the logs

Check the subscriber ID

Then search and use “Add as Test Subscription” in Dashboard.

Send test push via API

In the official doc, in the body of the request, there is "included_segments": [ "Test Users" ], you have to create a segment name “Test Users” (exact name like in the body)
  1. In your dashboard, go to Audience > Subscriptions, find your device, click the Options (three dots) button and select Add to Test Subscriptions.
  1. Create a Test Users segment: Go to Audience > Segments > New Segment, name it Test Users (exact name is important), and add the “Test Users” filter.
  1. Update your API call: Use "included_segments": ["Test Users"] in your API request instead of targeting individual player IDs

Troubleshooting

  1. OneSignal doesn't work with Brave browser (tested on Mac), even when all Brave shields or ad trackers are disabled.
  1. There is an unusual behavior with Opera browser (tested on Mac): users can connect and subscribe normally until they receive notifications. If OneSignal server fails to deliver a notification twice, it automatically changes the subscriber's status (Opera users in this case) to "Unsubscribed." There's no reliable way to detect this status from the front-end. For example, when using the following code to check:
    1. The information displayed indicates the client is still subscribed, despite the server-side status change.
      This is a server-side issue, not a problem with your client setup or application configuration.
  1. On Edge Windows → GET https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js net::ERR_BLOCKED_BY_CLIENT
    1. Go to Edge settings at edge://settings/privacy/trackingPrevention/blockedTrackers
 
In this post
◆References◆Remarks○TTL (Time To Live)○Handle spaming problem○Auto disappear notification popup on Windows after 5s◆Very simple sample◆With Angular (or Web SDK)◆Check the permission◆Stop receiving notification◆Clear browser data◆Users (Subscription ID, External ID, OneSignal ID)◆Debugging○Set log level○Test focus() notification○Debug the service worker○Check message delivery○Check the subscriber ID◆Send test push via API◆Troubleshooting
1importScripts('https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.sw.js');
1{
2  "app_id": "YOUR_APP_ID",
3  "included_segments": ["Active Users"],
4  "contents": { "en": "New message in your chat!" },
5  "web_push_topic": "chat-12345"
6}
1this.oneSignal.init({
2  appId: environment.oneSignal.appId,
3  // Make the notification disappear after 5s (default behavior of most browsers)
4  // It seems that OneSignal make this option "true" by default!
5  persistNotification: false,
6	// other options
7});
1Uncaught NetworkError: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at 'https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.sw.js' failed to load.
1[Service Worker Installation] Installing service worker failed TypeError: Failed to register a ServiceWorker for scope ('http://localhost:4200/') with script ('http://localhost:4200/OneSignalSDKWorker.js?appId=c9112e5f-f8e4-4831-b803-cd37207e3198&sdkVersion=160508'): ServiceWorker script evaluation failed
1// No return
2await this.oneSignal.Notifications.requestPermission();
3
4// To know user's action
5const isPermissionGranted = this.oneSignal.Notifications.permission;
1function foregroundWillDisplayListener(event) {
2    // Check if user is on the chat page and tab is focused
3    if (document.hasFocus() && window.location.pathname.includes('/chat')) {
4        // Prevent the notification from showing
5        event.preventDefault();
6    }
7    // If tab is not focused or user is elsewhere, notification will display normally
8}
9
10OneSignal.Notifications.addEventListener("foregroundWillDisplay", foregroundWillDisplayListener);
1this.oneSignal.Debug.setLogLevel('trace');
1this.oneSignal.User.PushSubscription.addEventListener('change', (event) => {
2  console.log('Push subscription changed:', event);
3  console.log('New subscription ID:', this.oneSignal.User.PushSubscription.id);
4  console.log('Opted in:', this.oneSignal.User.PushSubscription.optedIn);
5});
1// Returns true if subscribed, false if unsubscribed
2var isOptedIn = OneSignal.User.PushSubscription.optedIn;
3console.log("Current subscription status:", isOptedIn);
4
5// Get all subscription information
6Promise.all([
7  OneSignal.User.PushSubscription.id,
8  OneSignal.User.PushSubscription.token,
9  OneSignal.User.PushSubscription.optedIn
10]).then(([subscriptionId, pushToken, optedIn]) => {
11  console.log("Subscription ID:", subscriptionId);
12  console.log("Push Token:", pushToken);
13  console.log("Opted In Status:", optedIn);
14});