I recently had the problem that I wanted to pass a variable from a Visualforce page to a Visualforce component and assign it to the component’s controller.

So far so good, no problems at all, but then I wanted the controller’s variable to be updated when the variable on the page is changed during a re-render.

The page looks like this “`xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<apex:includeScript value="{!URLFOR($Resource.res,'lib/jquery/js/jquery-1.4.2.min.js')}" />

<script type="text/javascript">
    var jq$ = jQuery.noConflict();
</script>

<apex:form >
<!-- Page block with the display options and refresh button -->
<apex:pageBlock >
    <apex:pageBlockButtons location="top">
        <apex:selectList id="cbMode" value="{!selectedMode}" size="1">
            <apex:selectOptions value="{!Modes}"/>
        </apex:selectList> 
        <apex:selectList id="cbDay" value="{!selectedDay}" size="1">
            <apex:selectOptions value="{!Days}"/>
        </apex:selectList> 
        <apex:commandButton value="Refresh" action="{!onRefresh}" rerender="mySection,pgMap" 
            oncomplete="jQuery(document).trigger('updateData');" status="status"/>
        <apex:actionstatus id="status" startText="reading...">
        </apex:actionstatus>
    </apex:pageBlockButtons> 

       <apex:panelGroup id="pgMap"> 
            <c:Map data="{!tourlist}" mode="{!selectedMode}"/>
        </apex:panelGroup>    
    </apex:pageBlockSection>   
</apex:pageBlock>   

</apex:form>

/apex:page ”`

and the component like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<apex:component controller="SCMapController" selfClosing="true" rendered="true">

    <apex:attribute description="The actual data to show on the map"
                    required="false"
                    type="MapData[]"
                    name="data"
assignTo="{!mapData}"
/>

<apex:inputHidden value="{!data}" id="tourData" />

<script>
var tourData = {!mapDataConverted};
</script>
</apex:component>

I wanted to use an assignTo to assign the attribute to a variable in the controller, convert the content to a JS object string and then read the variable in a JS script part. This works perfectly fine for the first call of the page, but after pressing the Refresh button, that re-renders the component I still have the old data on the JS side.

Some debugging revealed that the changed data isn’t passed to the controller sigh

I finally used a different approach: I assign the variable to a hidden field, which serializes the variable to something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[MapData:
   [color=4080A0, 
    email=a@b.de, 
    items=(MapDataItem:
                [GeoX=5.465409, 
                 GeoY=51.354023, 
                 infoAddress= Brakenstraat 36A 5555 CL Valkenswaard
                ], 
            MapDataItem:
                [GeoX=5.442988572135558, 
                 GeoY=51.29824967330397, 
                 infoAddress=Some data
            ]),
    name=Test]]

The basic idea is to use this string and convert it to something that can be used in JavaScript. My object is in fact a list of map data objects that contains some fields and a list of items.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Convert the serialized version (string) of an sObject to an
 * JS object. You get the serialized sObject by assigning a data
 * structure to an hidden field for example
 *
 * @param data String of the serialized sObject
 * @return JS Object of the data
 */
function sObject2JSObject(data)
{
 data = data.replace(/^\[/,'');
 data = data.replace(/\]$/,'');
 data = data.replace(/\w+:\[/g, '[')
 data = data.replace(/\[/g,'{');
 data = data.replace(/\]/g, '}');
 data = data.replace(/=\(/g, ':[');
 data = data.replace(/\)/g, ']');
 data = data.replace(/=([^,}]*)([,}])/g, "='$1'$2");
 data = data.replace(/=/g, ':');
 data = "[" + data + "]";

 var tmpData;
 eval("tmpData=" + data);

 return tmpData;
} 

The JS function sObject2JSObject() accepts the serialized string and returns the JS object created from it. The function is far from complete, but may help other as a starting point…