2

This is a follow-up to the original question asked by tomlogic here: Errors using ISPICKVAL on Visualforce page since I don't have enough reputation points to comment on the answers.

I am trying to develop a visualforce page that will render as a PDF document. I have some records that I need to be displayed on the page conditionally, based on the return value of an IF() function that I want to use to check the values of a multi-select picklist field. Right now my code looks something like this:

        <apex:repeat value="{!PriceBook2.PriceBookEntries}" var="p">
            <apex:outputPanel styleClass="sheet-row" layout="block"
                rendered="{!
                    IF(
                        AND(
                            ISPICKVAL(p.Product2.Product_Family__c, $CurrentPage.parameters.family),
                            p.Product2.IsActive == True
                        ), 
                        'true', 'false'
                    )}">
                <div class="sheet-column-1">
                    <p class="product-name">
                        {!p.Product2.Short_Product_Description__c}
                    </p>
                    <p class="product-code">
                        ({!p.ProductCode})
                    </p>
                </div>
                <div class="sheet-column-2">
                    {!p.Product2.Description}
                </div>
                <div class="sheet-column-3">
                    <apex:outputText value="{0, number, currency}">
                        <apex:param value="{!p.UnitPrice}" />
                    </apex:outputText>  
                </div>
            </apex:outputPanel>
        </apex:repeat>  

After all these years, Salesforce still does not seem to allow us to use ISPICKVAL() on the VF page as it's treating the values returned by field references as regular strings ("Incorrect parameter type for function 'ISPICKVAL()'. Expected Picklist, received Text").

Is there any way I can reliably check the value of a multi-select picklist in the VF page without doing some crazy hack with CONTAINS()? I don't want to use CONTAINS() as it may incorrectly match certain values that contain the same strings (but are still distinct values).

For example if I have the values "ABC" and "ABC123" in my picklist, and I want only records that have selected "ABC", I can't use CONTAINS(p.Product2.Product_Family__c, "ABC") as it will also return true for the ones that have "ABC123" selected.

Is there any workaround short of using dirty javascript hacks or custom objects/apex controllers to pre-sort the records into separate lists? Note: INCLUDES doesn't work either. "Function INCLUDES may not be used in this type of formula"

2

String.split() is really the way to go, deal with it :)

It's

  • easy to understand (future maintenance developer will thank you)
  • fast to execute (each record is processed only once with a simple operation, build Map<String, List<sObject>> out of them (or Map<String, List<Id>> if viewstate is a concern) and you're done
  • do it right once and you don't have to update the class/page every time you add new picklist value

Compare it with having to do multiple conditionally rendered sections (can blow VF's internal viewstate), processing one record over and over (potentially you can have 1000 picklist values, good luck with that), will overall take longer...

In your particular case the Active true / false could also be achieved by throwing records into some helper wrapper class with extra Boolean. Again - one time investment that will keep it simple in the long run.


If you really want pure VF and "reliable CONTAINS()" - think what you're after. ABC that ends with semicolon ';' or is at the very end of the string is valid match. ABC123 is not. So you could have some orgy of

CONTAINS(obj.Field__c, 'ABC;') || RIGHT(obj.Field__c, LEN('ABC')) = 'ABC'

or

REGEX(obj.Field__c, 'ABC[;$]')

(untested, just an idea. But REGEX is an expensive operation. Here are some examples. Just make sure you test on a page with lots of records & check if "internal viewstate" doesn't blow up too much with these expressions)

  • Hi, I tried the formula you suggested (replacing every instance of "obj.Field__c" with 'p.Product2.Product_Family__c' and every instance of 'ABC' with $CurrentPage.parameters.family, concatenating strings when needed using '&'. Unfortunately, I'm getting "Syntax error: Missing ')'" Unfortunately there is not enough characters allowed in this post to post my code. – ag415 Sep 26 '17 at 17:28
  • Looks like I have enough characters to fit the expression in a second post "{! IF( AND( CONTAINS(p.Product2.Product_Family__c, $CurrentPage.parameters.family & ';') || RIGHT(p.Product2.Product_Family__c, LEN($CurrentPage.parameters.family) = $CurrentPage.parameters.family) REGEX(p.Product2.Product_Family__c, $CurrentPage.parameters.family & '[;$]'), p.Product2.IsActive == True ), 'true', 'false' )}" – ag415 Sep 26 '17 at 17:31
  • If I insert a || between the the RIGHT() function and the REGEX() function or remove the REGEX() function altogether, I get the error "Incorrect parameter type for operator '='. Expected Number, received Text. If I change the = to a == the error also persists – ag415 Sep 26 '17 at 17:44
  • 1
    No, don't use both lines ;) I was suggesting either "CONTAINS + RIGHT" or a "REGEX"-based solutions :) I'll have a look later today. String split would have to be done in Apex, VF PDF engine doesn't run Javascript code. Well, there are hacks like "jspdf" jQuery library but I don't think it's worth it, you want simplicity of pure, server-side solution – eyescream Sep 26 '17 at 18:05
  • 1
    Try CONTAINS(obj.Field__c, 'ABC;') || RIGHT(obj.Field__c, LEN('ABC')) = 'ABC' ? – eyescream Sep 26 '17 at 18:08
3

You have to use contains. The ms picklist is a UI concept. The values are stored in the DB as a ; deliminated string. Ispickval does not even work in anything else for a MS picklist so not sure what u were expecting

  • Thanks for the reminder about ISPICKVAL. I forgot that this only works on single select picklists. However the problem remains that I can't reliably check the selected value(s) of the multi-select picklist field without resorting to some crazy hack with CONTAINS(). – ag415 Sep 26 '17 at 17:41
  • One solution I did figure out would work is if I create a formula field on the products object that generates a string using CASE(), wrapping each value in unique strings like "@@@" and then checking IF(CONTAINS(Obj.Field__c, '@@@ABC@@@')) However this is pretty dirty and will require updating the formula field every time the values change. Ideally, I'd like a method that doesn't require constant updating of a formula field every time the picklist values change – ag415 Sep 26 '17 at 17:41

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.