Suivez votre progression
Accueil Trailhead
Accueil Trailhead

Partage des pages Visualforce entre Classic et Lightning Experience

Objectifs de formation

Une fois cette unité terminée, vous pourrez :
  • Mentionner deux avantages du partage de pages entre Salesforce Classic et Lightning Experience.
  • Expliquer la différence entre le contexte d'interface utilisateur demandé par l'utilisateur et celui dans lequel l'utilisateur se trouve réellement.
  • Décrire trois méthodes différentes pour tester et identifier le contexte d'interface utilisateur actuel de l'utilisateur.

Partage de pages Visualforce entre Classic et Lightning Experience

Dans la mesure du possible, nous vous recommandons de créer des pages Visualforce qui répondent correctement, qu’elles soient exécutées dans Salesforce Classic ou Lightning Experience. Les avantages en termes de réduction de la complexité du code et de la configuration de votre organisation seront considérables. Il y a de plus certains contextes, comme le remplacement par Visualforce d’actions standard, pour lesquels vous n’avez pas le choix. Un remplacement d'action utilise toujours la même page, qu'il soit exécuté dans Salesforce Classic, dans Lightning Experience ou dans l’application Salesforce.

Il est néanmoins tout à fait raisonnable de souhaiter un affichage ou un fonctionnement légèrement ou très différent selon le contexte de l'expérience utilisateur dans lequel la page est exécutée. Dans cette unité, nous allons examiner plusieurs méthodes pour créer des pages qui fonctionnent correctement dans toutes les expériences utilisateur, et comment votre code peut détecter et apporter des modifications dans des contextes spécifiques.

Détecter le contexte d’expérience utilisateur dans le marquage Visualforce et y répondre

Utilisez les variables globales $User.UITheme et $User.UIThemeDisplayed pour identifier le contexte d’expérience utilisateur en cours. Vous pouvez utiliser ces variables dans des expressions Visualforce pour adapter vos pages à Lightning Experience, à Salesforce Classic et à l’application Salesforce.
Ces variables globales renvoient une chaîne qui identifie le contexte d'interface utilisateur en cours de façon unique. Les valeurs possibles pour $User.UITheme et $User.UIThemeDisplayed sont identiques :
  • Theme1 : thème Salesforce obsolète
  • Theme2 : thème d’interface utilisateur Salesforce Classic 2005
  • Theme3 : thème d’interface utilisateur Salesforce Classic 2010
  • Theme4d : thème moderne Salesforce « Lightning Experience »
  • Theme4t : thème de l’application mobile Salesforce
  • Theme4u : thème de Lightning Console
  • PortalDefault : thème du portail client Salesforce
  • Webstore : thème AppExchange Salesforce
Les deux variables sont différentes : $User.UITheme renvoie la présentation que l'utilisateur est supposé afficher, alors que $User.UIThemeDisplayed renvoie la présentation que l'utilisateur affiche réellement. Par exemple, un utilisateur peut disposer de la préférence et des autorisations d'affichage de la présentation de Lightning Experience, mais s'il utilise un navigateur qui ne prend pas en charge cette présentation, par exemple les anciennes versions d'Internet Explorer, $User.UIThemeDisplayed renvoie une valeur différente. En général, votre code doit utiliser $User.UIThemeDisplayed.
Le moyen le plus simple d’utiliser les variables globales de ce thème est d’en utiliser une dans une expression booléenne, telle que {! $User.UIThemeDisplayed == "Theme3" }, dans l’attribut rendered d’un composant. Le composant s'affiche uniquement si la page apparaît dans le contexte d'interface utilisateur souhaité.
<apex:outputText value="This is Salesforce Classic."
    rendered="{! $User.UIThemeDisplayed() == 'Theme3' }"/>
Même si vous pouvez utiliser cette technique sur des éléments d’interface utilisateur individuels, il est généralement plus efficace d’inclure des segments de marquage plus importants dans un composant <apex:outputPanel> ou un composant d’un niveau de bloc similaire, puis de créer des blocs séparés pour chaque interface utilisateur différente que vous voulez présenter. Placez ensuite le test de thème sur l’attribut rendered des blocs, plutôt que sur des composants individuels. Vous obtiendrez de meilleurs résultats et votre code sera moins compliqué.
<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>
Une autre stratégie dans laquelle vous pouvez utiliser cela consiste à sélectionner dynamiquement une feuille de style à inclure dans votre page et à fournir une feuille de style différente pour chaque thème. Le procédé est un peu plus compliqué que vous ne le croyez, car la balise <apex:stylesheet> ne possède pas d’attribut rendered. Dans ce cas, vous devez inclure les composants de la feuille de style dans un autre composant qui contient un attribut rendered. Voici un exemple qui explique comment fournir une feuille de style différente pour chacun des trois thèmes modernes pris en charge par Salesforce.
<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>

Au-delà des concepts de base

Il s’agit d’une façon inhabituelle d’utiliser <apex:variable>, car la valeur de la variable créée n’est pas réellement intéressante. Nous voulons simplement un composant qui ne renvoie pas de sortie tout seul dans le but d’inclure le composant <apex:stylesheet>. C’est comme si le composant <apex:variable> « prêtait » son attribut rendered au composant <apex:stylesheet> inclus.

C’est une chance que la variable en elle-même ne nous importe pas, car une autre particularité de l’enveloppement du composant <apex:variable> autour d’un autre élément est que la variable n’est pas véritablement créée ! Fonctionnalité ou bogue ? Disons que c’est un comportement indéfini, et évitons d’utiliser la variable uiTheme ailleurs.

Détecter le contexte d'expérience utilisateur dans JavaScript et y répondre

Il est essentiel de détecter le contexte d'expérience utilisateur en cours dans le code JavaScript si vous utilisez souvent JavaScript dans vos pages et vos applications. Cela est particulièrement important pour gérer la navigation dans votre code JavaScript de la bonne manière.

Pour détecter ce que voit l’utilisateur en JavaScript, nous utilisons une méthode similaire à celle employée pour déterminer le contexte d'expérience de l’utilisateur actuel dans Visualforce. Appelez la variable globale UITheme.getUITheme() pour retourner une valeur qui identifie le thème d’interface de l’utilisateur actif.

Ici, le code vérifie si le contexte d’expérience de l’utilisateur actuel est le thème Lightning Experience.

function isLightningDesktop() {
  return UITheme.getUITheme === "Theme4d";
}

Définition du contexte d'expérience utilisateur dans Apex

Utilisez les méthodes de système UserInfo.getUiTheme() et UserInfo.getUiThemeDisplayed() afin d’identifier le contexte d’expérience de l’utilisateur actif dans le code Apex. Vous pouvez les utiliser quand les méthodes d'action ou les propriétés de votre contrôleur doivent s'exécuter différemment suivant le contexte.
L'exemple suivant illustre la façon d'utiliser ces méthodes en les rendant disponibles via des méthodes d'obtention dans une extension de contrôleur.
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();
    }
}
Vous pourriez évidemment travailler sur les valeurs dans votre code Apex, plutôt que de renvoyer directement les résultats de l'appel de méthode.
Ces méthodes de système Apex renvoient une chaîne qui identifie le contexte d'interface utilisateur actuel de façon unique. Les valeurs possibles renvoyées par ces méthodes sont identiques à celles renvoyées par les variables globales $User.UITheme et $User.UIThemeDisplayed.
  • Theme1 : thème Salesforce obsolète
  • Theme2 : thème d’interface utilisateur Salesforce Classic 2005
  • Theme3 : thème d’interface utilisateur Salesforce Classic 2010
  • Theme4d : thème moderne Salesforce « Lightning Experience »
  • Theme4t : thème de l’application mobile Salesforce
  • Theme4u : thème de Lightning Console
  • PortalDefault : thème du portail client Salesforce
  • Webstore : thème AppExchange Salesforce

L’utilisation de ces méthodes dans le code du contrôleur côté serveur doit être limitée, du moins par comparaison avec la fourniture d’un marquage Visualforce ou d’un code JavaScript différent. Il est recommandé que le code de votre contrôleur et de votre extension de contrôleur soit neutre en matière de contexte d'expérience utilisateur. Laissez votre code frontal, qu’il s’agisse de Visualforce ou de JavaScript, gérer les différences d’interface utilisateur.

Effectuer des requêtes pour Lightning Experience grâce à SOQL et à des accès à l'API

Bien que nous ne recommandions pas cette technique, il est possible d'effectuer directement des requêtes SOQL concernant le contexte préféré de l'utilisateur actuel.
La requête SOQL de base est la suivante.
SELECT UserPreferencesLightningExperiencePreferred FROM User WHERE Id = 'CurrentUserId'
Le résultat est une valeur de préférence brute que vous devrez convertir en quelque chose d'utilisable.
Cela ne concerne que la page Visualforce la plus simple possible qui exécute la requête SOQL ci-dessus et qui affiche le résultat sur une page.
<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>

Effectuer une requête directement pour la préférence de l'utilisateur concernant Lightning Experience est déconseillé. Le résultat indique les paramètres de préférence actuels de l'utilisateur, mais pas l'expérience utilisateur affichée sur l'écran. Il existe plusieurs cas d'utilisation où la valeur de préférence pourrait ne pas refléter l'expérience utilisateur en cours. Pour déterminer l’expérience utilisateur réelle en cours dans la demande actuelle, utilisez $User.UIThemeDisplayed ou UserInfo.getUiThemeDisplayed().