We recommend that, wherever possible, you create Visualforce pages that behave correctly whether they run in Salesforce Classic or Lightning Experience. The benefits in terms of reduced complexity in your organization’s code and configuration are obvious. And there are a number of contexts, such as Visualforce overrides of standard actions, where you don’t have a choice. An action override always uses the same page, whether you’re running in Salesforce Classic, Lightning Experience, or Salesforce1. In this chapter ...
• Detecting the User Experience Context
• Querying for Lightning Experience
It’s perfectly reasonable, though, to want slightly or significantly different behavior or styling that’s based on the user experience context in which the page is running. In Winter ’16 the ability to detect the current user experience context is extremely limited, but it should cover the essentials.
via SOQL and API Access
Detecting the User Experience Context
In Winter ’16 there’s no supported method for determining the current user experience context. However, there’s a technique that has proven reasonably reliable, and covers the most essential use case. The technique depends on detecting the presence of a JavaScript utility object that’s unique to Lightning Experience and Salesforce1.
Wait, what? When did Salesforce1 get invited to this party?
Well, this goes back to our discussion of app containers, and especially to the concept of single-page applications. As you know from that unit, Lightning Experience is a single-page application (or SPA), and when you look in the URL field of your browser, you can see that SPA is named “one.app.” One.app is a bundle, or collection of resources, including HTML markup, styles, data, and especially a lot of JavaScript.
Well guess what. If you were to look at what the Salesforce1 app is running inside the native client, it’s also a SPA named one.app. And in fact, it’s pretty much the same as the Lightning Experience one.app. While there’s a lot of complex details under the covers, for our purposes here you can think of the Lightning Experience and Salesforce1 apps as the same app, with different expressions for different form factors.
Again, there’s lots of actual differences when you get into the details. We’re simplifying here—work with us!
One thing that happens to a Visualforce page when it runs inside one.app—whether Lightning Experience or Salesforce1—is that a JavaScript utility object is added to the page. This object, sforce.one, is what provides the navigation APIs discussed in the Using Visualforce in Lightning Experience unit. And because it’s not available on your page in Salesforce Classic, we can use its presence or absence to know something about the current user experience context.
With me so far? Let’s do something practical, and look at some actual code. Here’s a simple JavaScript function you can use to detect if you’re in Lightning Experience or Salesforce1, or in Salesforce Classic.
function isLightningExperienceOrSalesforce1() {
return((typeof sforce != 'undefined') && sforce && (!!sforce.one)); }
You can use this utility function to make decisions in your JavaScript code. For example:
if( isLightningExperienceOrSalesforce1() ) { // Do something for Lightning Experience }
else {
// Use classic Visualforce }
With some cleverness you can use this technique and add some device detection logic, and tease apart Lightning Experience and Salesforce1. However, these techniques are likely to be fragile. Be sure to encapsulate them into your own utility functions, so that you can update them easily if you run into issues.
Limitations of Detecting sforce.one
There’s no sugar coating this, this solution isn’t great. If you need to build functionality that varies considerably between user experience contexts, it’s currently tough to do that in a supportable way. We hear you and, Safe Harbor, we’re working on it.
In the meantime, let’s make sure we’re clear on some limitations that you’ll need to keep in mind as you build your pages.
This technique works in JavaScript, which means it’s happening after the Visualforce and Apex code have already run. This means you can’t use isLightningExperienceOrSalesforce1() in a Visualforce expression—say, in the rendered attribute of a Visualforce tag. So you can’t, for example, use an <apex:stylesheet> tag to include in a Lightning Experience-specific stylesheet.
Detecting the User Experience Context Sharing Visualforce Pages Between Classic and Lightning
There are ways around this. For your UX context-specific stylesheets, you can add them to the page using JavaScript. For other page elements, you can use JavaScript to hide or show them, depending on the context.
This is work, no two ways about it. We again want to encourage you to minimize the differences in behavior that your page provides across user interface contexts.
We also have to say that while you can count on sforce.one being around for a while, detecting its presence to determine UX context is not a best practice, but more the opposite. Again, isolate your checks for the user interface context into utility functions that you can update once there is a supported way to determine it.
Querying for Lightning Experience via SOQL and API Access
Although we don’t recommend this technique, you can query for the current user’s preferred user experience directly using SOQL. The basic SOQL query is the following.
SELECT UserPreferencesLightningExperiencePreferred FROM User WHERE Id = 'CurrentUserId' The result is a raw preference value, which you need to convert into something useable.
Here’s just about the simplest possible Visualforce page that runs the above SOQL query and displays the result on the page. <apex:page>
<script src="/soap/ajax/35.0/connection.js" type="text/javascript"></script> <script type="text/javascript">
// Query for the preference value
sforce.connection.sessionId = '{! $Api.Session_ID }';
var uiPrefQuery = "SELECT UserPreferencesLightningExperiencePreferred " +
"FROM User WHERE Id = '{! $User.Id }'";
var userThemePreferenceResult = sforce.connection.query(uiPrefQuery); // Display the returned result on the page
document.addEventListener('DOMContentLoaded', function(event){ document.getElementById('userThemePreferenceResult').innerHTML =
userThemePreferenceResult; });
</script>
<h1>userThemePreferenceResult (JSON)</h1>
<pre><span id="userThemePreferenceResult"/></pre> </apex:page>
Querying for the user’s Lightning Experience preference directly is discouraged. The result tells you what the user’s current preference
setting is, not what user experience is actually on their screen. If the user is using an older browser, the preference value might not reflect the user experience that’s actually being delivered.
Querying for Lightning Experience via SOQL and API Access Sharing Visualforce Pages Between Classic and Lightning