Share Visualforce Pages Between Classic and Lightning Experience
Learning Objectives
- List two benefits of sharing pages between Salesforce Classic and Lightning Experience.
- Describe the difference between the user interface context requested by the user, and the user interface context the user is actually in.
- Describe three different ways to test for and determine the current user’s user interface context.
Sharing Visualforce Pages Between Classic and 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 this unit we’ll look at a variety of ways to create pages that work correctly in all user experiences, and how your code can detect and make changes for specific contexts.
Detecting and Responding to the User Experience Context in Visualforce Markup
- Theme1—Obsolete Salesforce theme
- Theme2—Salesforce Classic 2005 user interface theme
- Theme3—Salesforce Classic 2010 user interface theme
- Theme4d—Modern “Lightning Experience” Salesforce theme
- Theme4t—Salesforce mobile app theme
- Theme4u—Lightning Console theme
- PortalDefault—Salesforce Customer Portal theme
- Webstore—Salesforce AppExchange theme
<apex:outputText value="This is Salesforce Classic." rendered="{! $User.UIThemeDisplayed() == 'Theme3' }"/>
<apex:outputPanel rendered="{! $User.UIThemeDisplayed == 'Theme3' }"> <apex:outputText value="This is Salesforce Classic."/> <apex:outputText value="These are multiple components wrapped by an outputPanel."/> </apex:outputPanel> <apex:outputPanel rendered="{! $User.UIThemeDisplayed == 'Theme4d' }"> <apex:outputText value="Everything is simpler in Lightning Experience."/> </apex:outputPanel>
<apex:page standardController="Account"> <!-- Salesforce Classic "Aloha" theme --> <apex:variable var="uiTheme" value="classic2010Theme" rendered="{!$User.UIThemeDisplayed == 'Theme3'}"> <apex:stylesheet value="{!URLFOR($Resource.AppStyles, 'classic-styling.css')}" /> </apex:variable> <!-- Lightning Desktop theme --> <apex:variable var="uiTheme" value="lightningDesktop" rendered="{!$User.UIThemeDisplayed == 'Theme4d'}"> <apex:stylesheet value="{!URLFOR($Resource.AppStyles, 'lightning-styling.css')}" /> </apex:variable> <!-- Salesforce mobile theme --> <apex:variable var="uiTheme" value="SalesforceApp" rendered="{!$User.UIThemeDisplayed == 'Theme4t'}"> <apex:stylesheet value="{!URLFOR($Resource.AppStyles, 'mobile-styling.css')}" /> </apex:variable> <!-- Rest of your page --> <p> Value of $User.UIThemeDisplayed: {! $User.UIThemeDisplayed } </p> </apex:page>
Beyond the Basics
This is an unusual way to use <apex:variable>, because we’re not actually interested in the value of the variable created. Instead we just want a component that doesn’t render any output of its own to wrap the <apex:stylesheet> component. You can think of this as <apex:variable> “lending” its rendered attribute to the wrapped <apex:stylesheet> component.
It’s a good thing we don’t care about the variable itself, because another unusual aspect of wrapping the <apex:variable> component around something else is that the variable isn’t actually created! Feature or bug? Let’s call it...undefined behavior, and avoid using the uiTheme variable elsewhere.
Detecting and Responding to the User Experience Context in JavaScript
To detect what the user sees in JavaScript, we use a similar method to determining the current user experience context in Visualforce. Call the UITheme.getUITheme() global variable to return a value that identifies the current user interface theme.
Here the code checks if the current user experience context is the Lightning Experience theme.
function isLightningDesktop() { return UITheme.getUITheme === "Theme4d"; }
Determining the User Experience Context in Apex
public with sharing class ForceUIExtension { // Empty constructor, required for Visualforce controller extension public ForceUIExtension(ApexPages.StandardController controller) { } // Simple accessors for the System.UserInfo theme methods public String getContextUserUiTheme() { return UserInfo.getUiTheme(); } public String getContextUserUiThemeDisplayed() { return UserInfo.getUiThemeDisplayed(); } }
- Theme1—Obsolete Salesforce theme
- Theme2—Salesforce Classic 2005 user interface theme
- Theme3—Salesforce Classic 2010 user interface theme
- Theme4d—Modern “Lightning Experience” Salesforce theme
- Theme4t—Salesforce mobile app theme
- Theme4u—Lightning Console theme
- PortalDefault—Salesforce Customer Portal theme
- Webstore—Salesforce AppExchange theme
Using these methods in server-side controller code should be rare, at least compared to providing different Visualforce markup or JavaScript code. It’s a best practice for your controller and controller extension code to be neutral in terms of the UX context. Let your front end code, whether Visualforce or JavaScript, handle the user interface differences.
Querying for Lightning Experience via SOQL and API Access
SELECT UserPreferencesLightningExperiencePreferred FROM User WHERE Id = 'CurrentUserId'
<apex:page> <script src="/soap/ajax/36.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 Id, 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 actually is on their screen. There are several use cases where the preference value might not reflect the user experience that’s actually being delivered. To determine the actual user experience being delivered in the current request, use $User.UIThemeDisplayed or UserInfo.getUiThemeDisplayed().