2

I have 2 lists list<Attachment> and list<Calls__C>.

The relation is Attachment.parentid=CAlls__c.Account__C. Now I want single repeat that will traverse through these two lists in pageblocktable.

column1> Calls__c.name 
column2> CAlls__c.Account__r.name
Column3> Attachment.ParentId
Column4> Attachment.Id

Please find the apex page & controller as Kevin suggested. I guess controller is fine,page having some issue. Apex page:

<apex:page controller="attachmentCallsWrapper_parent">
<apex:panelGrid columns="5" border="1" >
  <apex:dataTable value="{!callWrapper}" var="cw">
  <apex:column >
    <apex:facet name="header" >SEQ</apex:facet>
    <apex:outputText value="{!cw.sequence}" />
  </apex:column>
  <apex:column >
    <apex:facet name="header" >Call Name</apex:facet>
    <apex:outputText value="{!cw.callName}" />
  </apex:column>
  <apex:column >
    <apex:facet name="header" >Account Name</apex:facet>
    <apex:outputText value="{!cw.accountName}" />
  </apex:column>
  <apex:column >
    <apex:facet name="header" >Call Id</apex:facet>
    <apex:outputText value="{!cw.callId}" />
  </apex:column>
  <apex:column >
    <apex:facet name="header" >Attachment Id</apex:facet>
    <apex:outputText value="{!cw.attachmentId}" />
  </apex:column>
  </apex:dataTable>
</apex:panelgrid>
</apex:page>

Controller:

public class attachmentCallsWrapper_parent{

public void attachmentCallsWrapper_parent(){}

public  List<attachmentCallsWrapper> callWrapper {get;set;}
public list<CAll2_vod__C> mycalls=[select id,name,account_vod__C from CAll2_vod__C where account_vod__C=:ApexPages.currentPage().getParameters().get('id') ];

// construct a map for calls/attachments
// using set to avoid duplicate attachmentIds, unlikely, if any happened to be found

map<id,set<attachment>> mapCallAttachments = new map<id,set<attachment>>();
public void method1(){
for (Attachment atf : [Select Id,Name from attachment where parentId = :ApexPages.currentPage().getParameters().get('id')]) {
  if (mapCallAttachments.containsKey(atf.ParentId)) {
    set<attachment> satf = mapCallAttachments.get(atf.ParentId);
    satf.add(atf);
    mapCallAttachments.put(atf.ParentId,satf);
  } else {
    set<attachment> satf = new set<attachment>();
    satf.add(atf);
    mapCallAttachments.put(atf.ParentId,satf);
  }
}

// using map, re-iterate through list
for (Call2_vod__c c : myCalls) {
  if (mapCallAttachments.containskey(c.Id)) {
    set<attachment> satf = mapCallAttachments.get(c.Id);
    for (attachment atf : satf) {
      attachmentCallsWrapper cw = new attachmentCallsWrapper(c,atf);
      cw.sequence = callWrapper.size()+1;
      callWrapper.add(cw);
    }
  } else {
   attachmentCallsWrapper cw = new attachmentCallsWrapper(c,null);
   cw.sequence = callWrapper.size()+1;
  callWrapper.add(cw);
  }
}
}
}
1
  • Is this possible to display in pageblocktable? Please help – Testing_SFDC Sep 5 '14 at 15:01
3

I definitely think the wrapper class is the way to go; I use this method a lot especially as it helps to avoid view state issues.

I would recommend not showing record Ids, and instead provide a hyperlink on the name references. This would reduce your visible output to the user considerably. For those, you can make use of std html a href tags, or use apex link options.. I'll leave that to the imagination for now.

VF

<apex:page standardController="Case" extensions="caseAttach_controller" >

<style>
.rowOdd  { background-color: #B5B5BA; }
.rowEven { background-color: #CFD6E1; }
</style>

<apex:sectionheader title = "Case Attachments" />

<apex:pageblock>

<apex:pageBlockTable value="{!caseWrapper}" rowClasses="rowOdd,rowEven" var="cw">
  <apex:column>
    <apex:facet name="header" >SEQ</apex:facet>
    <apex:outputText value="{!cw.sequence}" />
  </apex:column>

  <apex:column>
    <apex:facet name="header" >Account Name</apex:facet>
    <a href="{!$Site.Prefix}/{!cw.AccountId}" id="{!cw.AccountId}" 
      target="_blank" onblur="LookupHoverDetail.getHover('{!cw.AccountId}').hide();" 
      onfocus="LookupHoverDetail.getHover('{!cw.AccountId}', '{!$Site.Prefix}/{!cw.AccountId}/m?retURL=%2F{!cw.AccountId}&isAjaxRequest=1').show();" 
      onmouseout="LookupHoverDetail.getHover('{!cw.AccountId}').hide();" 
      onmouseover="LookupHoverDetail.getHover('{!cw.AccountId}', '{!$Site.Prefix}/{!cw.AccountId}/m?retURL=%2F{!cw.AccountId}&isAjaxRequest=1').show();">{!cw.AccountName}</a> 
  </apex:column>

  <apex:column>
    <apex:facet name="header" >Case Number</apex:facet>
    <a href="{!$Site.Prefix}/{!cw.CaseId}" id="{!cw.CaseId}" 
      target="_blank" onblur="LookupHoverDetail.getHover('{!cw.CaseId}').hide();" 
      onfocus="LookupHoverDetail.getHover('{!cw.CaseId}', '{!$Site.Prefix}/{!cw.CaseId}/m?retURL=%2F{!cw.CaseId}&isAjaxRequest=1').show();" 
      onmouseout="LookupHoverDetail.getHover('{!cw.CaseId}').hide();" 
      onmouseover="LookupHoverDetail.getHover('{!cw.CaseId}', '{!$Site.Prefix}/{!cw.CaseId}/m?retURL=%2F{!cw.CaseId}&isAjaxRequest=1').show();">{!cw.caseNumber}</a> 
  </apex:column>

  <apex:column>
    <apex:facet name="header" >Attachments</apex:facet>
    <apex:outputLink target="blank" value="{!cw.downloadLink}" >{!cw.AttachmentName}</apex:outputLink>
    <!--<apex:outputLink value="{!$Site.Prefix}/{!cw.attachmentId}" >{!cw.AttachmentName}</apex:outputLink>-->

  </apex:column>

  </apex:pageBlockTable>

  </apex:pageBlock>

</apex:page>

Controller

public with sharing class CaseAttach_Controller {

  transient public List<attachmentCaseWrapper> caseWrapper {get; set;}

  // --------------------------------------------------------------------------------------------------------------------------------------------------------                
  public CaseAttach_Controller(ApexPages.StandardController controller) {

    List<Case> listCases = new List<Case>();
    // populate listCases with your records (ie, cases by account for last n days

    caseWrapper = new List<attachmentCaseWrapper>();

    // construct a map for calls/attachments
    // using set to avoid duplicate attachmentIds, unlikely, if any happened to be found

    map<id,set<attachment>> mapCaseAttachments = new map<id,set<attachment>>();

    for (Attachment atf : [Select Id,Name,ParentId from attachment where parentId in :listCases]) {
      if (mapCaseAttachments.containsKey(atf.ParentId)) {
         set<attachment> satf = mapCaseAttachments.get(atf.ParentId);
         satf.add(atf);
         mapCaseAttachments.put(atf.ParentId,satf);
       } else {
         set<attachment> satf = new set<attachment>();
         satf.add(atf);
         mapCaseAttachments.put(atf.ParentId,satf);
       }
     } // for

    // using map, re-iterate through list
    for (Case c : listCases) {
      if (mapCaseAttachments.containskey(c.Id)) {
        set<attachment> satf = mapCaseAttachments.get(c.Id);
        for (attachment atf : satf) {
          attachmentCaseWrapper cw = new attachmentCaseWrapper(c,atf);
          cw.sequence = caseWrapper.size()+1;
          caseWrapper.add(cw);
        }
      } else {
        attachmentCaseWrapper cw = new attachmentCaseWrapper(c,null);
        cw.sequence = caseWrapper.size()+1;
        caseWrapper.add(cw);
      } 
    } // for
  }

  // --------------------------------------------------------------------------------------------------------------------------------------------------------                
  // The first part of the wrapper are the elements that you would need for the page:
  // (the ordering is not important)

  public class attachmentCaseWrapper {
    public integer sequence      {get; set;} 
    public string caseNumber     {get; set;}
    public string accountName    {get; set;}
    public string accountId      {get; set;}
    public string caseId         {get; set;}
    public string attachmentId   {get; set;}
    public string attachmentName {get; set;}
    public string downloadLink   {get; set;}
    // the second part of the wrapper accepts the parameters and assigns values
    // to the above class variables

    public attachmentCaseWrapper (Case cse, Attachment atf) {
      caseNumber = cse.CaseNumber;
      accountName = cse.Account.Name;
      accountId = cse.Account.Id;
      caseId = cse.Id;
      if (atf != null) {
        attachmentId = atf.Id;
        attachmentName = atf.Name;
        downloadLink = 'https://c.cs11.content.force.com/servlet/servlet.FileDownload?file=' + atf.Id;
      } 
    }
  }

}
3
  • Hi Kevin the page is having some issue. I am unable to find out.I have used your suggestion in controller .I think it is working fine.Please find the page I have used. – Testing_SFDC Sep 5 '14 at 19:15
  • Hi Saikat; understandably the former sample was pseudo code; I mocked this up for use with cases, as it would be natively usable for anyone; just retouch case references to your call object references.. – Kevin Br Sep 5 '14 at 21:56
  • Hi Kevin. The wrapper class is not returning attachment in image format. It should return the signature.Can you please help me here? – Testing_SFDC Sep 8 '14 at 9:08
2

As @kcshafer points out, this can be done with maps and inner lists. This will let you group the lists by a parent key.

Though I think your model may be flawed, as it presumes a one-to-one relationship between the Attachment on the Account and a Calls__c record. This may get you started...

Apex

// create the maps
Map<Id, List<Attachment>> accountAttachmentsMap = new Map<Id, List<Attachment>>();
Map<Id, List<Calls__c>> accountCallsMap = new Map<Id, List<Calls__c>>();

// populate the map from your list
for (Attachment a : attachmentList) {

    // check to see if there is already an inner list
    if (accountAttachmentsMap.isEmpty()) {

        // create the map entry and init with a populated list
        accountAttachmentsMap.put(a.ParentId, new List<Attachment>{ a });
    } else {

        // get the list from the map and add to it
        accountAttachmentsMap.get(a.ParentId).add(a);
    }

}

// populate the map from your list
for (Calls__c c : callsList) {

    // check to see if there is already an inner list
    if (accountCallsMap.isEmpty()) {

        // create the map entry and init with a populated list
        accountCallsMap.put(c.Account__c, new List<Calls__c>{ c });
    } else {

        // get the list from the map and add to it
        accountCallsMap.get(c.Account__c).add(c);
    }

}

VF

<!-- repeat over the keyset from a map, use array notation to get the column data -->
<!-- there are two tables as the inner lists can vary in size -->
<apex:repeat value="{!accountCallsMap}" var="accountId">
    <apex:outputText value="{!accountId}" />
    <apex:pageBlockTable value="{!accountCallsMap[accountId]}" var="calls">
        <apex:column value="{!calls.Name}"/>
        <apex:column value="{!calls.Account__r.Name}"/> 
    </apex:pageBlockTable>

    <apex:pageBlockTable value="{!accountAttachmentsMap[accountId]}" var="att">
        <apex:column value="{!att.Id}"/>
        <apex:column value="{!att.ParentId}"/> 
    </apex:pageBlockTable>

</apex:repeat>
1

Personally, I would utilize a simple class structure to iterate through on the vf page, though maps could also accomplish this. I made a simple example below of how I would go about this.

public class MyController{
    List<AttachmentCallsWrapper> att_calls {get;set;}

    public MyController(){
        this.att_calls = new List<AttachmentCallsWrapper>();
        //fill list
    }

    public class AttachmentCallsWrapper {
        public Call__c call {get;set;}
        public Attachment att {get;set;}

        public AttachmentCallsWrapper(Attachment a, Call__c c){
            this.call = Call__c;
            this.att = a;
        }
    }
}

<apex:repeat value="{!att_calls}" var="ac">
    <apex:outputText value="{!ac.call.Field}"/>
    <apex:outputText value="{!ac.att.Field}"/>
</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.