OneSignal (with Angular) — a notification service for websites

Anh-Thi Dinh
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!

Remarks

  • Ensure the integrated file OneSignalSDKWorker.js is accessible via http://localhost:<port>/<subfolder>/OneSignalSDKWorker.js
    • 1importScripts('https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.sw.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.

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
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.
      • 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}
    • Create a new tag to handle the consecutive messages.

Very simple sample

  • With just a HTML file.

With Angular (or Web SDK)

  • 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
    • 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
  • 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

1// No return
2await this.oneSignal.Notifications.requestPermission();
3
4// To know user's action
5const isPermissionGranted = this.oneSignal.Notifications.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!
    • 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);
      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)

  • 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

1this.oneSignal.Debug.setLogLevel('trace');

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

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});
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. 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});
      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