Skip to content

Instantly share code, notes, and snippets.

@paulroth3d
Last active April 27, 2022 07:17
Show Gist options
  • Save paulroth3d/0ffc5d0fb74f611aeae6df071ef2a3e0 to your computer and use it in GitHub Desktop.
Save paulroth3d/0ffc5d0fb74f611aeae6df071ef2a3e0 to your computer and use it in GitHub Desktop.
Lightning Safe Navigation

Overview

If we are navigating within Salesforce, we want to make sure that it will work correctly - regardless of whether we are on mobile or desktop or in console or even console layouts.

LightningSafeNavigation.navigate is meant to be a function to send more than just I want to go someplace - but to behave safely and correctly in any context.

It can be leveraged in Lightning Components (LWC or Aura) or VisualForce pages or even in lightning out - safely.

Example Problem call (from classic)

window.location.href = '/apex/ltng_PostMessageResources?info=none';

This will not work, because lightning experience requires the lightning events.

So using this library we can do this instead:

LightningSafeNavigation.navigate('/apex/ltng_PostMessageResources?info=none');

Example call:

<apex:includeScript value="{!URLFOR($Resource.ltng_PostMessageResources, 'js/navigation/LightningSafeNavigation.js')}" />

within javascript:

LightningSafeNavigation.navigate('{!$Page.MyPage}?Opportunity__c={!Opportunity.Opportunity_ID_Text__c}&retURL=/{!Opportunity.id}');

within visualforce:

<apex:commandButton value="Open Popup" onclick="LightningSafeNavigation.navigate('{!$Page.MyPage}?Opportunity__c={!Opportunity.Opportunity_ID_Text__c}&retURL=/{!Opportunity.id}');" rendered="{!showMoveNewButton}" reRender="formid" />

or with more info, like the tab name

<apex:commandButton value="Open Popup" onclick="LightningSafeNavigation.navigate({url:'{!$Page.MyPage}?Opportunity__c={!Opportunity.Opportunity_ID_Text__c}&retURL=/{!Opportunity.id}', label:'Quote'});" rendered="{!showMoveNewButton}" reRender="formid" />

This library has a number of fallbacks to use the best available context to navigate. Such as the lightning:workspaceAPI - for console navigation if available, sforce.one navigation if available, Lightning Navigation through $A if available, and others.

How to Include

Can be referenced from within Visualforce by:

<apex:includeScript
	value="{!URLFOR($Resource.ltng_PostMessageResources, 'js/navigation/LightningSafeNavigation.js')}"
/>
...
<apex:commandButton value="Test Navigation"
	onclick="LightningSafeNavigation.navigate({url:'{!$Page.TestPage}?Opportunity__c={!Opportunity.Id}', label:'Quote'});"
/>

Or within a Lightning Component through:

<ltng:require scripts="{! join(',',
		$Resource.ltng_PostMessageResources+'/js/navigation/LightningSafeNavigation.js'
	)}"
	afterScriptsLoaded='{!c.handleScriptsLoaded}'
/>
...
({
	navigateSomewhere : function(component, helper) {
		LightningSafeNavigation.navigate({ url:'...', label:'External System' });
	}
})

Parameters

Parameter Type Description Example
navigationInfo String address or Object is accepted Either the address to navigate to or an object with the structure below 'https://www.google.com' - assumes this is the object.url to go to, or {url:'https://www.google.com'}

Object interface

If the parameter is an object, these are the parameters expected

Property Required Type Description Example
url true String The url to navigate to '/apex/somePage'
tab false string='redirect' How to navigate, either to navigate as redirect|blank - new window|primary - new primary tab|secondary - new secondary tab 'blank'
name false string the optional name of the tab - so it can be later found by name 'opportunityQuote'
label false string the optional label of the tab 'My Quotes'
callback false function the optional callback on completion. See various sforce.one / lightning event callbacks for examples function(response){console.log('tab id:' + response.id + ' finally opened')}
active false boolean=true Whether the new tab should be active and the user navigates there, or false and the tab is created but not open.
id false string The id to assign to the window/tab when opening (if available)
workspace false lightning:workspaceAPI instance If using console navigation, pass the lightning:workspace along so it can be used during navigation. See here for more info: https://developer.salesforce.com/docs/component-library/bundle/lightning:workspaceAPI/documentation component.find('workspace');
/*global sforce console $A window */
/*eslint no-console: "off"*/
/**
* Utility Scripts for common navigation patterns.
**/
this.LightningSafeNavigation = {};
/**
* Safely navigates to a target URL regardless if in mobile or lightning
* or just plain visualforce.
* @param navigationInfo (string|object) - the target url we want to go to or object with other properties.
* @param navigationInfo.url (string) - if an object is sent, the url is required - the address to redirect to
* @param navigationInfo.tab (string?=redirect) - (redirect - to navigate as redirect|blank - new window|primary - new primary tab|secondary - new secondary tab)
* @param navigationInfo.name (string?) - the optional name of the tab - so it can be later found by name
* @param navigationInfo.callback (function?) - the optional callback on completion
* @param navigationInfo.label (string) - the optional label of the tab
* @param navigationInfo.active (boolean?=true) - whether to navigate to the tab right away
* @param navigationInfo.id (string?) - the tab id to try to set for the new tab
* @param navigationInfo.workspace (object?) - the instance of component.find('workspace') of type lightning:workspaceAPI
**/
this.LightningSafeNavigation.navigate = function(navigationInfo) {
//-- safe object.assign
var navigationObject = {
tab: 'redirect',
active: true,
id: null,
url: null,
label: null,
name: null,
callback: null
};
if (typeof navigationInfo === 'string') {
navigationObject.url = '' + navigationInfo;
}
for (var key in navigationInfo) {
if (navigationInfo.hasOwnProperty(key)) {
navigationObject[key] = navigationInfo[key];
}
}
//-- check if there is a workspace sent
var workspace = navigationInfo.workspace ? navigationInfo.workspace : null;
//-- require at least the url
if (!navigationObject.url) {
throw(new Error('navigation.url is required'));
}
if (workspace) {
//-- we have a lightning workspace
//-- https://developer.salesforce.com/docs/atlas.en-us.api_console.meta/api_console/sforce_api_console_methods_lightning_tabs.htm
workspace.getFocusedTabInfo()
.then( function(response) {
var focusedTabId = response.tabId;
if (navigationObject.tab==='primary') {
workspace.openTab({
focus: navigationObject.active,
url: navigationObject.url
});
} else {
workspace.openSubtab({
parentTabId: focusedTabId,
focus: navigationObject.active,
url: navigationObject.url
});
}
})
.catch( function(err) {
console.error('error in finding focused tab:' + JSON.stringify(err));
console.error(err);
});
} else if (typeof sforce != "undefined" ) {
if (typeof sforce.one != "undefined") {
//-- use standard navigation
//-- see https://developer.salesforce.com/docs/atlas.en-us.salesforce1.meta/salesforce1/salesforce1_dev_jsapi_sforce_one.htm
var isRedirect = navigationObject.tab === 'redirect';
sforce.one.navigateToURL(navigationObject.url, isRedirect);
} else if (typeof sforce.console != "undefined") {
//-- use the console navigation
//-- see https://developer.salesforce.com/docs/atlas.en-us.api_console.meta/api_console/sforce_api_console_opensubtab.htm
sforce.console.getEnclosingPrimaryTabId(
function(result) {
var currentPrimaryTab = result.id;
if (navigationObject.tab==='primary') {
sforce.console.openPrimaryTab(navigationObject.id, navigationObject.url, navigationObject.active, navigationObject.label, navigationObject.callback, navigationObject.name);
} else {
sforce.console.openSubtab(currentPrimaryTab, navigationObject.url, navigationObject.active, navigationObject.label, navigationObject.id, navigationObject.callback, navigationObject.name);
}
}
);
} else {
throw(new Error('neither sforce.one or sforce.console defined'));
}
} else if (typeof $A != "undefined" && typeof $A.get != "undefined") {
//-- see https://developer.salesforce.com/docs/component-library/bundle/force:navigateToURL/documentation
$A.get("e.force:navigateToURL")
.setParams({
"url": navigationObject.url
}).fire();
} else {
window.location.href = navigationObject.url;
}
}
@harshadkumbhar-kpmg
Copy link

Hello this is amazing for navigation purpose. I have one query is it possible to navigate one LWC to another LWC using this js. currently I am doing this using Aura component but it opening in new browser tab but we need that in console tab.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment