Building alternative Path using Lightning components and Flows.

Introduction

In this article we will see how to create Flows and reusable Lightning components to simulate the standard Path behavior.
The Standard Lightning Path is a standard component usable in Lighting record pages of most Salesforce standard Object and all custom objects.

Unfortunately, this component has some limitations that our solution can answer to:

  • It cannot be used for the Case Object on Communities.
  • The Path is dependent to a picklist field of the chosen object.
  • It can display maximum five fields per step.

Figure 1: Custom Path final result.

Audience.

This article describes steps to creates Lightning components.
A developer background is helpful to understand the mechanics but not needed to reproduce the below steps.

Knowledge on how to create (visual) Flows however is needed since this article does not offer an in-depth description of the process and only focuses on the relevant aspects.

Building alternative path Lightning components.

Figure 2: Page composition.

We will build two separate Lightning components to achieve this result.
One component to display the Progress Indicator bar (Path equivalent) and one component to display our Flow.
This separation gives us more options when designing our Lightning record page layout and Communities page layouts since the components can be placed separately on the page.

To communicate through the Lightning record page, our components will use events:

Figure 3: Components communication.

Progress Indicator lightning component.

There are several base Lightning components usable to recreate the Salesforce standard Path visuals and behavior:

  • lightning:Path
  • lightning:picklistPath
  • lightning:progressIndicator

We will use the lightning:progressIndicator since it’s specifically designed to work with Flows (even though it’s not mandatory).

Notes: Among the custom solutions, lightning:Path is the closest to the standard Salesforce path behavior but it’s not working properly on Communities at the moment.

Let’s start:

– First, we head to the developer console:

Figure 5: Enter developer console.

– From there we create our first Lightning component: File > New > Lighting Component

Figure 6: Creating CustomPath lightning component.

– Replace the existing code with the following one:
Don’t try to save yet, you would get an error. That’s because the markup references a component that does not exist yet (L19), we will handle that later.


1. <aura:component implements="forceCommunity:availableForAllPageTypes, 
2. flexipage:availableForAllPageTypes, 
3. flexipage:availableForRecordHome, 
4. force:hasRecordId" 
5. access="global"> 
6. 
7. 
8. <!-- ATTRIBUTES --> 
9. <aura:attribute name="currentStage" 
10. type="Object" 
11. description="Will contains the current stage of the flow."/> 
12. 
13. <aura:attribute name="activeStages" 
14. type="Object[]" 
15. description="Will contains all the active stage of the flow. We contruct our path based on this list"/> 
16. 
17. <!-- HANDLERS --> 
18. <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> 
19. <aura:handler event="c:statusChanged" action="{!c.handleStatusChanged}"/> 
20. 
21. <!-- MARKUP --> 
22. <br></br> 
23. <lightning:progressIndicator aura:id="progressIndicator" 
24. currentStep="{!v.currentStage.name}" 
25. type="path" 
26. variant="base" 
27. /> 
28. 
29. </aura:component> 

Notes:
The Component implements the forceCommunity:availableForAllPageTypes interface that makes it available for Communities’ pages.

– We add a JavaScript controller to our component. To do that we click on the CONTROLLER button on the right side of the screen.

Figure 7: Creating CustomPath controller.

– Replace the existing code with the following one:



1.	({  
2.	      
3.	    //Launched at component initialization.  
4.	    doInit  : function(component, event, helper) {  
5.	          
6.	        //Call helper to create/Update the Porgression indicator bar  
7.	        helper.initPath(component, event, helper);  
8.	          
9.	    },  
10.	      
11.	    /* 
12.	     * Launched when StatusChanged event is received. 
13.	     * Updates the Porgression indicator bar based on new information recieved from the event. 
14.	     */  
15.	    handleStatusChanged : function(component, event, helper){  
16.	          
17.	        //getting currentStage and activeStages parameters from the "StatusChanged" event and   
18.	        //updating the currentStage and activeStages attributes in the markup.  
19.	        component.set("v.currentStage", event.getParam("currentStage"));  
20.	        component.set("v.activeStages", event.getParam("activeStages"));  
21.	          
22.	        //Call helper to create/Update the Porgression indicator bar  
23.	        helper.initPath(component, event, helper);  
24.	          
25.	          
26.	    }  
27.	      
28.	}) 

– Save (ctrl + s).

– We add a JavaScript helper to our component the same way we did with the controller but this time selecting the HELPER button.

– Replace the existing code with the following one:


1.	({  
2.	      
3.	    /* 
4.	     * Function that will dynamically construct the path (progressIndicator) bar based on the value of the "activeStages" attribute. 
5.	     */  
6.	    initPath : function(component, event, helper) {  
7.	          
8.	        var progressIndicator = component.find('progressIndicator');  
9.	        var body = [];  
10.	          
11.	        var activeStages = component.get("v.activeStages");  
12.	          
13.	        //for each value in the activeStages list, we create a new element of "lightning:progressStep" and  
14.	        //we add it to the progressIndicator component to costruct our path.  
15.	        var stage;  
16.	        for (var key in activeStages) {  
17.	              
18.	            stage = activeStages[key];  
19.	              
20.	            $A.createComponent(  
21.	                "lightning:progressStep",  
22.	                {  
23.	                    "aura:id": "step_" + stage.name,  
24.	                    "label": stage.label,  
25.	                    "value": stage.name  
26.	                },  
27.	                function(newProgressStep, status, errorMessage){  
28.	                    // Add the new step to the progress array  
29.	                    if (status === "SUCCESS") {  
30.	                        body.push(newProgressStep);  
31.	                        progressIndicator.set("v.body", body);  
32.	                    }  
33.	                      
34.	                    //Handle error cases here.  
35.	                }  
36.	            );  
37.	        }  
38.	          
39.	    }  
40.	})  

– Save (ctrl + s).

Now, remember that we could not save our component markup during the first step ?
Our component markup was referencing (Ligne 19) an event that we have not yet created.
This event will allow our component to communicate with other components in the page.

Let’s create it now: File > New > Lightning Event

Figure 8:Creating StatusChanged event.

– Replace the existing code with the following one:


1.	<aura:event type="APPLICATION">  
2.	    <aura:attribute name="currentStage" type="Object"/>  
3.	    <aura:attribute name="activeStages" type="Object[]"/>  
4.	</aura:event>  

– Save (ctrl + s).

– Head back to the markup (customPath.cmp tab) and save (ctrl + s).

And that’s it for the Custom Path component.
It can already be added to the lightning record page of any object but will not display anything for the moment.

Flow embedded lightning component.

Figure 9: Custom flow lightning component.

The Flow component will embed a Flow and will be able to send information about the Flow to other components in the page (for instance to the Progression indicator we created earlier).

Let’s start:

– In the developer console, we create the customFlow lightning component:

Figure 10: Creating CustomFlow lightning component.

– Replace the existing code with the following one:



1.	<aura:component implements="flexipage:availableForRecordHome,  
2.	                            force:hasRecordId,  
3.	                            forceCommunity:availableForAllPageTypes"  
4.	                access="global" >  
5.	      
6.	    <!-- ATTRIBUTES -->  
7.	      
8.	    <aura:attribute name="flowName"   
9.	                    type="String"   
10.	                    description="ApiName of the flow."/>  
11.	  
12.	    <aura:attribute name="currentStage"   
13.	                    type="Object"   
14.	                    description="Will contains the current stage of the flow."/>  
15.	      
16.	    <aura:attribute name="activeStages"   
17.	                    type="Object[]"   
18.	                    description="Will contains all the active stage of the flow. We contruct our path based on this list"/>  
19.	      
20.	    <!-- HANDLERS -->  
21.	    <aura:handler name="init" value="{!this}" action="{!c.init}" />  
22.	      
23.	    <!-- EVENTS -->  
24.	    <aura:registerEvent name="statusChanged" type="c:statusChanged"/>  
25.	      
26.	    <!-- MARKUP -->  
27.	    <article class="slds-card">  
28.	          
29.	        <lightning:flow aura:id="caseFlow"   
30.	                        onstatuschange="{!c.handleStatusChange}"/>  
31.	    </article>  
32.	</aura:component>  

– We add a JavaScript controller to our component.
– Replace the existing code with the following one:



1.	({    
2.	    //Start the defined flow.  
3.	    init : function (component) {  
4.	          
5.	        var flowName = component.get("v.flowName");  
6.	  
7.	        if(flowName){  
8.	  
9.	            //caseFlow: is the identifier of the flow component in the markup (the view).  
10.	            var flow = component.find("caseFlow");  
11.	            var flowName = component.get("v.flowName");  
12.	            flow.startFlow(flowName);  
13.	  
14.	        }  
15.	    },  
16.	      
17.	      
18.	    /* 
19.	     * Handle status change of the flow (user click on next, finish etc...). 
20.	     * Send an event that boradcast the current stage and the active stages of the flow  
21.	     * to the other lightning component of the page. 
22.	     */  
23.	    handleStatusChange : function (component, event) {  
24.	          
25.	        var flowName = component.get("v.flowName");  
26.	  
27.	        if(flowName){  
28.	  
29.	            // Pass $Flow.ActiveStages into the activeStages attribute  
30.	            // and $Flow.CurrentStage into the currentStage attribute  
31.	            component.set("v.currentStage", event.getParam("currentStage"));  
32.	            component.set("v.activeStages", event.getParam("activeStages"));  
33.	          
34.	            var currentStage = component.get("v.currentStage");  
35.	            var activeStages = component.get("v.activeStages");  
36.	          
37.	            //Fire the statusChanged to transmit the "currentStage" and "activeStages" attributes to the other components.  
38.	            var statusChanged = $A.get("e.c:statusChanged");  
39.	            statusChanged.setParams({   
40.	                "currentStage" : currentStage,  
41.	                "activeStages" : activeStages  
42.	                                    });  
43.	            statusChanged.fire();  
44.	  
45.	        }  
46.	  
47.	  
48.	          
49.	          
50.	    },  
51.	})  

Now we want to create a design resources for our customFlow lightning component.
This will allow administrators to specify which flow they want to embed in the component.
This will be configurable in the lightning app builder or the community builder.

– We click on the DESIGN button on the rigth:

Figure 11: CustomFlow design ressource.

– Replace the existing code with the following one:



1.	<design:component>  
2.	    <design:attribute name="flowName" label="Flow Name" description="ApiName of the flow we want to display." />  
3.	</design:component> 

Both components are now built and can be used in Lightning record page as well as community detail pages.

Don’t forget to set the Flow Api name in the CustomFlow lightning component parameters of the Lightning app builder (or community appbuilder) to define which flow should be used:

Figure 12:Setup flow api name in Lightning app Builder.

Building Case sample flow.

To test our component, we need an existing flow.

We will not describe step by step how to create a flow here but focus on the definition of the flow stages that will be used later in our Progression Indicator component.

Here is what will look like our reference flow:

Figure 13: Sample flow process.

This sample flow handles the Case record process from its creation to its closure.

Our Case has four status:

  • New = > has just been created (New stage).
  • Pending => Is waiting for customer answer (Need more information stage).
  • Working => Is processed by Customer support agents (Being processed stage).
  • Closed => Is closed (Closed stage).

Our flow will also have four stages to mirror the Case status but know that the Case status and the flow stages can be independent to one another.

To create the four stages of our Flow that will later compose the Progression Indicator bar at the top of our Case lightning record page we proceed as follow:

– In the flow designer we go to the Resources tab and double click Stage.

Figure 14: Creating flow stages.

– Then we create the first stage as follow:

Label: New
Unique name: NewStage
Order: 10
Acive by Default: true

Figure 15: Define flow first stage.

– We create the other stages as follow:

Label Unique name Order Acive by Default
Need more information NeedMoreInformationStage 20 true
Being processed BeingProcessedStage 30 true
Closed ClosedStage 40 true

You can see the created stage in the Explorer tab:

Figure 16: Visualize flow stages.

Bibliography

Training sources:

Tools:

 

Comments 3

  1. It works fine apart from one thing. if the user hits the previous button in my flow they are greeted with the following error message! (The delimiter of “StageVariable” stage is missing.) I really like the stage progress bar but i don’t think i can keep it if the user can’t return to the previous screen 🙁

    1. Hello Nathan,

      Thank you for you post.
      Please can you share some more details about the flow you have implemented (e.g: screenshots) and your general use case for this flow.
      I could not reproduce the issue.

      *I sent you a PM for you to answer to.

  2. Hi Thank you for the solution,
    what about when all stages are completed then show all of them marked as completed?

Leave a Reply

Your email address will not be published. Required fields are marked *