4

I'm creating a custom Visualforce page and have a list of contacts to make a "list view" kind of table. Perfect for an apex:repeat I thought.

One of the requirements, however, is to split up the Contacts depending on the letter their surname begins with. So you might have:

A

Abbas, Ahmed
Adamson, Neil

B

Ball, Alan

E

Edgar, Dean

I realize this will complicate the apex:repeat, but I'm not sure how to best solve it. The only thing I can think of at the moment is to create 26 Lists with names like List contactsA, List contactsB etc. and use an apex:repeat for each of them, but that's pretty grim and I imagine the governor limits would be lurking around some dark corner. What's the best solution?

2

I like Jon's approach, but if you're looking for simpler VF markup (so you don't need 26 repeats) you can try a list of lists of contact records. The code below isn't my prettiest, but the main list will contain lists of contacts grouped by the first letter of their last name.

Controller:

public List<List<Contact>> getAlphaContacts() {
    List<List<Contact>> alphaContacts = new List<List<Contact>>();
    Contact[] cons = [select id, firstname, lastname from contact
                         order by lastname asc, firstname asc];
    if ( cons != null && cons.size() > 0 ) {
        String currentLetter = 'anyLetter';
        List<Contact> currentList = new List<Contact>();
        for ( Contact c : cons ) {
            String letter = c.lastname.substring(0,1);
            if ( currentLetter != letter ) {
                if ( currentList.size() > 0 ) {
                    alphaContacts.add(currentList);
                    currentList = new List<Contact>();
                }
                currentLetter = letter;
            }
            currentList.add(c);
        }
        if ( currentList.size() > 0 )
            alphaContacts.add(currentList);
    }
    return alphaContacts;
}

VF page:

<apex:pageBlock title="Alpha Test" >
    <apex:repeat value="{!alphaContacts}" var="alpha">
        <p style="font-weight:bold;">{!LEFT(alpha[0].lastname, 1)}</p>
        <apex:repeat value="{!alpha}" var="con">
            {!con.lastname}, {!con.firstname}<br/>
        </apex:repeat>
    </apex:repeat>
</apex:pageBlock>

This will produce something similar to:

B
Barr, Tim
Bond, James
Bond, John
Boyle, Lauren

D
D'Cruz, Liz
Davis, Josh

F
Forbes, Sean
Frank, Edna

G
Gonzalez, Rose
Green, Avi
Grey, Jane
2

UPDATE - I have tested this now, my original answer was almost correct but not quiet right, I made the wrong suggestion that a variable could be passed to an apex:repeat. This has been remedied by passing back the Map rather than getting the controller to extract the list from the map.

I think your best bet here is to create a custom controller that loads all your contacts into Lists that are then stored in a Map by letter.

You should be able to overcome the need for 26 apex:repeats by using a nested apex:repeat. This page may be of help:

http://www.salesforce.com/us/developer/docs/pages/Content/pages_compref_repeat.htm

VF Page:

<apex:page controller="myController">
<apex:pageBlock >
<apex:repeat value="{!alphabet}" var="letter">
    <apex:pageBlockSection title="{!letter}">
        <apex:repeat value="{!alphaMap[letter]}" var="con">
            <apex:outputText value="{!con.LastName}" />
            <apex:outputText value="{!con.FirstName}" />
        </apex:repeat>
    </apex:pageBlockSection>
</apex:repeat>
</apex:pageBlock>
</apex:page>

Controller:

public with sharing class myController
    {

    public Map<String,List<Contact>> alphaConList = new Map<String,List<Contact>>();
    public String letterToGet{ get; set; }

    public myController() {
        List<Contact> cons = [SELECT Id, FirstName, LastName FROM Contact];
        letterToGet = '';

        for(Contact con: cons){
            String letter = '';
            letter = con.LastName; // Grab the lastname
            letter = letter.toUpperCase(); //Make upper case, you could always use lowercase
            letter = letter.substring(0,1); //Take only the first character
            List<Contact> conlist = new List<Contact>();

            if (alphaConList.containsKey(letter))
            {
                conlist =  alphaConList.get(letter); // return a list of Contacts for the given letter i.e. 'A'
            }

            conlist.add(con);

            alphaConList.put(letter, conlist); //Add the contact to the list
        }
    }

    public Map<String,List<Contact>> getAlphaMap()
    {
        return alphaConList;
    }

    public List<String> getAlphabet(){
        List<String> alpha = new List<String>(alphaConList.keySet());
        return alpha;
    }
}
8
  • This is great, especially the use of containsKey and the dynamic letter variable (I was going to use startsWith() and a load of ifs but this is a lot cleaner) – George S. Sep 18 '12 at 15:30
  • Glad I could help, also Ive just updated with a bit of info about using repeats to cycle through a list of strings (i.e. A, B, C) which I think you may be able to use to nest the repeats and make your VF page much neater – Jon Hazan Sep 18 '12 at 15:32
  • @JonHazan, how would you call getAlphalist with an argument from the nested apex:repeat? Would you need an extra apex:param in there to pass back to the controller? I went with the list of lists approach because I wasn't aware of any easy way to call Map.get from VF. – JCD Sep 18 '12 at 15:44
  • 1
    Haha, as you asked that I added an example :) I'm pretty sure you use this notation: {!alphaList[letter]} – Jon Hazan Sep 18 '12 at 15:47
  • 2
    This seems like it will display a header for a letter even if there are no Contacts with a matching name. One solution to that would be to return your list<String> Alphabet as a sorted list made from the keyset of your map<String, list<Contact>> Alphalist, instead of a static list of all letters. – Jeremy Nottingham Sep 18 '12 at 15:56

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.