Convert serialized Apex Object to Javascript Object
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
<apex:page controller="PageController">
<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
<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:
[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.
/**
* 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…