Internationalization Options in AngularJS and Salesforce: Part 2 – Time Zone Conversions

In part one of this series, we covered how to leverage some of the settings from the user’s profile in Salesforce to control the locale settings in angularJS. In this post, we will build off that initial example and extend it to cover time zone conversions. This will enable us to see all the data presented to the logged in user in that user’s time zone. This example could be further extended to allow the logged in user to see the data presented in another user’s time zone. An example of this is if an activity, task, or event needs to be assigned to a user in a different time zone, it is created in their time zone, not the time zone of who has created the activity. As long as the correct time zone is pulled from their profile, the new task is created in their time zone using the techniques outlined below.

The author of this article is Courtney Dascenzo, Principal Technical Evangelist with ISV Partner Enablement Team

In part one of this series, we covered how to leverage some of the settings from the user’s profile in Salesforce to control the locale settings in angularJS. In this post, we will build off that initial example and extend it to cover time zone conversions. This will enable us to see all the data presented to the logged in user in that user’s time zone. This example could be further extended to allow the logged in user to see the data presented in another user’s time zone. An example of this is if an activity, task, or event needs to be assigned to a user in a different time zone, it is created in their time zone, not the time zone of who has created the activity. As long as the correct time zone is pulled from their profile, the new task is created in their time zone using the techniques outlined below.

Note: Be sure to reference the assumptions from part one of this series before continuing.

Time Zones

The default behavior in angularJS is to leverage the time zone setting in the browser. We do not want to depend on this to be correct so we are going to use the user’s time zone setting from their profile. In part one of this series, we stored this setting into a $rootScope variable:

//Retrieve Profile setting in Remote Action in Apex Controller
//refer to Part 1 of this series for full code example
TimeZone tz = userInfo.getTimezone(); // get the timezone
String timezone = tz.toString(); // convert to user’s timezone

//store in $rootScope variable in angular controller
//refer to Part One in this series for full code example
$rootScope.userTimeZone = data.timezone;

Now we can leverage the variable dynamically to change the dates/times to reflect the correct time zone in our app. To do this, we utilize a JavaScript library called Moment.js and Moment-Timezone.js.

1. Create a custom date filter in app.js file to leverage the Moment.js and Moment-Timezone libraries to do the conversion. Inject the $rootScope so you have access to the user’s time zone from their Salesforce profile. Pass in the date format that you want displayed as well.

storeApp.filter('moment', ['$rootScope', function($rootScope) {
   return function(dateString, format) {
     var localTime = moment.tz(dateString, $rootScope.userTimeZone)
     return moment(localTime).format(format);
  };
}]);

2. Now you can display the date in the user’s time zone in the Visualforce page. You can use a Visualforce component to do this or straight HTML. ‘LLL’ is a Moment.js date format.

Example using Visualforce Component:

<td><apex:outputText value="{{ order.CreatedDate | moment:'LLL'}}"/></td>

 Example using just HTML:

<td>{{ order.CreatedDate | moment:'LLL'}}</td>

It is possible to leverage <apex:outputField> to display the user’s time zone instead of a third party library like Moment.js. However, there is a noticeable difference in the page performance as the data is written to two different variables. This will make the page run slower than using straight JavaScript. Unless the data needs to be manipulated in angular later, it is not advised to store the results of the <apex:outputField> into a JavaScript variable. Leverage the Visualforce component as-is when planning to display data from an object.

1. To be able to use the <script> tag in your Visualforce page, include the JQuery.js file prior to including the angular.js file. Otherwise, angular will not recognize a <script> tag. It is best practice to use minified files.

<apex:includeScript value="{!URLFOR($Resource.storeDemoRM,'storeDemoRM/js/jquery/dist/jquery.js')}"/>
<apex:includeScript value="       {!URLFOR($Resource.storeDemoRM,'storeDemoRM/js/angular/angular.js')}"/>

2. Write the results of an <apex:outputField> to a JavaScript variable. In this case you are creating an array of invoice objects that include both <apex:outputField> and <apex:outputText> components.

<script type="text/javascript">
   var vfinvoices = new Array();
   <apex:repeat var="invoice" value="{!invoices}">
      var entity = InvoiceEntity.fromVFPage('<apex:outputText value="{!invoice.Id}"/>',
         '<apex:outputText value="{!invoice.Status__c}"/>',
         '<apex:outputText value="{!invoice.Name}"/>',
         '<apex:outputField value="{!invoice.CreatedDate}"/>',
         '<apex:outputText value="{!invoice.CreatedById}"/>',
         '<apex:outputText value="{!invoice.Description__c}"/>');

      vfinvoices.push(entity);
   </apex:repeat>
</script>

3. Pass the JavaScript array back into the angular controller through the init() method in the Visualforce page.

 <div ng-init="init(vfinvoices)">

4. Store the array as a $scope variable to be rendered in HTML and used elsewhere in angular.

$scope.init = function(vfinvoices){
  $scope.vfinvoices = $window.vfinvoices;
}

5. Leverage the $scope variable in the HTML page (or Visualforce page).

<tr ng-repeat="order in vfinvoices | orderBy:predicate:reverse" class="dataRow">

 

The final option we evaluated for time zone conversion takes place in the Apex Controller. This assumes Visualforce Remoting is used, and not Remote Objects.

1. In the Remote Action of the Apex Controller, convert the DateTime field into the user’s time zone using the format() method. Note a String is returned.

String formattedDate = createdDate.format();

 2. Retrieve the data from the Remote Action and parse into variables. In angular, convert the String back into a DateTime field using JavaScript Date.parse() method. Time zone offsets are preserved using this technique. Returns the DateTime in Milliseconds. Store the variable into $scope.

//converting the String DateTime received from the
// Remote Action call into a millisecond DateTime
var milliDate = Date.parse(stringDate); 

$scope.createdDate = milliDate;

 3. Now you can use the newly created DateTime variable in a standard angular data filter in the HTML/Visualforce page.

<td class="dataCell">{{ createdDate | date:'medium'}}</td>

 

An untested option that may exist is to leverage the TimeZone methods available in the DateUtil Class of the SfdcCore.js file available for download when header information is not shown in a Visualforce page. If you decide to experiment with this class, download the file and include it as a static resource in your project. Salesforce can change this file from release to release; so do not reference it dynamically.

This concludes part two on Internationalization Options in angularJS and Salesforce. Be sure to check out the next post in this series on Language Conversions.

Code samples for the whole series can be found in Github at: https://github.com/cdascenzo/salesforce_angular_internationalization

Don’t want to mess with the code?  Spin up a trial and see the demo in action (some images do not resolve on the Heroku site, don’t worry, the signup still works): https://pacific-inlet-1581.herokuapp.com/

Published
July 29, 2015

Leave your comments...

Internationalization Options in AngularJS and Salesforce: Part 2 – Time Zone Conversions