It seems like there's a bug rerendering an <apex:map>
from asynchronous callout.
On my sample page below I have two <apex:commandbutton>
one executing an asynchronous callout(action="{!requestServiceAsync}"
) and the other one a synchronous callout(action="{!requestService}"
) and both rerenders the mapPanel
which contains an <apex:map>
.
If I click the button that executes the asynchronous callout, it doesn't render the map when it finishes. While it renders properly when the synchronous callout is executed.
Am I missing something on how asynchronous callout renders an <apex:map>
component or is this a bug?
Sample page below:
modified page from:(http://www.jitendrazaa.com/blog/salesforce/continuation-object-in-apex-asynchronous-callouts-for-long-running-request-live-demo/)
VF PAGE
<apex:page docType="html-5.0" showHeader="false" sidebar="false" controller="ISOtoCodeService">
<apex:form >
<apex:pageBlock title="Continuation Demo">
Country ISO :
<apex:input label="Country ISO" value="{!countryISO}"/>
<apex:commandButton action="{!requestServiceAsync}" value="Request Service Async" reRender="responseBlock, mapPanel"/>
<apex:commandButton action="{!requestService}" value="Request Service" reRender="responseBlock, mapPanel"/>
</apex:pageBlock>
<apex:pageBlock title="Response from Webservice" id="responseBlock">
<pre> {!response} </pre>
</apex:pageBlock>
</apex:form>
<style type="text/css">
pre{
font-size : 1.7em;
}
</style>
<apex:outputPanel id="mapPanel">
<apex:map rendered="true"
scrollBasedZooming="false"
width="80%"
height="200px"
mapType="satellite"
zoomLevel="18"
center="1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA">
<apex:mapMarker position="1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA"
title="1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA"/>
</apex:map>
</apex:outputPanel>
Page Controller
public class ISOtoCodeService {
public String countryISO {get;set;}
public String response {get;set;}
private String baseSericeURL = 'http://services.groupkt.com/country/get/iso2code/';
private String returnedContinuationId ;
public ISOtoCodeService()
{
countryISO = 'IN';
}
public Object requestServiceAsync(){
//Timeout in seconds, 60 is limit
Continuation con = new Continuation(60);
// Set callback method
con.continuationMethod='renderResponse';
// Create callout request
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint(baseSericeURL+countryISO);
returnedContinuationId = con.addHttpRequest(req);
return con;
}
public Object renderResponse() {
// Get the response by using the unique label
HttpResponse httpRes = Continuation.getResponse(returnedContinuationId);
// Set the result variable that is displayed on the Visualforce page
response = httpRes.getBody();
// Return null to re-render the original Visualforce page
return null;
}
public void requestService() {
// Create callout request
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint(baseSericeURL+countryISO);
// Get the response by using the unique label
Http http = new Http();
HTTPResponse res = http.send(req);
// Set the result variable that is displayed on the Visualforce page
response = res.getBody();
}
}
UPDATE: 22/06/2017---------------------
Thanks to @Daniel on his comment below for pointing out the difference on the return type of the async/sync methods. It seemed to have worked using a separate <apex:actionfunction>
to render the panels.
I used the renderPage
<apex:actionfunction>
that renders the specified panels and then called it on oncomplete attribute of the async commandbutton.
I still don't get why the <apex:map>
doesn't rerender properly from a continuation request because I've also tried putting the {!response}
inside the mapPanel and it rendered properly using the async command button. Which means this might be a bug that is only specific for <apex:map>
component
UPDATED VF PAGE
<apex:page docType="html-5.0" showHeader="false" sidebar="false" controller="ISOtoCodeService">
<apex:form >
<apex:pageBlock title="Continuation Demo">
Country ISO :
<apex:input label="Country ISO" value="{!countryISO}"/>
<apex:commandButton action="{!requestServiceAsync}" value="Request Service Async" oncomplete="renderPage();"/>
<apex:commandButton action="{!requestService}" value="Request Service" reRender="responseBlock, mapPanel"/>
<!-- oncomplete="renderPageX();" -->
<apex:actionFunction name="renderPage" reRender="responseBlock,mapPanel"/>
</apex:pageBlock>
<apex:pageBlock title="Response from Webservice" id="responseBlock">
<pre> {!response} </pre>
</apex:pageBlock>
<apex:outputPanel id="mapPanel">
<apex:map rendered="true"
scrollBasedZooming="false"
width="80%"
height="200px"
mapType="satellite"
zoomLevel="18"
center="1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA">
<apex:mapMarker position="1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA"
title="1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA"/>
</apex:map>
</apex:outputPanel>
</apex:form>
<style type="text/css">
pre{
font-size : 1.7em;
}
</style>
</apex:page>
responseBlock
rerender as expected when using the continuation? I did wonder aboutmapPanel
being outside theapex:form
, but that wouldn't explain why it fails for the continuation but not for the synchronous requestService. The other big difference is thatrequestService
returns void. Outside continuations the commandButton action method should return void or a PageReference.apex:map
component when using async