function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
tengeltengel 

List of Available Records not Loading

I am modifying this awesome code package from Michael Farrington (http://www.michaelforce.org/recipeView?id=a0G30000006eVxVEAU) to build a VF page that will allow a user to add multiple competitors to an Opportunity at one time. It is SO CLOSE to working, but I have this one final hurdle to cross...

I am using a junction object to attach competitors to Opportunities. My list of options is stored in the Competitor_Option__c object, and then my Competitor__c object has a master-detail to Opportunity, and a lookup to Competitor_Option__c.

The VF page has two sections: Selected Competitors and Available Competitors. Everything is working great EXCEPT that my list of Available Competitors is not returning any results when the page is first loaded. If I hit backspace while in the page's search field, my Competitor_Options__c load. If I Remove a previously added option, the options load. Can anyone help me find what in my code is preventing any options from loading when the page first loads?

User-added image

VF page:
<apex:page standardController="Opportunity" extensions="opportunityProductEntryExtension" >
    <apex:sectionHeader Title="Manage Competitors" subtitle="{!opportunity.Name}"/>
    <apex:messages style="color:red"/>
  
    <style>
        .search{
            font-size:14pt;
            margin-right: 20px;  
        }
        .fyi{
            color:red;
            font-style:italic;
        }
        .label{
            margin-right:10px;
            font-weight:bold;
        }
    </style>
  
    <script type='text/javascript'>
        // This script assists the search bar functionality
        // It will execute a search only after the user has stopped typing for more than 1 second
        // To raise the time between when the user stops typing and the search, edit the following variable:
        var waitTime = 0;
        var countDown = waitTime+1;
        var started = false;
        function resetTimer(){
            countDown=waitTime+1;
            if(started==false){
                started=true;
                runCountDown();
            }
        }
        function runCountDown(){
            countDown--;
            if(countDown<=0){
                fetchResults();
                started=false;
            }
            else{
                window.setTimeout(runCountDown,1000);
            }
        }
    </script>
  
    <apex:form id="form">
        <apex:outputPanel id="mainBody">
<!-- this is the upper table... a.k.a. the "Shopping Cart"-->
            <apex:pageBlock title="Selected Competitors" id="selected">
                <apex:pageblockTable value="{!shoppingCart}" var="s">
                    <apex:column >
                        <apex:commandLink value="Remove" action="{!removeFromShoppingCart}" reRender="selected,searchResults" immediate="true">
                            <!-- this param is how we send an argument to the controller, so it knows which row we clicked 'remove' on -->
                            <apex:param value="{!s.Competitor__c}" assignTo="{!toUnselect}" name="toUnselect"/>
                        </apex:commandLink>
                    </apex:column>
                    <apex:column headerValue="Competitor" value="{!s.Competitor__c}"/>
                    <apex:column headerValue="Incumbent">
                        <apex:inputField value="{!s.Incumbent__c}" />
                    </apex:column>
                    <apex:column headerValue="Winner">
                        <apex:inputField value="{!s.Winner__c}" />
                    </apex:column>
                    <apex:column headerValue="Strengths">
                        <apex:inputField value="{!s.Strengths__c}" style="width:200px; height:100px" required="false"/>
                    </apex:column>
                    <apex:column headerValue="Weaknesses">
                        <apex:inputField value="{!s.Weaknesses__c}" style="width:200px; height:100px" required="false"/>
                    </apex:column>
                    <apex:column headerValue="Competing Producer">
                        <apex:inputField value="{!s.Producer__c}" required="false"/>
                    </apex:column>
                </apex:pageblockTable>
                <apex:pageBlockButtons >
                    <apex:commandButton action="{!onSave}" value="Save"/>
                    <apex:commandButton action="{!onCancel}" value="Cancel" immediate="true"/>
                </apex:pageBlockButtons>
            </apex:pageBlock>
<!-- this is the lower table: search bar and search results -->
            <apex:pageBlock id="block">
                <apex:outputPanel styleClass="search">
                    Search for Competitors:
                </apex:outputPanel>
                <apex:actionRegion renderRegionOnly="false" immediate="true" id="region">
                    <apex:actionFunction name="fetchResults" action="{!updateAvailableList}" reRender="searchResults" status="searchStatus" id="function" />
                    <!-- here we invoke the scripting to get out fancy 'no button' search bar to work -->
                    <apex:inputText value="{!searchString}" onkeydown="if(event.keyCode==13){this.blur();}else{resetTimer();}" style="width:300px" id="search" />
                    &nbsp;&nbsp;
                    <i>
                        <!-- actionStatus component makes it easy to let the user know when a search is underway -->
                        <apex:actionStatus id="searchStatus" startText="searching..." stopText=" "/>
                    </i>
                </apex:actionRegion>
                <br/>
                <br/>
                <apex:outputPanel id="searchResults">
                    <apex:pageBlockTable value="{!AvailableProducts}" var="a">
                        <apex:column width="100px;">
                            <!-- command button in a column... neato -->
                            <apex:commandButton value="Select" action="{!addToShoppingCart}" reRender="selected,searchResults" immediate="true">
                                <!-- again we use apex:param to be able to tell the controller which row we are working with -->
                                <apex:param value="{!a.Id}" assignTo="{!toSelect}" name="toSelect"/>
                            </apex:commandButton>
                        </apex:column>
                        <apex:column headerValue="Competitor" value="{!a.Name}" width="300px;" />
                        <apex:column headerValue="Type" value="{!a.Competitor_Type__c}" />
                    </apex:pageBlockTable>
                    <!-- We put up a warning if results exceed 100 rows -->
                    <apex:outputPanel styleClass="fyi" rendered="{!overLimit}">
                        <br/>
                        Your search returned over 100 results, use a more specific search string if you do not see the desired Competitor.
                        <br/>
                    </apex:outputPanel>
                </apex:outputPanel>
            </apex:pageBlock>
        </apex:outputPanel>
    </apex:form>
</apex:page>





Apex extension:
public with sharing class opportunityProductEntryExtension {

    public Opportunity theOpp {get;set;}
    public String searchString {get;set;}
    public Competitor__c[] shoppingCart {get;set;}
    public Competitor_Option__c[] AvailableProducts {get;set;}
  
    public String toSelect {get; set;}
    public String toUnselect {get; set;}
    public Decimal Total {get;set;}
  
    public Boolean overLimit {get;set;}
  
    private Competitor__c[] forDeletion = new Competitor__c[]{};

    public opportunityProductEntryExtension(ApexPages.StandardController controller) {

        theOpp = [select Id from Opportunity where Id = :controller.getRecord().Id limit 1];
      
        // If products were previously selected need to put them in the "selected products" section to start with
        shoppingCart = [select Id, Name, Incumbent__c, Winner__c, Strengths__c, Weaknesses__c, Producer__c, Competitor__c from Competitor__c where Opportunity__c=:theOpp.Id order by Id Asc];

    }

    public void updateAvailableList() {
  
        // We dynamically build a query string and exclude items already in the shopping cart
        String qString = 'select Id, Name, Competitor_Type__c from Competitor_Option__c where Active__c=true';
      
        // note that we are looking for the search string entered by the user in the name OR description
        // modify this to search other fields if desired
      
        if(searchString!=null){
            qString+= ' and (Name like \'%' + searchString + '%\')';
        }
      
        Set<Id> selectedEntries = new Set<Id>();
        for(Competitor__c d:shoppingCart){
            selectedEntries.add(d.Competitor__c);
        }
      
        if(selectedEntries.size()>0){
            String tempFilter = ' and Id not in (';
            for(Id i : selectedEntries){
                tempFilter+= '\'' + (String)i + '\',';
            }
            String extraFilter = tempFilter.substring(0,tempFilter.length()-1);
            extraFilter+= ')';
          
            qString+= extraFilter;
        }
      
        qString+= ' order by Name';
        qString+= ' limit 101';
      
        system.debug('qString:' +qString);      
        AvailableProducts = database.query(qString);
      
        // We only display up to 100 results... if there are more than we let the user know (see vf page)
        if(AvailableProducts.size()==101){
            AvailableProducts.remove(100);
            overLimit = true;
        }
        else{
            overLimit=false;
        }
    }
  
    public void addToShoppingCart(){
  
        // This function runs when a user hits "select" button next to a product
  
        for(Competitor_Option__c d : AvailableProducts){
            if((String)d.Id==toSelect){
                shoppingCart.add(new Competitor__c(Opportunity__c=theOpp.Id, Competitor__c=d.Id));
                break;
            }
        }
      
        updateAvailableList();
    }
  

    public PageReference removeFromShoppingCart(){
  
        // This function runs when a user hits "remove" on an item in the "Selected Products" section
  
        Integer count = 0;
  
        for(Competitor__c d : shoppingCart){
            if((String)d.Competitor__c==toUnselect){
          
                if(d.Competitor__c!=null)
                    forDeletion.add(d);
          
                shoppingCart.remove(count);
                break;
            }
            count++;
        }
      
        updateAvailableList();
      
        return null;
    }
  
    public PageReference onSave(){
  
        // If previously selected products are now removed, we need to delete them
        if(forDeletion.size()>0)
            delete(forDeletion);
  
        // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here
        try{
            if(shoppingCart.size()>0)
                upsert(shoppingCart);
        }
        catch(Exception e){
            ApexPages.addMessages(e);
            return null;
        }
         
        // After save return the user to the Opportunity
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
  
    public PageReference onCancel(){

        // If user hits cancel we commit no changes and return them to the Opportunity 
        return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id'));
    }
 
}

tengeltengel
Image of page, for further clarification:

User-added image