1

I am trying to create a VisualForce page that display 3 objects. I want it in a way where I want to see a parent and all child records underneath. I tried using a nested repeater however It displays the Parent and all children records even if it is not related to that parent.

I researched the best way to handle what I am trying to accomplish and I think using a repeater with lists is the best approach but again it is displaying the parent correctly, but the child records are displaying under the parent even if it is not related to the parent.

Controller:

public class G_SPRFormExtension {

Private SBQQ__Quote__c soQuote {get;set;}

public  List<Host__c> lstHost {get;set;}

public List<Asset> lstHost_Assets {get;set;}
Private Set<Id> idHostAsset = new Set<Id>();
//public List<Contact> cont{get;set;}

 Public String getAccountID() {
    return soQuote.SBQQ__Account__c;
}

Public G_SPRFormExtension(ApexPages.StandardController stdController) 
{
     system.debug(logginglevel.debug, 'DEBUG: START CONSTRUCTOR');

    // Get Case ID number 
    This.soQuote = (SBQQ__Quote__c)stdController.getRecord(); 

    //Get the Case Values for this record
    soQuote = [Select Id, Name, SBQQ__Account__c
               From SBQQ__Quote__c 
                Where Id = :This.soQuote.Id]; 

     system.debug(logginglevel.debug, 'DEBUG: soQuote' + soQuote );

    GetHosts();

}

Public Void GetHosts()
{

    lstHost = [Select Id, Name
                 From Host__c 
                    Where Location__c  = :soQuote.SBQQ__Account__c];

      system.debug(logginglevel.debug, 'DEBUG: lstHost     ' + lstHost);

       for (Host__c soHost : lstHost){
        idHostAsset.add(soHost.Id);
    }

     GetHostAssets();

}

Public Void GetHostAssets()
{
    system.debug(logginglevel.debug, 'DEBUG: idHostAsset' + idHostAsset );

    lstHost_Assets = [Select Id, Name
                 From Asset 
                    Where Host__c  = :idHostAsset];

}

}

Page:

<apex:page standardController="SBQQ__Quote__c" extensions="G_SPRFormExtension" >
<apex:pageBlock>
        <apex:pageblocksection columns="2">

            <apex:outputField value="{!SBQQ__Quote__c.name}"/>

        </apex:pageBlockSection>



<apex:repeat var="a" value="{!lstHost}">


<apex:pageBlock title="Host">
    <apex:pageblocksection columns="2">
         <apex:outputPanel id="Host">
                    <apex:outputText value="{!a.Name}"/>

                    <apex:repeat var="asset" value="{!lstHost_Assets}">
                        <apex:outputText value="{!asset.Name}"/>
                     </apex:repeat>

         </apex:outputPanel>  
      </apex:pageBlockSection>
 </apex:pageBlock>

 </apex:repeat>

</apex:pageBlock>
</apex:page>

2 Answers 2

2

This should help you out. I chose to use a wrapper that would connect all of your assets by each host that you have listed under this quote. See Below:

Page:

    <apex:page standardController="SBQQ__Quote__c" extensions="G_SPRFormExtension" >

        <apex:pageBlock mode="mainDetail" title="{!SBQQ__Quote__c.Name}">

            <apex:repeat value="{!assetList}" var="host" >

                <apex:pageBlockSection columns="2" title="{!host.host.Name}">

                    <apex:pageBlockTable value="{!host.assets}" var="asset" title="{!host.host.Name} Assets">
                        <apex:column value={!asset.Name}" />
                    </apex:pageBlockTable>

                </apex:pageBlockSection>

            </apex:repeat>

        </apex:pageBlock>
    </apex:page>

Extension:

    public class G_SPRFormExtension {

        public SBQQ__Quote__c soQuote   { get; set; }
        public List<Host__c> lstHost    { get; set; }

        private Map<String, Host__c> hostMap        { get; set; }

        public List<QuoteAssets> assetList          { get; set; }
        private Map<String, List<Asset>> assetMap   { get; set; }


        public String getAccountId(){
            return soQuote.SBQQ__Account__c;
        }

        public G_SPRFormExtension( ApexPages.StandardController controller ){
            this.soQuote = ( SBQQ__Quote__c )controller.getRecord();

            soQuote = [ SELECT Id, Name, SBQQ__Account__c
                        FROM SBQQ__Quote__c
                        WHERE Id =: this.soQuote.Id ];

            this.assetList = new List<QuoteAssets>();

            List<Host__c> hosts = getHosts();
            hostMap = new Map<String, Host__c>();

            if(hosts != null && !hosts.isEmpty() ){
                hostMap.putAll( hosts );

                assetMap = getAssetsMapByHostId( hostMap.keySet() );

            }

            buildAssetMap();

        }

        public List<Host__c> getHosts(){

            List<Host__c> results;
            Map<String, Schema.SObjectField> hostFields = Host__c.getSObjectType().getDescribe().fields.getMap();

            String qry = 'SELECT ' + String.join( new List<String>( hostFields.keySet() ), ',' ) 
                        + ' FROM Host__c '
                        + ' WHERE Location__c = \'' + this.soQuote.SBQQ__Account__c + '\'';

            try{
                results = Database.query( qry );
            }
            catch( Exception ex ){
                System.debug( 'Error fetching Hosts: ' + ex.getMessage() );
            }

            return results;     
        }

        public Map<String, List<Asset>> getAssetsMapByHostId( Set<String> hostIds ){

            List<Asset> assets;
            Map<String, Schema.SObjectField> assetFields = Asset.getSObjectType().getDescribe().fields.getMap();

            String qry = 'SELECT ' + String.join( new List<String>( assetFields.keySet() ), ',' )
                        + ' FROM Asset '
                        + ' WHERE Host__c IN : hostIds';

            try{
                assets = Database.query( qry );
            }
            catch( Exception ex ){
                System.debug( 'Error fetching assets: ' + ex.getMessage() );
            }
            Map<String, List<Asset>> results = new Map<String, List<Asset>>();

            if( assets != null && !assets.isEmpty() ){

                for( Asset asset : assets ){
                    String index = asset.Host__c;

                    List<Asset> temp;
                    if( results.containsKey( index )){
                        temp = results.get( index );
                    }
                    else {
                        temp = new List<Asset>();
                    }

                    temp.ass( asset );

                    results.put( index, temp );
                }           
            }

            return results;
        }

        public void buildAssetMap(){

            if( this.assetList == null ){
                this.assetList = new List<QuoteAssets>();
            }

            if( hostMap != null && !hostMap.values().isEmpty() ){

                for( String hostId : hostMap.keySet() ){
                    QuoteAssets hostWrapper = new QuoteAssets();

                    List<Asset> assets = ( assetMap != null && assetMap.containsKey( hostId )
                                                ? assetMap.get( hostId )
                                                : null;
                    hostWrapper.assets = ( assets != null ? assets : new List<Asset>() );
                    hostWrapper.host = hostMap.get( hostId );

                    this.assetList.add( hostWrapper );
                }
            }
        }

        public class QuoteAssets {

            public Host__c host     { get; set; }
            public List<Asset> assets       { get; set; }

        }
    }

Using this helps reduce the complexity of what your layout needs to handle when nesting your repeats and reduces the time that it should take to build the view. I hope this helps. Cheers!

2
  • I like it. However what happens if I need additional columns for Host?
    – Brooke
    Commented Jul 2, 2018 at 12:35
  • Then since you've queried all columns in the back end in this example.. just put your columns in the page. All you will need is to output them above the page lock table for the assets. They too cna be repeated in a page block section of their own. Doing that way would make each section appear to have host details in a ph section and your host assets will then appear as a related list to the host, as desired. Later tonight I can provide a simple example with different object in a screenshot to possibly provide more visual, if you'd like?
    – Ronnie
    Commented Jul 3, 2018 at 0:24
0

The problem is that you have all assets for all hosts in a single list. One approach is to use a wrapper class so you can manage the host record and its own list of assets:

public class HostWrapper {
    public Host__c host { get; set; }
    List<Asset> assets { get; set; }

    public HostWrapper(Host__c host, List<Asset> assets) {
        this.host = host;
        this.assets = assets;
    }
}

You iterate through your host records and their assets to build a list of HostWrappers.

List<HostWrapper> hostWrappers = new List<HostWrapper>();
for (Host__c host : [SELECT Name, (SELECT Name FROM Assets) FROM Host__c]) {
    HostWrappers.add(new HostWrapper(host, host.Assets));
}

Then you can do you nested repeat in Visualforce. The outer repeat iterates over the HostWrappers list, and the inner repeat iterates over the List of assets for the specific wrapper.

<apex:repeat value="{!hostWrappers}" var="hw">
    The Host name is {!hw.host.Name}
    <!-- other host-related info here -->

    <apex:repeat value="{!hw.assets}" var="asset">
        The Asset name is {!asset.Name}
        <!-- other asset-related info here -->
    </apex:repeat>

</apex:repeat>
1
  • Yes, That is correct however when I did it this way the spacing if adding more was an issue for me. I thought that if I added it within a Pageblock or something it would be better spacing.
    – Brooke
    Commented Jul 2, 2018 at 12:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .