UPDATE: Check out my newer post with redesigned solution for Winter ’18
Overview
This project is inspired by Jennifer Lee who asked on Twitter how to have a Flow component in Lightning Record Page refresh and reflect changes when the record was updated. This project is also inspired by my growing interest in embedding Flows in Lightning Pages.
To my knowledge, the standard Flow component provided by Salesforce does not (yet) provide this capability. The Flow is static and doesn’t react when the underlying record has changed.
The wonderful Shawna Wolverton replied saying that the Flow needed something to tell it to refresh. In her reply she mentions Scott Kozinchik, Product Manager for Flow, and hints at future capabilities ^_^ #safeharbor
While we wait for Salesforce to roll this out as standard feature, this project demos embedding Flow (via Visualforce) in Lightning Record Page and reloading the Flow when the record is updated.
This is achieved by the magic of Lightning Data Service which allows us to know when the record on the page has changed so that we can refresh the Visualforce page that embeds the Flow. Likewise, if the Flow finishes then it may have created, updated, or deleted data too, so the Lightning Record Page is refreshed in those scenarios. Bi-directional.
You can achieve the exact same effect without this project by manually refreshing your browser page after saving the record 😉 But who has time to do such trivial things like refreshing their browser page?!?! “Not I”, said the Cat. Yeah, me neither, so let’s automate it!
Demo
Installation
You may install the unmanaged code from GitHub and make any desired adjustments.
This project includes the following:
- Flow (
Demo_Account_Flow
) – demo loading some account data - Visualforce Page (
DemoAccountFlowPage
) – embeds the flow - Lightning Component (
VisualforceRecordCmp
) – embeds the visualforce page, reloading it when the account record is updated - Lightning Record Page (
Refresh_Flow_Account_Page
) – embeds the lightning component
Getting Started
- Install this unmanaged package to your org using link above.
- Edit (or create new) a Lightning Record Page for Account object (later you can repeat this for any other objects).
- Ensure the Standard component named Record Detail is on your Lightning Page.
- Drag the Custom component named Visualforce onto your Lightning Page.
- Configure the Custom component by specifying Visualforce Page name (e.g.
DemoAccountFlowPage
).
- Save and Activate your Lightning Record Page.
- Navigate to an Account record page to test.
- Edit and save the account record and notice your Flow refreshes too!
- Click the Finish button on your Flow and notice the rest of the page refreshes too!
Customizations
The Lightning Component (VisualforceRecordCmp
) is reusable. It really should be just drag-n-drop to your Lightning Record Pages and you’re off to the races! It prompts for two parameters: the name of a Visualforce Page to embed and the height in pixels. Being as generic as it is, this simply lets you embed Visualforce Pages in your Lightning Record Pages and be able to refresh automatically when the underlying record changes.
We build on top of that functionality to load a Visualforce Page that embeds a Flow, thereby being able to have our Flow automatically refresh when the underlying record changes.
You do not have to use the included Flow (Demo_Account_Flow
), it’s just an example. You can come up with whatever Flow you want provided that is has at least one input text variable to receive the record ID from the page. Check out the DemoAccountFlowPage
as example of how to embed your Flow in your page.
Limitations
- This does not work with the standard Flow component for Lightning Pages because that component does not listen for record changes, hence this project.
- Although your Visualforce page used to embed your Flow via iframe could specify the Flow URL and get the benefit of Lightning Skin, it requires nearly 2x or more height on the page to render the screen elements. This is why my example Visualforce page embeds the Flow via <flow:interview> tag.
Technical Architecture
For those curious how this all works “under the hood”, let’s discuss the below diagram.
We have a Lightning Record Page with two Lightning Components side-by-side. Within our custom component on the left is an embedded Visualforce Page which in-turn embeds a Flow. The standard component on the right simply loads the record’s layout and allows the user to edit the fields.
To communicate from the standard component to our custom component we do so indirectly. With the Lightning Data Service our custom component subscribes to updates to the record then takes action. This decouples the left and right components entirely, which is good because we can’t do much with the standard component to get it to talk to us anyways.
When our custom component receives event that the record has been updated then it simply reloads the iframe of the Visualforce Page embedding our Flow. Our Flow runs again and re-queries fresh data. This is a good time to remind you that Flows that are embedded on Visualforce Pages or Lightning Pages should not perform any actions such as create or delete records before the first screen.
Ok, so that handles when the user updates the record using the standard component to cause our Flow to refresh.
What about the other way around, when our Flow finishes to cause the page to refresh?
When the user clicks the Finish button on your Flow you may have made some data changes, created, updated, or deleted records. To ensure the whole page still displays current and relevant data then we should refresh those components too.
A couple challenges though, when the Flow finishes the only entity that knows is the Visualforce Page, and the Visualforce Page embedded in an iframe
on a different domain than the Lightning Components cannot simply tell the window
to refresh due to browser security. If the Visualforce Page were to attempt a window.parent.reload();
then it will be met with error DOMException: Blocked a frame with origin "https://your-instance.visual.force.com" from accessing a cross-origin frame.
.
But fear not, Christophe Coenraets has taught us how to send messages from Visualforce Pages to Lightning Components. When our flow finishes then the Visualforce Page posts a message to its parent window, which is the Lightning Component. The Lightning Component then tells the other components on the page they all need to refresh.
Nice!!! I’m excited to see the limits pushed with Flow + LEX
LikeLiked by 1 person
Thanks Keith, glad you like it!
LikeLike
Great solution! The only issue I found is that because the flow is embedded in a visualforce page Salesforce uses the classic look and feel for the flow. Any suggestions on that one?
LikeLiked by 1 person
Hi Andy, glad you like the solution!
Regarding the Lightning Skin:
Although your Visualforce page used to embed your Flow via iframe could specify the Flow URL and get the benefit of Lightning Skin, it requires nearly 2x or more height on the page to render the screen elements. This is why my example Visualforce page embeds the Flow via tag.
If you want Lightning Skin then in Page Builder when configuring my custom Flow component, specify a URL to your desired Flow rather than the Visualforce page.
LikeLike
Well, actually looks like in the published version of my code I only support visualforce page names when configuring my custom component in App Builder. If you’re so inclined, you could customize the Lightning Component so that the iframe URL could support a Flow URL. If I get some free time I may try and provide that option in the app.
LikeLike
You’ve encouraged me to dig into your code and improve my understanding on how it works. 🙂
So currently we have a lightning component which contains an iframe, which contains the VF page which in turn contains the flow (with ). The benefit of is that we can specify the finish location and refresh the parent component.
If as I think you are suggesting we put the flow URL directly as the iframe target then don’t we lose the finish location and therefore the refresh?
LikeLiked by 1 person
Dig in!
The Lightning Component itself handles the refresh via Lightning Data Service. The iframe in the component determines your content. The src attribute of the iframe can be your “/flow/YourFlow?param=value” instead of “/apex/YourPage?param=value” to get Lightning Runtime for your Flow
LikeLike
Thanks, I modified the lighning component to specify the flow name directly as the src for the iframe – and the flow appears now in Lightning UI, and refreshes when I update a field on the main page. But if I update a field in using the flow the main page is no longer being refreshed. I think what is missing is the message from the VF page that the flow is finished; your listener is there, but specifically looks for that message which will now never come.
LikeLike
Womp womp
>
LikeLike
Hi Dough, Nice work.
Does your Visualforce page work in Classic or only LEX?
We are not working with LEX yet, but this work is fantastic and I’d love to incorporate it into our Account page (Classic)
Thanks,
Miguel.
LikeLiked by 1 person
Hi Miguel,
As currently developed, I believe this will only work in Lightning Experience.
Keep working on that transition and check out the below resources 🙂
Make the Move to Lightning Experience
Transition to Lightning Experience at Your Pace
LikeLike
Hi, I have been trying to get this component to work in the lightning community. I am not much of a developer, so aside from adding forceCommunity:availableForAllPageTypes, I was wondering what would need to be changed in the lightning component.
LikeLike
hi team , i am getting some issues when i am deploying into my production, Deployment CompleteTest failure, method: CeligoOpportunityLineItemTest.testAddOLI — System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, iPaas_Score_on_Oppty: execution of BeforeInsert
caused by: System.ListException: List index out of bounds: 0
Trigger.iPaas_Score_on_Oppty: line 7, column 1: [] stack Class.CeligoOpportunityLineItemTest.testAddOLI: line 66, column 1
Test failure, method: TestTaskTrigger.TestmethodTestTaskTrigger — System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out stack (postal_io)
Test failure, method: testTriggers3.leadConvTest — System.Exception: Assertion Failed: Expected: testing, Actual: null stack Class.testTriggers3.leadConvTest: line 17, column 1
LikeLike