Control the context within the app with App Lifecycle Methods

The Freshworks apps render in placeholders within the host Freshworks products. Placeholders are available on different pages of its host. For example, the Tickets page in Freshdesk has Ticket Sidebar placeholder and Full-Page app placeholder in the left side navigation bar that is available on all the pages. So, the placeholders render the apps depending on when the page opens up and relevant placeholders come into the scope.

When app loads for the first time

The Javascript Engine on the browsers cascades a series of Web API Event invocations until the thread of execution starts executing your app.

A typical scenario looks as follows:

  • User hits the URL of the host product in the browser. For example, https://domain.freshdesk.com/a/tickets/12345. The web pages of the host product load alongside the placeholders.
  • Once the browser establishes the window web interface, the document renders and emits the “DOMContentLoaded” event.
  • The Javascript of our app can use web APIs to listen to this event (or any equivalent ones) to ensure the DOM tree is loaded.
  • Finally, invoke our APIs exposed by our platform app.initialized() to use it to hear app activated and deactivate events.

In Single Page Applications (SPAs), the complete page is not reloaded when the user navigates to another page. To notify the app about the page change of the parent window, the app.activate event triggered. On the Full-Page app placeholder, the page is never changed as the app is visible on all the pages of the product, the app.activated event is also triggered when the app is loaded for the first time and never triggers if the app placeholder is not affected by page changes.

Understanding app life-cycle events

Freshworks products are Single page applications (SPAs). SPAs do not reload the complete website when the user moves to the next page. Only specific content refreshes when the user moves to another page. For example, ticket conversations change when the page is switched from one ticket to another, but the sidebar and header stay the same.

Life cycle events in the app are introduced to know when the app has to refresh the content when the app is first time initialized and the page changed. It depends on the app placeholder in the product since the placeholder in the sidebar does not need to reload the app when the user switches the page between one ticket to another. It introduces the uncertainty on how to get the updated context for the app when the page is switched from one page to another. For example, if the user changes the page from ticket number #1000 to ticket number #1005, the app should get the updated data about the new ticket number #1005.

App Lifecycle Methods with Data Methods

When the Data method is used to get the data from the current page’s context, the data that are Ïpresent on the current page is passed in the app. For the page change action, when is this Data method consumed is important to get the new page’s data.

Let’s look at an example of app lifecycle methods in the app.

document.addEventListener('DOMContentLoaded', () => {
  // The document is completely loaded
  app.initialized().then(function (_client) {
    // The app has been loaded for the first time now and the client is available
    window.client = _client;
    client.events.on('app.activated', function(){
      // The app is now ready after first time load and page change
    });
  });
});

In the app lifecycle methods, app.initialized() is triggered when the app first loads, and app.activated event is triggered whenever the page reloads. The app.deactivated event is triggered when the page is moved to another and the app is removed from the scope. Depending on some app placeholders, both app.initialized() and app.activated events could be triggered at the same time if the app is created whenever the page is reloaded. Check https://developer.freshdesk.com/v2/docs/app-lifecycle-methods for more details on which app placeholder gets the app.activated event.

The app.activated event gets triggered whenever the page reloads and it’s the right event to get the updated page’s context and data.

So, place all the app logic under app.activated event handler function to get the updated data.

Problem

Let’s say, I’m in the Freshdesk Support Desk product and I want to get the ticket details from a ticket’s page. Based on the ticket’s status, I will update my shipping status in an external eCommerce system. Let’s write a code for this.

document.onreadystatechange = function () {
  if (document.readyState === 'interactive'){
    app.initialized().then(function (_client) {
      window.client = _client;

      // The Data Method is used regardless of the app.activated event
      client.data.get('ticket').then(function (data) {
        // Check if the ticket status is resolved (4) or closed (5)
        if(data.ticket.status === 4 || data.ticket.status === 5){
          // If the ticket is resolved or closed, update the shipping status
          updateShippingStatus(data.ticket.id);
        }
      });
  
    });
  }
};

The problem here is, the data could be stale data from the app’s first load and the app could fetch and show the previous page’s data. For the users, the app using the previous page’s data could cause damage with a huge impact. This could update the shipping status for an order corresponding to the current ticket with the previously looked at ticket’s status. Preventing it with other validations is possible but unnecessary and mostly missed since this problem often goes unnoticed.

Solution

Let’s look at the code for proper usage to get data from the page context for this case.

document.onreadystatechange = function () {
  if (document.readyState === 'interactive'){
    app.initialized().then(function (_client) {
      window.client = _client;
      client.events.on('app.activated', function(){

        // The Data Method is used after the app is activated
        client.data.get('ticket').then(function (data) {
          // Check if the ticket status is resolved (4) or closed (5)
          if(data.ticket.status === 4 || data.ticket.status === 5){
            // If the ticket is resolved or closed, update the shipping status
            updateShippingStatus(data.ticket.id);
          }
        });

      });
    });
  }
};

In this updated example,

  • The additional code client.events.on(‘app.activated’, function(){…}); is added. It is an important event to add to listen to the page change and make relevant actions in the app.

  • After the app.activated event is triggered, the Data Method fetches the ticket data. Thus, it is now ensured that the ticket data comes from the current page’s context.

  • When the page changes, depending on the app placeholder, the app.initialized() event may or may not get triggered and the app might retain the previous page’s context.

  • Unless required, all the app functionality should go under app.activated event handler function.