Load a Visualforce Page with Parameters in Lightning

I was looking to display Visualforce pages using the RecordId inside of my Lightning Record pages. After finding a very elegant solution to pass the RecordId from a Lightning Component container to a Visualforce page displayed in an iframe I decided it was time to move the solution from the Sandbox into Production.
What worked perfectly in the Sandbox gave me nothing but a blank component in Production. After spending way too much time trying to find a solution, I gave up and went to Plan B. (edit: my other component works once I adjusted my clickjack settings)


Plan B
I was able to use a Lightning Component to navigate to a URL, so I built a component to build the URL for the Visualforce page including the parameter for the RecordId of the page.
doInit : function(component, event, helper) {
var params = new Array();
params.push(component.get('v.recordIdVar') + '=' + component.get('v.recordId'));
params.push(component.get('v.otherParams'));
component.set('v.queryString', params.join('&'))
},
openPage : function(component, event, helper) {
// Show spinner once button is pressed
var spinner = component.find('spinner');
$A.util.removeClass(spinner, 'slds-hide');
// Build url for Visualforce page
var vfURL = 'https://' + component.get("v.domain") + '--c.visualforce.com/apex/';
vfURL = vfURL + component.get("v.pageName") + '?';
vfURL = vfURL + component.get("v.queryString");
console.log("VF URL: ",vfURL);
On a standard Lightning page, the component switches from the Record page to the Visualforce page, returning to the Record page when the Visualforce page exits.
// Handle non-console user
console.log("Not in Console");
var urlEvent = $A.get("e.force:navigateToURL");
urlEvent.setParams({
"url": vfURL
});
urlEvent.fire();
It is a bit different on a Console page where the Visualforce Page is loaded in a new Tab which reverts to the Record page Tab when exiting. This normally leaves a second “dead” Record page Tab behind so I made sure the component saved the ID of the Calling Tab or Subtab and closed the “dead” tab when finishing with the Visualforce page.
// Check to see if running in a Console
var workspaceAPI = component.find("workspace");
workspaceAPI.isConsoleNavigation().then(function(consoleResponse) {
console.log("IsConsole: ", consoleResponse);
if (consoleResponse) {
// Save current tab info
workspaceAPI.getFocusedTabInfo().then(function(tabResponse) {
var closeTabId = tabResponse.tabId;
var closeTitle = tabResponse.title;
var parentTabId = tabResponse.parentTabId;
var isSubtab = tabResponse.isSubtab;
console.log("Current Tab: ", closeTabId + " | " + closeTitle);
console.log("Is Sub: ",isSubtab," ParentId: ",parentTabId);
// Open Visualforce Page in a new tab
if (isSubtab) {
workspaceAPI.openSubtab({
parentTabId: parentTabId,
url: vfURL,
focus: true
}).then(function(openSubResponse) {
console.log("New SubTab Id: ", openSubResponse);
})
.catch(function(error) {
console.log(error);
});
} else {
workspaceAPI.openTab({
url: vfURL,
focus: true
}).then(function(openParResponse) {
console.log("New ParentTab Id: ", openParResponse);
})
.catch(function(error) {
console.log(error);
});
}
// Because exiting the VF page will reopen the object record,
// close the tab we started on
if (tabResponse.closeable && !tabResponse.pinned) {
workspaceAPI.closeTab({
tabId: closeTabId
}).then(function(closeResponse) {
console.log("Closed: ", closeTitle);
})
.catch(function(error) {
console.log(error);
});
} else {
console.log("Left Open: ", tabResponse.title);
}
})
.catch(function(error) {
console.log(error);
})
Rather than load the Visualforce page immediately, I display a button. On the button press, the component loads the Visualforce page.
<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global">
<lightning:workspaceAPI aura:id="workspace" />
<aura:attribute name="buttonLabel" type="String" />
<aura:attribute name="buttonVariant" type="String" default="neutral" />
<aura:attribute name="domain" type="String" />
<aura:attribute name="pageName" type="String" />
<aura:attribute name="recordIdVar" type="String" />
<aura:attribute name="otherParams" type="String" />
<aura:attribute name="queryString" type="String" />
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<lightning:card>
<lightning:layoutItem padding="around-small">
<lightning:button label="{!v.buttonLabel}" variant="{!v.buttonVariant}" onclick="{!c.openPage}" />
<lightning:spinner aura:id="spinner" alternativeText="Loading" variant="brand" size="large" class="slds-hide" />
</lightning:layoutItem>
</lightning:card>
</aura:component>
I’ve made the component as generic as possible by providing the following attributes one can use when placing the component on their Lightning Record page.
- Button Label (Text to appear on the Button)
- Button Variant (Button styling: base, neutral, brand, destructive, success)
- Domain Name (Your Production or Sandbox domain name)
- Visualforce Page Name (The API Name of the Visualforce Page)
- RecordId Parameter Name (The name of the URL parameter for the RecordId)
- Additional URL Parameters (Any other URL parameters & values)
Here’s the Component in Action
