Inspired by Brian Kwong‘s creative blog post on “URL Hacking” with supported features in Lightning Experience using a mix of Flow URLs and Lightning Components, I wanted to see if I could solve the problem he posed at the end about providing a reusable solution.
In Brian’s approach, he cleverly used a flow’s URL in a custom button so that he could have a URL to pass parameters to just like the “URL Hacking” days in Classic experience to open a new record form with pre-populated field values. His flow then used a new feature that allows developers to surface components in flow screens by implementing the lighting:availableForFlowScreens interface. His component then fired the force:recordCreate event to pre-populate a new form with default field values passed in from the flow’s URL. Wowza!
URL >> Flow >> Component >> Record Create Form
One of the challenges with variables in flows is that we are limited on the supported data types. Maps, which in Apex and JavaScript are convenient ways to pass a dynamic number and type of variables, is not supported, so by using flow we need to concretely know the number and type of input variables we can pass to the flow and we need to concretely know the number and type of attributes on the Lightning Component we want to pass those flow variables to. Double whammy here by tightly coupling the flow and component to provide one customizable URL. Not very scalable, which Brian points out at the end of his blog post.
Please vote for these ideas to add support for more data types in flows:
Looking for ways to get around the data type limitations of flows, I began thinking of others Salesforce supported ways to create URLs to pass parameters in Lightning Experience. That’s when I remembered a new interface introduced in Summer ’18 lightning:isUrlAddressable. Components that implement this interface are able to be navigated to in Lightning Experience via a URL and they accept an arbitrary number of URL parameters. Bingo!
I started an open source project to build out a proof of concept and I’m quite pleased with how it turned out. I was able to remove the flow from the equation and exclusively use Lightning Components.
URL >> Component >> Record Create Form
However, although I solved one problem (remove data type limitations introduced by using flows), I’ve encountered a new obstacle to the overall user-experience.
By navigating to this lightning component as a URL, you are navigating away from the original page you were on when you clicked the button. The problem with that is there is no way (at the time of this writing) to control what happens when the user clicks the cancel or save buttons in the modal dialog. Meaning, there is no way to return the user back to where they were in my approach. Clicking save will navigate you to the new record, that’s acceptable, but clicking cancel will leave the user on a page that only has the top navigation tabs but no body content. Womp, womp…
Please vote for these ideas for
force:recordCreate
event to expose callbacks to customize the modal dialog button behavior:
Update August 7, 2018
Inspired by reader Suraj Pillai, who suggested doing some navigation trickery asynchronously, I added support for loading the current record in the background while the modal to create a new record appears to the user. By the time the user decides they want to cancel the form, the original record they were working with will have loaded as if the user had never navigated away to begin with.
This record loading is optional in my solution. To use it, add the URL parameter recordId and set the value to the current record being viewed. For example, recordId={!Account.Id}. If you do not specify a recordId parameter, then when the user cancels the form, they will see a blank page like the above screen shot.
Here’s using the recordId parameter in action, note that when clicking the cancel button, the user still is contextually on the original record. Win!
If you have any ideas or solutions to improving in Lightning Experience how to use URLs to pre-populate forms in a dynamic and scalable manner, please let me know in the comments below.
Thanks!
Doug, Thanks for the use case. learnt a few good things around (y)
LikeLiked by 1 person
Glad you liked it, Praveen!
LikeLike
I think I may have a solution, although not very elegant. Accept another urlparam called, let’s say, ‘actiononcancel’ where the user is expected to provide the url of the page they wish to navigate to on cancel. Delay firing the ‘createrecord’ event using ‘setTimeout’ by a couple of seconds and redirect the user to the ‘actiononcancel’ url using ‘force:navigateToURL’ immediately after that line. The user is redirected to the url first and then the ‘force:createRecord’ event is fired so when the user cancels, they stay on the same page.
Something like this:
setTimeout(()=>{$A.get( ‘e.force:createRecord’ ).setParams( eventParamMap ).fire()},2000);
if(urlParamMap.actiononcancel){
helper.navigateToUrl(urlParamMap.actiononcancel);
}
LikeLiked by 1 person
Hi Suraj, thanks for the idea. Assuming the URL they navigate to is in Lightning Experience, then this might work. If they navigate elsewhere then not sure our timeout code will fire. But I’ll try it out. Good to document the workarounds until supported option comes.
LikeLike
You’re right. This would work only with internal urls
LikeLiked by 1 person
Welcome and glad I could help. And thank you for this awesome idea!
LikeLiked by 1 person
Hi Suraj, thanks again for the idea! I’ve updated the end of my blog post with a modified version of using asynchronous behavior to load the cancel action content. Let me know your thoughts =)
LikeLiked by 1 person
Thanks Doug. I like how you used ‘recordid’ instead of a generic url for the cancel action to keep things internal and called ‘navigateToUrl’ before enqueuing the action to replicate the same behavior as setTimeout. The only thing I can think of is if the record create modal were to come up before the navigation action was completed, it would disappear on moving away from the page. And since we the standard enqueueAction does not allow us to specify the timeout, we wouldn’t be able to change the behavior. Not sure how much of a concern that is in practice, though. Again, really good work and appreciate all your contributions to the community!
LikeLiked by 1 person
Hey Doug,
great post. I’ve been waiting for a URL replacement in Lightning for a while.
Now, where can we see the Lightning Component markup and its controller for the given example?
/lightning/cmp/c__URL_CreateRecordCmp?recordId={!Account.Id}&objectName=Contact&FirstName=Doug&LastName=Ayers
Apologies for being thick.
Thanks
Martin
LikeLiked by 1 person
Hi Martin, please see my open source project: https://github.com/douglascayers/sfdx-record-create-url-component
Thanks
LikeLike
Yeah, found it now and managed to get it work as well. So far so good. Will let you know if I discover any issues with it.
LikeLiked by 1 person
This is by far the best alternative to providing data via URL params in Lightning. Thank you for putting it together.
I was not able to get the background record to load, without replacing the Create New form in the foreground. The minute I remove the background record id, create new works as expected. Any idea what I am missing there?
LikeLiked by 1 person
Hi Stefan,
Thanks for the kind feedback, glad you like this solution!
Hrmm.. I’m not sure I follow on what you mean by replacing or removing things in the background/foreground. Do you mean that if you use the recordId URL parameter then it doesn’t work for you?
This is still a bit of a hack, so it’s possible this approach won’t work for everyone.
LikeLike
Yes, when the recordId is provided, the new record popup disappears. As you said, it may not work in all environments.
I am hopeful SF will extend the event handling on force:recodCreate, or allow providing default values on the lightning data service components.
LikeLiked by 1 person
Doug, thanks for this, it looks great. If I wanted the Save action to save the record and then display the same page as the Cancel action does, what modifications would be needed?
LikeLiked by 1 person
Hi Stephen,
To my knowledge, the standard save/cancel dialog buttons are not customizable yet. On save, the page will be redirected to the new record. On cancel, the dialog simply closes, and that is why I employ a trick to load the original record’s page behind the scenes so when the dialog closes the user thinks they stayed on the original page.
Make sure to vote up the ideas mentioned in the blog =)
Thanks
LikeLike
Hey doug i have one workaround
$A.get( ‘e.force:createRecord’ ).setParams(
“panelonDestroyCallBack”:function(cmp){
}
By this method you can control save and cancel behaviour i did it for one of my requirement in the migration of Javascript List buttons
LikeLiked by 1 person
Hi Shubham. Do you have specific examples of how you were able to handle save (capture new record id, and stop the redirect event to the new record)?
LikeLiked by 1 person
Yes steafan give me your email id i will mail u a poc
LikeLike
Interesting, but this is stretching into hacking on internal Salesforce functions which may change or be required for correct operation. Regardless, how do you detect which button was clicked, cancel or save or simply closing the modal? By the name of the function I think this is called anytime the modal is being dismissed for any reason.
LikeLike
Doug!!! if you are clicking on cancel it will redirect you to new record page at that time you will not have any record id so like that i override cancel button and for save you will get the record id so like that you can override save, if you want code so i can give you for understanding……
LikeLike
Shubham,
Can you please provide us the code for this?
Thanks.
LikeLike
Hi Subham,
can you please provide the code to capture record id?
LikeLike
Hi Doug,
Is it possible to use Record Type name instead of a RecordTypeID?
Thanks!
LikeLike
One issue is that save & new does not function with this redirect.
LikeLike
Hey Doug, Enjoying the full scope of your post and advice, 🙂
On URL report, how to manage with filter report. works on Classic but LEX Lightning, Link works but does not filter it the following {!Communities__c.Name}&pv1={!Communities__c.Name}&pv2={!Communities__c.Name} on our report
Example below:
/00OD0000006TaGY?pv0={!Communities__c.Name}&pv1={!Communities__c.Name}&pv2={!Communities__c.Name}
LikeLiked by 1 person
Hi Christophe,
Thanks for the kind feedback. Regarding URLs to reports, I haven’t done much with that in Lightning Experience. You might have some luck exploring this help article.
LikeLike
Hi Doug, Thanks very much for assistance,
I will look at the Help Article.
LikeLiked by 1 person
This is really good! Thanks Doug!
How would you pre populate a date (e.g. Today)?
I’m assuming modifications to the controller?
LikeLiked by 1 person
Hi Adam,
You’re very welcome, glad you like it.
To pre-populate a date field is like pre-populating any other field via the URL. Specify the API Field Name = Some Value where the value is formatted as YYYY-MM-DD where:
YYYY is the four digit year (e.g. 2018)
MM is the one or two digit month (e.g. 4 or 12)
DD is the one or two digit day of the month (e.g. 7 or 28)
For example, to specify an opportunity’s close date to July 12th, 2020 then the URL might look like this:
/lightning/cmp/c__URL_CreateRecordCmp?objectName=Opportunity&recordId={!Account.Id}&CloseDate=2020-07-12
.To specify the opportunity’s close date to “today” then we can’t use {!TODAY()} function alone because that doesn’t format as YYYY-MM-DD, so our URL might look like this:
/lightning/cmp/c__URL_CreateRecordCmp?objectName=Opportunity&recordId={!Account.Id}&CloseDate={!YEAR(TODAY())}-{!MONTH(TODAY())}-{!DAY(TODAY())}
LikeLike
Can it be used opportunity product with passing parameters for product filtering?
LikeLike