Migration to Salesforce LEX: A Lightning Developer Guide

Migration
to Salesforce
LEX: A Lightning
Developer Guide

Developer Guide

It’s been a while since Salesforce announced their new and sexy Lightning Experience (a.k.a. LEX) – I found this amazing, but lots of organizations keep using Salesforce Classic UI. Though Salesforce offers Lightning Experience to all new users, the Salesforce Classic version is reserved to the veterans with their customized platform features, specific workflows thus finding it difficult to migrate.

Salesforce Classic is still there, but one day Salesforce will surely stop the Classic UI version support. But it seems, that not only some missing features are a problem with migration, but also some massive coding concepts changes. This lightning developer guide will cover both – what’s still not released in LEX, and what changes developer needs to apply to existing customization so this migration can bring benefits for business.

Chapter I: Pre-development considerations

Prior to development, you need to check, if it’s possible to migrate to LEX – by Salesforce features which are already in the organization. Lightning development team is doing it’s best to deliver most of the functionality to LEX, so, it only means that you can plan your migration pretty soon. Below is the list of the features, which are not available in LEX:

  • General Platform
    • Javascript buttons
    • Mass Actions On A Records List View
    • Mass Inline Editing for List View
    • Recycle Bin
    • Printable View
    • Manual Sharing
  • Sales Cloud
    • Account Contact Roles
    • Account Partners
    • Divisions
    • Mass Email (for Users, Cases, Campaigns). Mass Email Scheduling
    • Customizable Forecasting
    • Similiar Opportunities
    • Territory Management
    • Opportunity PartnersSales Cloud
  • Service Cloud
    • Public Knowledge
    • Solutions
    • Ideas
    • SOS
    • Knowledge (can be used, but lots of functionality is unavailable at this moment)

If your business is OK with switching to Salesforce Classic to continue to use those features – then you’ll be fine to proceed with customization changes. If not – this lightning developer guide can help.

→ Have a look at the specific Salesforce offerings by Avenga, as it will help you see the Salesforce platform from a different, more individualized perspective

Chapter II: Moving existing customization to LEX

Basically, two main issues when you’re migrating will be:

  • Custom Javascript Buttons (some people still have all the business logic developed as a series of custom buttons!)
  • Visualforce Pages Service Cloud

Custom Javascript buttons

The main point here will be – change the javascript buttons to lightning quick actions, here it’s described in details on how to do this. The reason to ban javascript buttons is simple – it’s unsafe, and usually not very user-friendly. Below I’ll put a simple example as a part of Lightning developer guide of how to change the button from Javascript to a Lightning Quick Action.

1. Identify the process behind your button

There are a lot of the use cases for Javascript buttons – from simple record update – to difficult business process of creation of the data model along with triggering the webservice from Apex. Basically, the more complex your javascript button logic is – the worse is system design – and this requires rework of the whole process. But for a simple record update/new records creation, there’s a very simple process of transition, which as a result will give No Changes for an end user flow.

2. Move the code!

The simple example of the case javascript button named “Transform Social Case”

transform-social-case.js

{!REQUIRESCRIPT("/soap/ajax/23.0/connection.js")};
 
var сaseObject = new sforce.SObject("Case");
сaseObject.Id = "{!Case.Id}";
var type = "{!Case.Type}";
 
if ( type != "Social" )
{
    alert("error message for non-social case");
}
 
else
{
    сaseObject.Type = "Social Plus";
    var result = sforce.connection.update([сaseObject]);
 
    if (result[0].success == "true")
    {
        location.reload(true);
    }
 
    else
    {
        alert(result[0].errors.message);
    }
}

The process is simple as 1-2-3: Check the case type and update it if it’s social. It’s super easy to move this process to lightning component – no Apex required!

TransformSocialCaseAction.cmp

<aura:component implements="force:LightningQuickActionWithoutHeader,force:hasRecordId">
 
    <aura:attribute name="record" type="Object"/>
    <aura:attribute name="simpleRecord" type="Object"/>
    <aura:attribute name="recordError" type="String"/>
 
    <force:recordData aura:id="recordLoader" recordId="{!v.recordId}" layoutType="FULL" targetRecord="{!v.record}" targetFields="{!v.simpleRecord}" targetError="{!v.recordError}" recordUpdated="{!c.handleRecordUpdated}" />
 
    <aura:if isTrue="{!not(empty(v.recordError))}">
         
        <div class="recordError">
            <ui:message title="Error" severity="error" closable="true">
                {!v.recordError}
            </ui:message>
        </div>
 
    </aura:if>
</aura:component>

TransformSocialCaseActionController.js

({
    handleRecordUpdated: function(component, event, helper) {
        var eventParams = event.getParams();
        if (eventParams.changeType === "LOADED") {
            helper.updateSocialTypeCase(component);
        }
    }
})

TransformSocialCaseActionHelper.js

({
    updateSocialTypeCase : function(component) {
        var caseObject = component.get("v.record");
        if (caseObject.Type != "Social") {
            component.set("v.recordError", "error message for non-social case");
        } else {
            caseObject.Type = "Social Plus"
            component.set("v.record", caseObject);
            component.find("recordLoader").saveRecord(
                $A.getCallback(
                    function(saveResult) {
                        //You can add error handling here
                    }
                )
            );
        }
    }
})

A Final step will be simple – create action with this component for Case sObject and add it to the layout! No Apex/3rd-party library required.

Visualforce pages

Every scenario here is pretty unique, however, few common page types can be separated: Reports pages, PDF-rendered pages, and new/edit/view pages. For the PDF we’ll keep it as it is – as no changes and no ability to render lightning components as a pdf. But for the report-like and new/edit/view pages we have to change it to the lightning UI;

1. Set the lightning styles

If you want it fast, your page is too big to transform and you just want it to look like Lightning UI? Quickest way will be just to add styles – although, it is limited (e.g. some VF tags are not available – click here for details). How to do this?

LightningPage.vfpage

<apex:page showHeader="false" standardStylesheets="false" sidebar="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0">
  <apex:slds />   
    <div class="slds-scope">
        <!-- CONTENT GOES HERE -->
    </div>
</apex:page>

That’s IT! No changes in your controller, only some small changes in the page markup! But if you want to go even further.

2. Transform the visualforce page into a set of lightning components

Well.. sometimes – this is hardcore, especially, if the backend composition is hard and depends on a viewstate – because there’s no viewstate in Lightning UI. I will not focus on the frontend changes – they will be huge, and they will be different from page to page. Please note, that some things are critically changed, especially input lookup (native one is unavailable) and some chatter vf tags.

But how to transform the backend? At first, we should answer two questions: – Should we use page both on Salesforce Classic and in Lightning UI? If yes, then consider lightning styles for a visualforce page. Only do this if you cannot utilize VF page in two different places – has your backend developed through @RemoteAction annotated methods, or it’s done via view state instance methods?

RemoteAction changes

RemoteAction changes are very simple – both VF and LC can use the same controller with the same methods! An example below will compile and work:

TestController.apxc

public class TestController {
    @AuraEnabled @RemoteAction
    public static void remoteAction() {
        //magic in here
    }
}

Instance methods changes

Let’s say, that in 95% of all cases visualforce controllers contain both logic, model and view model data (view model data – is button enabled, Boolean showErrorMessage, etc). To be able to split to two different controllers – one for VF and one for LC we need to utilize full split of the classes: there will be one model, two view models (if they are actually needed – can be just for VF while aura will handle all of the view models as aura:attributes) and one business logic component. As the source, I’ll take this VF controller:

TestVFController.apxc

public with sharing class TestVFController {
    public Boolean hasAccountResults { get; private set; }
    public Boolean hasBrandResults { get; private set; }
    public Boolean isPopupEnabled { get; private set; }
    public Boolean hasSearchBeenInitiated { get; private set; }
    public List&lt;SObjectModel&gt; accounts;
    public List&lt;SObjectModel&gt; brands;
    public TestVFController() {
        //some logic to init model
    }

    public void search(String term) {
        //actual SOSL queries and model creation from SOSL results
    }
 
    public void save() {
        //actual DML operations to unwrap the model and save
    }
}

We should split that. There should be some service which will perform some operations on a model (not inside the controller), we should somehow unite the model and separate the model from the view model. Don’t forget why we’re doing this – to be able to reuse all of the code we did on a Lightning UI. Resulting code will be something like:

TestVFController.apxc

public with sharing class TestVFController {
    public ViewModel viewModel { get; private set; }
 
    //names should correspond to the processes and tasks for this class.
    //e.g. AccountModel if only accounts are in use, UpdateService if the service is doing update and so on.
    // I'm putting the simple realization at the bottom of this class
 
    public Model model { get; private set; }
    private Service service {get; private set;}

    public TestVFController() {
        viewModel = new ViewModel();
        service = new Service(); //should be common for two classes
        model = service.initializeModel();
    }
 
    public void search(String term) {
        service.search(model, term);
    }
 
    public void save() {
        service.save(model);
    }
 
    public class ViewModel {
        //some variables to control the view for VF page e.g. Boolean hasAccountResults
        public ViewModel(){}
    }
 
    public class Model {
        public List<SObjectModel> accounts;
        public List<SObjectModel> brands;
        public Model(){}
    }
 
    public class Service {
        public Service(){}
        public void search(...) {}
        public void save(...) {}
    }
}

TestLCController.apxc

public with sharing class TestLCController {
    @AuraEnabled
    public static State initializeLightningComponent() {
        State lightningComponentState = new State();
        lightningComponentState.initialize(service);
        return lightningComponentState;
    }
 
    @AuraEnabled
    public static State search(State lightningComponentState, String term) {
        lightningComponentState.service.search(lightningComponentState.model, term);
        return lightningComponentState; //can be removed. But usually state is changed after operations like search
    }
 
    @AuraEnabled
    public static State save(State lightningComponentState) {
        lightningComponentState.service.save(lightningComponentState.model);
        return lightningComponentState; //can be removed. But usually state is changed after operations like search
    }
 
    public class State {
        public @AuraEnabled TestVFController.ViewModel viewModel;
        public @AuraEnabled TestVFController.Model model;
        public @AuraEnabled TestVFController.Service service;
        public State() {}
        public void initialize() {
            viewModel = new TestVFController.ViewModel(); //This one can be completely removed as usually
                //view model info are not passed to apex controller and it's value determines inside of the javascript helpers
            service = new TestVFController.Service();
            model = service.initializeModel();
        }
    }
}

A state is stored in LC as an Aura. Attribute – an idea is to mock the view state by creating a new version of it. For creation and managing this State class, you can utilize Memento pattern.

Migration to Salesforce Lightning

Final thoughts

Lightning Components – is a different view of the frontend development – it’s made to be reusable. Use the same button set for different pages, use Lightning components as building blocks. Visualforce Components were similar – but developers worldwide didn’t utilize the power of components and they are not designed as LC. So, this frontend change is the greatest change for development and a big challenge for developers – will developers change their mind about reusability?

Hopefully with our lightning development guide your migration is easier and quicker.

Other articles

or

Book a meeting

Zoom 30 min

or call us+1 (800) 917-0207

Start a conversation

We’d like to hear from you. Use the contact form below and we’ll get back to you shortly.