Strange how things go in cycles. This week there's been several
questions on the discussion boards, asking how to refresh the standard record
view page from an Visualforce page. An example of an embedded page is
shown below:
The purpose of the embedded page allows the user to choose a country ISO code
and when the save button is clicked, populate the account billing country
field with the full name of the account.
The controller method to carry out the save:
public PageReference save() { Account acc=(Account) stdCtrl.getRecord(); acc.BillingCountry=chosenCountry; return stdCtrl.save(); }
As this is an extension controller, the account is retrieved from the standard
controller to update the field. The return value is the result of the standard
controller save method, which is the standard record view page.
Unfortunately, as the Visualforce page is embedded in the standard view via an
iframe, this simply puts the full record view page inside the existing record
view page:
Not exactly what I had in mind!
Unfortunately there's no way to return a page reference to the browser that
tells it to refresh the entire page, rather than just the iframe. It is
possible, however, to use javascript to carry out a client side redirect back
to the record view. Thus there needs to be some javascript that is
conditionally rendered into the page after the save (and only after the save,
otherwise the page will continually refresh) that executes the refresh.
The full revised controller is shown below - the new aspect is the refreshPage
property, which is initialised to false in the constructor and set to true
when the record is saved. I've also changed the controller to return null, as
I need the embedded page to refresh and render the javascript.
public class EmbeddedController { public ApexPages.StandardController stdCtrl {get; set;} public String chosenCountry {get; set;} public Boolean refreshPage {get; set;} public EmbeddedController(ApexPages.standardController std) { stdCtrl=std; refreshPage=false; } public PageReference save() { Account acc=(Account) stdCtrl.getRecord(); acc.BillingCountry=chosenCountry; refreshPage=true; stdCtrl.save(); return null; } }
and the important new element in the page:
<apex:outputPanel rendered="{!refreshPage}"> <script> window.top.location='/{!Account.id}'; </script> </apex:outputPanel>
Now when I click the save button in the embedded Visualforce page, the entire
record view page refreshes and my chosen country appears in the billing
address.
Update: 22/12/2012
A number of the comments for this post have asked about achieving this via the service cloud console. This mechanism won't work I'm afraid, due to the browser same origin policy. Looking at my javascript error console in chrome, I see 'Unsafe Javascript' exceptions when the refresh method fires. I've also investigated converting the embedded page to a custom console component and using the integration toolkit to refresh the enclosing tab, but the same errors are thrown, event after whitelisting every domain involved.
Refreshing the entire record with this would that handle the validation rules also without having to put the validation handling into a controller?
ReplyDeleteDoes that make sense? I'm still learning apex so my terminology/ knowledge is limited. Thanks
The validation rule would most likely throw an exception when the record was saved. In that case the page would redirect to an error page unless the exception was caught.
ReplyDeleteI keep getting this error when I run the Controller: "Incompatible types since an instance of sObject is never an instance of Account."
ReplyDeleteIt works correctly for me - have you made any changes to the page or controller? There's various reasons why you might see this error - one is if you have a custom class called Account with is masking the Account sobject type.
ReplyDeleteNice post.
ReplyDeleteBut to raise the ante a bit, what if the page is embedded in a page layout which is shown in the Console?
The above logic throws you out of the Console and into account or case or whatever entity you are modifying.
How to get round that?
It seems you can't use the Console Toolkit to refresh VF Pages either.
/Mats
I've spent some time trying to get this to work in the service cloud console, but to no avail. I've updated the blog post to reflect this, and detail the routes that I tried.
DeleteInteresting question. I'm going to have a play around with that when I get time.
ReplyDeleteVF page refreshing a Service Cloud Console tab.
DeleteDid you get a chance to investigate this ?
My scenario is as follows :
I have Service Cloud users viewing a subtab that displays a Contact layout, which contains a VF page component.
The VF page component has inline edit enabled.
When a user does an inline edit within the VF page and presses save, I want the whole subtab to refresh.
any ideas ?
I forgot all about this if I'm honest. I will try to make time to look into this as I'd like to play around more with the console.
DeleteI've spent some time trying to get this to work in the service cloud console, but to no avail. I've updated the blog post to reflect this, and detail the routes that I tried.
DeleteYou can always use a var in the parent container object as the conduit to pass messages from the VF embedded page to itself on a refresh...
ReplyDeleteReally good solution ...
ReplyDeleteHi Bob, I used this method for a while and it's been great. I recently ran into this issue:
ReplyDeleteOn a standard page layout, using a visualforce page within, I have a droplist of email templates and a button to redirect to '/email/author/emailauthor.jsp?' with parameters. The issue is that the parent URL starts as cs9.salesforce.com(sandbox) but the visualforce page and javascript redirect runs as c.cs9.visual.force.com. So the final redirect is c.cs9.visual.force.com/email/author/emailauthor.jsp?template_id=00XA00000019aZtMAI Bug in the final page is the same errors in Chrome you saw.. except this is not a custom page, it's the email editor trying to render the contents of the iframe for it's HTML editor.
Correct URL redirect should be: cs9.salesforce.com/email/author/emailauthor.jsp?template_id=00XA00000019aZtMAI
Any reference to the baseURL from within the visualforce OR controller will always yield the visual.force.com url. And not including the baseURL will imply to use the url of the child iframe. Then you get into issues with the security of the browser and not being allowed to reference the parent URL without implementing some kind of document.domain solution.
Solution was to save a custom setting of the REAL baseURL for standard pages, and pass that into the URL to redirect to. I could have parsed the c.cs9.visual.force.com to get the instance and compile the URL too.
Anyway, thought this was a good place to share for others since I could not find another good example.
Is it possible to refresh just the inline vf instead of parent page on closing a popup?
ReplyDeleteI am getting an error, Unsupported attribute extension in .
ReplyDeleteI am new to apex, so here is my class
public class CaseExtention
{
public ApexPages.StandardController stdCtrl {get; set;}
public Boolean refreshPage {get; set;}
public CaseExtention(ApexPages.standardController std)
{
stdCtrl=std;
refreshPage=false;
}
public PageReference save()
{
refreshPage=true;
stdCtrl.save();
return null;
}
}
I reference the extension in the first line, but I get the above error. Is there something I have to do in the vf page?
This usually means you've referred to an attribute by the wrong name in the page - unfortunately you've only posted the controller so its difficult to tell what the problem might be.
DeleteGreat post Bob. Currently the page scrolls to the section of the embedded VF page when it refreshes. Is there a way you can control this to scroll to any section of choice? If not, how can one add functionality to scroll to the bottom of the page?
ReplyDeleteThanks
Thanks so much for this post! Question, this seems to cause a mini-page refresh when the command button is clicked, followed immediately by the full refresh. It's a little jarring. Are you aware of any way to stop the commandbutton from causing that first apparent refresh?
ReplyDeleteI know this question was posted 2 years ago, but I figured out a solution and wanted to provide it here in case someone else wants to fix this. Basically I created an extra outputPanel with the following javascript:
Deletefunction refreshAgmt(){
window.top.location = "{!redirectUrl}";
}
The {!redirectURL} is a string value in my class that points to the URL you want to go to. The next step is to add a rerender="NameOfOutputPanel" and oncomplete="refreshAgmt();". This essentially refreshes the outputPanel (that has nothing in it) virtually not showing any refresh on the page (in order to update the value of {!redirectURL} on the page) and then refreshes the top page to that URL.
Woops, meant to say "add a rerender="NameOfOutputPanel" and oncomplete="refreshAgmt();" to the end of your commandButton."
DeleteWe use onclick and oncomplete together in Commandbutton and we invoke refreshPrimaryTabById() method oncomplete to refresh the page in service cloud console. That works.
ReplyDeleteThis comment has been removed by the author.
DeleteHi. Could you share some more details about how you got this to work? I've had a particularly hard time retrieving the Primary Tab ID to pass into the refresh method.
DeleteThis comment has been removed by the author.
ReplyDeleteHi,
ReplyDeleteI have popped a vf page from a button of on the std case page (of console layout).
I am inserting some values from the pop up vf page to the std case page.
But to see the updated values in the cosole case page, I need to manually refresh the console.
Is there any way, can that console page be automatically refreshed when the values are inserted in to case object?
Please let me know.
Thanks,
Babu.
Nice post. its of great help....
ReplyDeleteHi Bob,
ReplyDeleteI have a requirement to Display a Visual Force Page in Custom object Page Layout On Click of Custom Detail Button.
Is it Possible? If yes, Could You Please Guide me
Thanks in Advance!!
Thanks alot, you have saved my day.
ReplyDeleteThank you a lot. Works perfectly. You put a smile on ma head !
ReplyDeleteI have a inline vf page on parent record. Page shows list of child items which can be edited and save button. I am able to update entire page on update. But it gives problem when custom validation rule on child record makes the update from inline vf page fail. The child record does not get updated and we do not get any error message. Can you please guide on my issue.
ReplyDeleteGreat, thanks Bob, was able to apply this to a custom/quick action in the chatter feed on opportunity records.
ReplyDeleteNow to get it into production.
Bob! It's now late 2017 and I'm still using this blog post. Magic! Thanks, thanks, thanks.
ReplyDeleteMy use case is very similar to your example, except I've built a replacement related list VF component on a master/detail relationship. The detail needs to update a field on the master... and the detail VF component on the page switches between view & edit modes. Your solution is simple and (somewhat) clean.
https://developer.salesforce.com/forums/ForumsMain?id=9060G000000MUYqQAO