5

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>
  • 1
    Does responseBlock rerender as expected when using the continuation? I did wonder about mapPanel being outside the apex:form, but that wouldn't explain why it fails for the continuation but not for the synchronous requestService. The other big difference is that requestService returns void. Outside continuations the commandButton action method should return void or a PageReference. – Daniel Ballinger Jun 19 '17 at 23:24
  • @DanielBallinger good point about the return value of the methods. See my updated post for the workaround. Seems like a bug on rendering apex:map component when using async – Sean Soriano Jun 22 '17 at 3:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.