Embed a Flex Slider in a Visualforce Page

I did a simple Flex callback with JavaScript about a year ago and I always wanted to follow up with this topic. Somehow I never quite found the time. This is an example of how you can create a Flex component (a horizontal slider in this case), wrap it up as a Visualforce component and then make it reusable in your Visualforce pages. This technique provides you with some great ways to add "eye candy" to your Visualforce pages without breaking the UI model.

You can run this example on my Developer Site.

FlexSlider

The Flex component is simply a wrapper around the standard HSlider control. It expects some setup values to be passed into it and then when the users moves the slider, it makes a callback to a JavaScript function in the Visualforce page to update the value in the record. It's loosely coupled so that you can pass in the values, the name of the JavaScript function to call and the ID of the DOM property to update.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute" alpha="1.0" backgroundGradientAlphas="[0,0,0,0]"
 creationComplete="init()">

<mx:Script>
 <![CDATA[
  import flash.external.ExternalInterface;
  import mx.events.SliderEvent;

  // bind the variables so that can be notified of value updates
  [Bindable] private var slideInterval:int;
  [Bindable] private var minSliderValue:int;
  [Bindable] private var maxSliderValue:int;
  [Bindable] private var sliderLabels:Array = new Array();
  [Bindable] private var sliderTickValues:Array = new Array();
  [Bindable] private var callbackFunction:String;
  private var startSliderValue:int;
  private var boundDomId:String;

  // method to be called immediately after component is created
  private function init():void {

   // set some values passed in from the Visualforce page
   startSliderValue = this.parameters.startSliderValue;
   minSliderValue = this.parameters.minSliderValue;
   maxSliderValue = this.parameters.maxSliderValue;
   slideInterval = this.parameters.slideInterval;
   // add the min & max as the slider labels
   sliderLabels.push(minSliderValue,maxSliderValue);
   // add the min & max as the slider values
   sliderTickValues.push(minSliderValue,maxSliderValue);

   // set name of the callback javascript function
   callbackFunction = this.parameters.callbackFunction;
   // set the id of the DOM element attached to the slider so we can reference it
   boundDomId = this.parameters.boundDomId;

   // set the values initially for the component
   mySlider.tickValues = sliderTickValues;
   mySlider.labels = sliderLabels;
   mySlider.value = startSliderValue;

   // set the background color of the flex component so it matches the page
   Application.application.setStyle("backgroundColor",this.parameters.bgColor ? this.parameters.bgColor : "#F3F3EC");

  }

  // notify the external interface that the slider was changed
  public function handleSliderChange(evt:SliderEvent):void {
   ExternalInterface.call(this.callbackFunction,evt.currentTarget.value,this.boundDomId);
  }
 ]]>
</mx:Script>

<mx:HSlider
 id="mySlider"
 minimum="{minSliderValue}"
 maximum="{maxSliderValue}"
 snapInterval="{slideInterval}"
 tickValues="{sliderTickValues}"
 labels="{sliderLabels}"
 allowTrackClick="false"
 liveDragging="false"
 change="handleSliderChange(event)"/>

</mx:Application>

So that I can reuse slider, I created a Visualforce component with it that can be embedded into other Visualforce pages. I've included a default JavaScript function but you can override it with your own in the embedded Visualforce page if needed. There are a number of attributes included which make for handy syntax coding in the browser and Eclipse.

<apex:component >

  <script language="JavaScript" type="text/javascript">
  function updateHiddenValue(value, eId){
  var e = document.getElementById(eId);
  e.value = value;
  }
  </script>

  <apex:attribute name="minSliderValue" description="Minimum slider value" type="Integer" required="true"/>
  <apex:attribute name="maxSliderValue" description="Maximum slider value" type="Integer" required="true"/>
  <apex:attribute name="startSliderValue" description="Starting value of the slider" type="Integer" required="false" />
  <apex:attribute name="slideInterval" description="The tick interval that the slider can be moved" type="Integer" default="1" required="false" />
  <apex:attribute name="callbackFunction" description="The name of the JavaScript function that is called by the Flex components and passes the bound value of the slider. By default the component uses updateHiddenField but you can override it with your own." type="String" default="updateHiddenValue" required="false" />
  <apex:attribute name="boundDomId" description="The $Component id of the DOM element bound to the slider value's. The slider's change event passes the value of boundDomId back to the JavaScript function." type="String" required="false"/>
  <apex:attribute name="height" description="The height of the slider" type="Integer" default="50" required="false" />
  <apex:attribute name="width" description="The width of the slider" type="Integer" default="200" required="false" />
  <apex:attribute name="bgColor" description="The background color of the flex component so it matches the page. Defaults to Salesforce gray." type="String" default="#F3F3EC" required="false" />

  <apex:flash src="{!$Resource.FlexSlider}"
  height="{!height}"
  width="{!width}"
  flashVars="minSliderValue={!minSliderValue}&maxSliderValue={!maxSliderValue}
    &startSliderValue={!startSliderValue}&slideInterval={!slideInterval}
    &callbackFunction={!callbackFunction}&boundDomId={!boundDomId}&bgColor={!bgColor}" />

</apex:component>

So now that you have your Flex slider built and uploaded as a Static Resource you can build your Visualforce page using your new component. If you want to pass more parameters into the component from your Visualforce page, you add them to the FlexSliderComponent attributes based up what is available from the component itself.

So now when you move the slider in the Flex component, it makes a callback to your Visualforce page to a JavaScript function which updates the value of the "geek factor" field in the record. When you save the record, that value is update.

When using the demo link above, after submitting the form you will see a white page. This is because I am lazy. You'll have to go back and reload the form to see the value change.

<apex:page standardController="Person__c">

 <apex:sectionHeader title="{!Person__c.First_Name__c} {!Person__c.Last_Name__c}" subtitle="Flex Slider Example"/>

 <apex:form id="myForm">
  <apex:pageBlock title="Geek Rating" id="myBlock" mode="edit">
  <apex:pageBlockButtons >
    <apex:commandButton value="Save" action="{!save}"/>
    <apex:commandButton value="Cancel" action="{!cancel}" />
  </apex:pageBlockButtons>
  <apex:pageMessages />

  <apex:pageBlockSection id="ratings" showHeader="true" title="Contact Info" columns="2">

    <apex:inputField value="{!Person__c.First_Name__c}"/>
    <apex:pageBlockSectionItem >
    <apex:outputLabel value="Geek Factor Slider"/>
    <c:FlexSliderComponent minSliderValue="0" maxSliderValue="10"
       startSliderValue="{!Person__c.Geek_Factor__c}"
       boundDomId="{!$Component.ratings.geekfactor}" />
    </apex:pageBlockSectionItem>

    <apex:inputField value="{!Person__c.Last_Name__c}"/>
    <apex:inputField id="geekfactor" value="{!Person__c.Geek_Factor__c}" />

  </apex:pageBlockSection>
  </apex:pageBlock>
 </apex:form>

</apex:page>