[SalesForce] Live Agent Pre Chat Form – Use Custom Case Picklist Fields

I am trying to make a Live Agent Pre Chat Form inside our Salesforce Customer Community using visualforce. I've tried out a lot of approaches and cannot figure out how to use custom picklist fields (including dependent picklists) and get them to appear in the Service Cloud Console or map into the case that is created. Has anyone done this before?

Here is my in progress code:

Visualforce Page:

<script type='text/javascript'>
    (function() {
        function handlePageLoad(){
        var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)");
        document.getElementById('prechatForm').setAttribute('action',
        decodeURIComponent(endpointMatcher.exec(document.location.search)[1]));
        } 
        if (window.addEventListener){
            window.addEventListener('load', handlePageLoad, false);
        } 
        else {window.attachEvent('onload', handlePageLoad, false);}
    })();
</script>

<img src="{!$Resource.Live_Agent_Logo}"/><p/>
<apex:outputText style="font-weight:bold;font-size:14px;color:#595959;padding:0 10px;" value="Chat with Technical Support"/><p/>
<div id="chatFormDiv">
<!-- Get chat visitor info and set for Live Agent -->
<form method='post' id='prechatForm'>
    <apex:form>
    <apex:outputPanel id="inputFields">
        <div style="width:600px; height:auto; float:left; text-align:left;">
            <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Product:"/>
            <apex:inputField id="inputProduct" value="{!newCase.Affected_Product__c}" styleClass="picklist" required="true">
                <apex:actionsupport event="onchange" action="{!validateInputFields}" rerender="inputFields">
                    <apex:param value="{!newCase.Affected_Product__c}" assignTo="{!caseProduct}" name="caseProduct"/>            
                </apex:actionsupport>
            </apex:inputField>
        </div><br/>
        <div style="width:600px; height:auto; float:left; text-align:left;">
            <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Version:"/>
            <apex:inputField id="inputVersion" value="{!newCase.Version__c}" styleClass="picklist" onchange="pushVersion();" required="true"></apex:inputField>
            <apex:actionRegion>
                <apex:actionFunction name="pushVersion" action="{!getCaseProduct}" reRender="inputFields" immediate="true">
                    <apex:param value="{!newCase.Version__c}" assignTo="{!caseVersion}" name="caseVersion"/>
                </apex:actionFunction>
            </apex:actionRegion>
        </div><br/>
        </apex:outputPanel>
        <div style="width:600px; height:auto; float:left; text-align:left;">
            <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Category:"/>
            <apex:inputField value="{!newCase.Request_Type__c}" styleClass="picklist" required="true"></apex:inputField>
        </div><br/>
        <div style="width:600px; height:auto; float:left; text-align:left;">
            <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Topic:"/>
            <apex:inputField value="{!newCase.Problem_Type__c}" styleClass="picklist" required="true"></apex:inputField><p/>
        </div><br/>
        <apex:outputText style="font-weight:bold;font-size:12px;color:#595959;float:left;padding:0 10px;" value="Provide a brief describe of the issue:"/>
        <apex:inputField value="{!newCase.Subject}" styleClass="textarea" html-placeholder="How we can help?" required="true"></apex:inputField>

        <!-- Hidden Collection Fields -->
        <input type="hidden" name="liveagent.prechat:CaseContactId" id="contactId" value="{!contactId}"/>
        <input type="hidden" name="liveagent.prechat:CaseAccountId" id="accountId" value="{!contactAccountId}"/>
        <input type="hidden" name="liveagent.prechat:ContactFirstName" id="firstName" value="{!contactFirstName}"/>
        <input type="hidden" name="liveagent.prechat:ContactLastName" id="lastName" value="{!contactLastName}"/>
        <input type="hidden" name="liveagent.prechat:ContactEmail" id="email" value="{!contactEmail}"/>
        <input type="hidden" name="liveagent.prechat:CaseSubject" id="subject" value="{!caseSubject}"/>
        <input type="hidden" name="liveagent.prechat:CaseDescription" id="description" value="{!newCase.Description}"/>
        <input type="hidden" name="liveagent.prechat:CaseStatus" value="New"/>
        <input type="hidden" name="liveagent.prechat:CaseOrigin" value="Live Chat"/>
        <input type="hidden" name="liveagent.prechat:CaseProduct" id="product" value="{!caseProduct}"/>
        <input type="hidden" name="liveagent.prechat:CaseProductVersion" id="version" value="{!caseVersion}"/>
        <input type="hidden" name="liveagent.prechat:CaseRequestType" id="requestType" value="{!newCase.Request_Type__c}"/>
        <input type="hidden" name="liveagent.prechat:CaseProblemType" id="problemType" value="{!newCase.Problem_Type__c}"/>
        <input type="hidden" name="liveagent.prechat:CaseRecordType" value="012F0000000zp0x"/>
        <input type="hidden" name="liveagent.prechat.name" id="prechat_field_name"/>

        <!-- Map Contact Fields via Form -->
        <input type="hidden" name="liveagent.prechat.findorcreate.map:Contact" value="Id,CaseContactId;FirstName,ContactFirstName;LastName,ContactLastName;Email,ContactEmail"/>

        <!-- Map Case Fields via Form -->
        <input type="hidden" name="liveagent.prechat.findorcreate.map:Case" value="Subject,CaseSubject;Status,CaseStatus;Origin,CaseOrigin;Description,CaseSubject;Affected_Product__c,CaseProduct;Version__c,CaseProductVersion;Request_Type__c,CaseRequestType;Problem_Type__c,CaseProblemType;ContactId,CaseContactId;AccountId,CaseAccountId;RecordTypeId,CaseRecordType"/>

        <!-- Find Contact and Match or Create  -->
        <input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Contact" value="Id,true"/>
        <input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Contact" value="Id,true"/>
        <input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Contact" value="FirstName,true;LastName,true;Email,true;Phone,true"/>

        <!-- Create Case and attach Chat Details -->
        <input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Case" value="CaseNumber,true"/>
        <input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Case" value="CaseNumber,true"/>
        <input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Case" value="Subject,true;Status,true;Origin,true;Description,true;Affected_Product__c,true;Version__c,true;Request_Type__c,true;Problem_Type__c,true;ContactId,true;AccountId,true;RecordTypeId,true"/>

        <!-- Link Contact to New Case -->
        <input type="hidden" name="liveagent.prechat.findorcreate.linkToEntity:Contact" value="Case,ContactId"/>

        <!-- Display Contact and Case as sub-tabs in Live Agent Console -->
        <input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Contact" value="true"/>
        <input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Case" value="true"/>

        <!-- Link Chat Transcript to Contact and Case -->
        <input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Contact" value="ContactId"/>
        <input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Case" value="CaseId"/>

        <!-- Hide Case Record Type from Agent -->
        <input type="hidden" name="liveagent.prechat.findorcreate.displayToAgent:CaseRecordType" value="false"/>

        <!-- Start Chat Button -->
        <p/><input type='submit' value='REQUEST A CHAT' id='prechat_sbumit' onclick="setName()"/>

        <!-- Set Chat Visitor name in Live Agent Console -->
        <script type="text/javascript">
            function setName(){
                document.getElementById("prechat_field_name").value = 
                document.getElementById("firstName").value + " " + document.getElementById("lastName").value;
            }
        </script>
        </apex:outputPanel>
    </apex:form>
</form>
</div>
<!-- CSS -->
    <style>
        .picklist{
            width:300px;
            height:20px;
            background: #fff;
            border: 1px solid #CCCCCC;
            border-radius: 5px;
            margin-bottom: 5px;
            padding: 0 10px;
        }
        select{
            width:300px;
            height:20px;
            background: #fff;
            border: 1px solid #CCCCCC;
            border-radius: 5px;
            margin-bottom: 5px;
            padding: 0 10px;
        }
        .textarea{
            width:450px;
            height: 100px;
            background: #fff;
            border: 1px solid #CCCCCC;
            border-radius: 5px;
            margin-bottom: 5px;
            padding: 0 10px;
        }
    </style>

    <style>
    body{
    background-color:#fff;
    }
    #chatFormDiv
    {
    width:300px;
    text-align:center;
    padding:5px;
    }
    #chatHeader
    {
    color:#6d6d6d;
    font-size:18px;
    font-weight:bold;
    float: center;
    }
    #prechat_submit
    {
    font-weight:bold;
    float: center;
    }
    label
    {
    width:150px;
    font-weight:bold;
    }
    input[type=text], textarea
    {
    height: 50px;
    width:400px;
    background: #fff;
    border: 1px solid #CCCCCC;
    border-radius: 5px;
    margin-bottom: 5px;
    padding: 0 10px;
    }
    input[type=email]
    {
    height: 30px;
    width:280px;
    background: #fff;
    border: 1px solid #CCCCCC;
    border-radius: 3px;
    margin-bottom: 5px;
    padding: 0 10px;
    }
    input[type=tel]
    {
    height: 30px;
    width:280px;
    background: #fff;
    border: 1px solid #CCCCCC;
    border-radius: 3px;
    margin-bottom: 5px;
    padding: 0 10px;
    }
    input[type=text]
    {
    height: 30px;
    }
    textarea{
    height:140px;
    padding-top: 10px;
    padding-bottom: 10px;
    }
    .chatStatusDiv
    {
    display:none;
    }
    </style>
</apex:page>

Controller:

public class CommunityPreChatController {

private ApexPages.StandardController stdCtrl;
public Case newCase {get;set;}
public String caseSubject {get;set;}
public String caseProduct {get;set;}
public String caseVersion {get;set;}
public Id contactId {get;set;}
public Id contactAccountId {get;set;}
public String contactFirstName {get;set;}
public String contactLastName {get;set;}
public String contactEmail {get;set;}
public Datetime currentDateTime {get;set;}
public Boolean formComplete {get;set;}

public CommunityPreChatController(ApexPages.StandardController stdController) {
    stdCtrl = stdController;
    newCase = (Case) stdctrl.getRecord();
    //newCase = new Case();
    newCase.Status = 'New';
    getCurrentUserContact();
    currentDateTime = Datetime.now();
}

public void getCurrentUserContact(){
    User u = [SELECT Id, ContactId, Contact.AccountId, Contact.FirstName, Contact.LastName, Contact.Email FROM User WHERE Id=:UserInfo.getUserId() LIMIT 1];
    contactId = u.ContactId;
    contactAccountId = u.Contact.AccountId;
    contactFirstName = u.Contact.FirstName;
    contactLastName = u.Contact.LastName;
    contactEmail = u.Contact.Email;
 }
}

Best Answer

How to show dependent picklist in PreChat form.

It seems that PrechartForm couldn't render apex:inputfield component and bind SObject field. I've found that we could display primitive property initialized within controller. Knowing this, we could solve problem with displaying picklist by implementing next steps:

  • Get schema PicklistValues in custom prechart controller
  • Bind Json as a string to hidden field in prechatform
  • Parse hidden field with JS and create picklist

Custom PrechartForm controller:

public class prechartclass {
public string typeSelectOption{get;set;}
public string customSelectOption{get;set;}

public prechartclass() {

    Schema.DescribeFieldResult fieldResult; 
    List<Schema.PicklistEntry> ple;

    fieldResult = case.type.getDescribe();
    ple = fieldResult.getPicklistValues();
    typeSelectOption = json.serialize (ple);

    fieldResult = case.cards__c.getDescribe();
    ple = fieldResult.getPicklistValues();
    customSelectOption = json.serialize (ple);

}   }

Code above will return Type and custom Cards__c picklistvalues:

{"active":true,"defaultValue":false,"label":"Mad Hatter","value":"Mad Hatter"}
{"active":true,"defaultValue":false,"label":"hearts","validFor":"nAAA","value":"hearts"}

Visualforce could get this from controller by:

<input type="hidden" id="hiddenType" value="{!typeSelectOption}"/>
<input type="hidden" id="hiddenCustom" value="{!customSelectOption}"/>

In your prechatForm, you should add JS and html markup:

Add right after you input elements:

<table >
<tr>
    <td><label for="dependentOption">Type:</label></td>
    <td><div id="dependentOption"> </div></td>
</tr>
</table>

Div component will be used for picklist creation

Add field Type to .map:Case and .map.doCreate:Case:

<input  type="hidden" 
    name="liveagent.prechat.findorcreate.map:Case" 
    value="Subject,CaseSubject;Status,CaseStatus;Origin,CaseOrigin;Type,CaseType;" />

<input  type="hidden" 
    name="liveagent.prechat.findorcreate.map.doCreate:Case" 
    value="Subject,true;Status,true;Origin,true;Type,true;" />

Note that field name here is case sensitive

Whole Prechat Form with JavaScript:

<apex:page showHeader="false" Controller="prechartclass">

<!-- This script takes the endpoint URL parameter passed from the deployment page and makes it the action for the form -->
<script type='text/javascript'>
(function() {
function handlePageLoad() {
var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)");
document.getElementById('prechatForm').setAttribute('action',
decodeURIComponent(endpointMatcher.exec(document.location.search)[1]));
} if (window.addEventListener) {
window.addEventListener('load', handlePageLoad, false);
} else { window.attachEvent('onload', handlePageLoad, false);
}})();

var controlingId = "ctrl"
var dependentId = "dpnd";

window.onload = setupPage;

var ctrlFieldDesc;
var depFieldDesc;
var dependentOptions;

function setupPage() {
//load page with selectlist component
ctrlFieldDesc = document.querySelectorAll('[id$="hiddenType"]')[0].value;
depFieldDesc = document.querySelectorAll('[id$="hiddenCustom"]')[0].value;

dependentOptions = getDependentOptions();
buildControlling();
buildDependent();

}

function buildControlling() {

var list = JSON.parse(ctrlFieldDesc);

var myDiv = document.getElementById("controllingOption");
appendSelectOption(myDiv, list, controlingId, buildDependent, 'liveagent.prechat:CaseType');

}

function buildDependent() {

var x = document.getElementById(controlingId).value;
var myDiv = document.getElementById("dependentOption");
var slist = document.getElementById(dependentId);
if (slist) { myDiv.removeChild(slist); }
appendSelectOption(myDiv, dependentOptions[x], dependentId, '', 'liveagent.prechat:CaseCustom');

}

//create every selectoption component
function appendSelectOption(myDiv, list, cId, ajax, name) {

selectList = document.createElement("select");
selectList.id = cId;
selectList.name = name;
if (ajax) {
    selectList.onchange = buildDependent;
}
myDiv.appendChild(selectList);

var option = document.createElement("option");
option.text = "--None--";
selectList.appendChild(option);
for (var item in list) {
    if (list[item]["active"]) {
        var option = document.createElement("option");
        option.value = list[item]["label"];
        option.text = list[item]["label"];
        if (list[item]["defaultValue"]) {
            option.selected="selected";
        }
        selectList.appendChild(option);
    }
}
}

function getDependentOptions(){
// Set up return object
var dependentOptions = {};
var ctrlValues = JSON.parse(ctrlFieldDesc);

for (var i=0; i<ctrlValues.length; i++) {
    dependentOptions[ctrlValues[i].label] = [];
}
// For each dependent value, check whether it is valid for each controlling value
var depValues = JSON.parse(depFieldDesc);
for (var i=0; i<depValues.length; i++) {
    var thisOption = depValues[i];
    var validForDec = decodeBase64(thisOption.validFor);
    for (var ctrlValue=0; ctrlValue<ctrlValues.length; ctrlValue++) {
        if (testBit(validForDec, ctrlValue)) {
            dependentOptions[ctrlValues[ctrlValue].label].push(thisOption);
        }
    }
}
return dependentOptions;
}

function testBit (validFor, pos) {
var byteToCheck = Math.floor(pos/8);
var bit = 7 - (pos % 8);
return ((Math.pow(2, bit) & validFor.charCodeAt(byteToCheck)) >> bit) == 1;
}

function decodeBase64(s) {
var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;
var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(i=0;i<64;i++){e[A.charAt(i)]=i;}
for(x=0;x<L;x++){
    c=e[s.charAt(x)];b=(b<<6)+c;l+=6;
    while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}
}
return r;
};

</script>

<h1>Live Agent Pre-Chat Form</h1>

<!-- Form that gathers information from the chat visitor and sets the values to Live Agent Custom Details used later in the example -->
<form method='post' id='prechatForm'>
First name: <input type='text' name='liveagent.prechat:ContactFirstName' id='firstName' /><br />
Last name: <input type='text' name='liveagent.prechat:ContactLastName' id='lastName' /><br />
Email: <input type='text' name='liveagent.prechat:ContactEmail' id='email' /><br />
Phone: <input type='text' name='liveagent.prechat:ContactPhone' id='phone' /><br />
Issue: <input type='text' name='liveagent.prechat:CaseSubject' id='subject' /><br />

<table >
<tr>
    <td><label for="controllingOption">Type:</label></td>
    <td><div id="controllingOption"> </div></td>
</tr>
<tr>
    <td><label for="dependentOption">Custom:</label></td>
    <td><div id="dependentOption"> </div></td>
</tr>
</table>

<input type="hidden" id="hiddenType" value="{!typeSelectOption}"/>
<input type="hidden" id="hiddenCustom" value="{!customSelectOption}"/>

<!-- Hidden fields used to set additional custom details -->
<input type="hidden" name="liveagent.prechat:CaseStatus" value="New" /><br />

<!-- This example assumes that "Chat" was added as picklist value to the Case Origin field -->
<input type="hidden" name="liveagent.prechat:CaseOrigin" value="Chat" /><br />

<!-- map: Use the data from prechat form to map it to the Salesforce record's fields -->
<input type="hidden" name="liveagent.prechat.findorcreate.map:Contact" value="FirstName,ContactFirstName;LastName,ContactLastName;Email,ContactEmail;Phone,ContactPhone" />

<input type="hidden" name="liveagent.prechat.findorcreate.map:Case" 
value="Subject,CaseSubject;Status,CaseStatus;Origin,CaseOrigin;Type,CaseType;Cards__c,CaseCustom" />

<!-- doFind, doCreate and isExactMatch example for a Contact: 
Find a contact whose Email exactly matches the value provided by the customer in the form 
If there's no match, then create a Contact record and set it's First Name, Last Name, Email, and Phone to the values provided by the customer -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Contact" value="Email,true" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Contact" value="Email,true" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Contact" value="FirstName,true;LastName,true;Email,true;Phone,true" />

<!-- doCreate example for a Case: create a case to attach to the chat, set the Case Subject to the value provided by the customer and set the case's Status and Origin fields -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doCreate:Case" value="Subject,true;Status,true;Origin,true;Type,true;Cards__c,true" />

<!-- linkToEntity: Set the record Contact record, found/created above, as the Contact on the Case that's created --> 
<input type="hidden" name="liveagent.prechat.findorcreate.linkToEntity:Contact" value="Case,ContactId" />

<!-- showOnCreate: Open the Contact and Case records as sub-tabs to the chat for the agent in the Console -->
<input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Contact" value="true" />
<input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Case" value="true" />

<!-- saveToTranscript: Associates the records found / created, i.e. Contact and Case, to the Live Chat Transcript record. --> 
<input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Contact" value="ContactId" />
<input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Case" value="CaseId" />

<!-- displayToAgent: Hides the case record type from the agent -->
<input type="hidden" name="liveagent.prechat.findorcreate.displayToAgent:CaseRecordType" value="false" />

<!-- searchKnowledge: Searches knowledge article based on the text, this assumes that Knowledge is setup -->
<input type="hidden" name="liveagent.prechat.knowledgeSearch:CaseSubject" value="true" />

<input type='submit' value='Chat Now' id='prechat_submit' onclick="setName()"/>

</form>
</apex:page>

Result:

Prechat Piclist

Chosen custom picklist value arrives in agent console.

I've used code snippets from: broc.seib and Benj