11

I currently have some VisualForce which uses an <apex:repeat/> element and inserts commas in between the items like so:

<apex:variable var="idx" value="{!0}"/>
<apex:repeat value="{!items}" var="item">
    <apex:outputPanel rendered="{!NOT(idx == 0)}">, </apex:outputPanel>
    <apex:outputPanel>
        <apex:outputText value="{!item.Name}" />
    </apex:outputPanel>
    <apex:variable var="idx" value="{!idx + 1}" />
</apex:repeat>

However, today I noticed in the documentation that this is explicitly unsupported and is a case of undefined behavior:

<apex:variable> does not support reassignment inside of an iteration component, such as <apex:dataTable> or <apex:repeat>. The result of doing so, e.g., incrementing the <apex:variable> as a counter, is unsupported and undefined.

(Source: https://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_variable.htm)

So my question is this: if the syntax I'm using is not supported (even though it does work), then how can I render this comma-delimited data in a way that is supported, using only VisualForce? Is this possible?

4
  • Ideally I'm looking for a way to detect if an item is first or last in an apex:repeat without relying on the controller to do that, since i would like to be able to do things such as apply CSS styles to the first item, emit different markup, etc., in addition to comma-delimiting the items. However, I understand that this may not be possible using only visualforce. If that's the case then a solution involving the controller is ok. Jun 5 '14 at 17:02
  • how is the items list being populated is it a list<sobject> that you are returning?
    – Rao
    Jun 5 '14 at 17:05
  • The list is a property on the controller populated by a SOQL select Jun 5 '14 at 17:07
  • 2
    Apexonauts unite and vote for having officially index support:success.salesforce.com/… Jul 24 '14 at 17:54
8

I rethought and this seems the best way so deleting the previous contents:

  1. Create a map<account,index>

  2. Put the account, index and also populate the last index in your method, and check if the index = last index and render the ,

Page:

<apex:page controller="testing_repeat_controller" sidebar="false">
<apex:repeat value="{!Itemsforuser}" var="item">    
    <apex:outputPanel >
        <apex:outputText value="{!item.Name}"/>
        {!IF(account_index_map[item] == lastindexofaccount,'',',')}
    </apex:outputPanel>
</apex:repeat>
</apex:page>

Controller:

public class testing_repeat_controller {
    public integer indexofaccount {get;set;}
    public integer lastindexofaccount {get;set;}
    public map<account,integer> account_index_map {get;set;} 
    public testing_repeat_controller(){
        indexofaccount      = 0;
        lastindexofaccount  = 0;
        account_index_map = new map <account,integer>();
    }
    public List<account> getItemsforuser(){
        list<account> acc_list = [select id,name from account limit 10];
        lastindexofaccount = acc_list.size();
        for(account a: acc_List){        
            indexofaccount = indexofaccount +1;
            account_index_map.put(a,indexofaccount);
        }
        return acc_List;
    }
}

Output:

abc ,abc ,TEST ,test123 ,abc ,abc ,abc ,TEST ,Test_Pass ,abc
9
  • The first method doesn't seem to work unfortunately (I don't get any commas in the output at all). I think my best bet is probably to use a wrapper class as in method 4 Jun 5 '14 at 17:57
  • dang it i used my custom list my bad i will delete that solution, the wrapper is the only way then
    – Rao
    Jun 5 '14 at 17:58
  • The option you removed gave me the idea to do something like this inside the loop: {!IF(item != items[0], ', ', '')}, however salesforce threw this error when I tried it: "Incorrect parameter type for subscript. Expected Text, received Number". Next I tried using this: {!IF(item != items[items.size-items.size], ', ', '')} which, strangely, worked as expected... However, I'd still like to figure out a way to avoid relying on behavioral quirks like this Jun 5 '14 at 18:15
  • 3
    Without adding any methods to the controller, this seems to work: <apex:outputPanel rendered="{!item != items[items.size - 1]}">, </apex:outputPanel>... however, VF's subscripting appears to have some strange behavior with lists. I opened another question about that here: salesforce.stackexchange.com/questions/38811/… Jun 5 '14 at 18:39
  • categories.size = 10 say when you do categories.size-1 it pulls categories[9] which is the 10th account in the list. you are simply saying VF do not display , for the 10th account from the {!categories} list.
    – Rao
    Jun 5 '14 at 18:43
5

I think a more elegant solution would be to render items as an unordered list and then use css to render the commas.

<style type="text/css">
    .cs-list {
      display: inline;
      list-style: none;
      padding-left: 0px;
    }

    .cs-list li {
      display: inline;
      margin-left: 3px;
    }

    .cs-list li:after {
      content: ", ";
    }
    .cs-list li:last-child:after {
        content: "";
    }
</style>

...
<ul class='cs-list'>
<apex:repeater var='item' value='{!myStringList}'>
   <li>{!item}</li>
</apex:repeater>
</ul>

You might need to tweak margin and padding a bit to get it to look perfect (this example came from an project that was loading bootstrap css).

2

Another way slightly different to Jennifer and without a controller, is to count each time it loops, and check if the last one is the same size as the list size:

<apex:variable var="cnt" value="{!0}" /> 
<apex:repeat value="{!items}" var="item">
    <apex:variable var="cnt" value="{!cnt+1}"/>

    <apex:outputPanel rendered="{!cnt == items.size}">
        Display Empty
    </apex:outputPanel>
    <apex:outputPanel rendered="{!cnt != items.size}">
        Equal display empty
    </apex:outputPanel>    
</apex:repeat>
0

You could do it without a controller with help of the ID of the record.

<apex:repeat value="{!items}" var="item">
  <apex:outputPanel rendered="{!items[0].id != item.id}">, </apex:outputPanel>
  <apex:outputPanel>
     <apex:outputText value="{!item.Name}" />
  </apex:outputPanel>
</apex:repeat>

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.