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
awilawil 

nested repeat grid-style editor - binding inputFields to objects in Apex

I'm having trouble binding a grid-style editable input form and using a "save" button to upsert the users' edits back to the database. I used a nested repeat in my VF code and a sub class for iterating through the data to build the grid. I used Ron Hess' advice from this post:
http://forums.sforce.com/sforce/board/message?board.id=Visualforce&message.id=668

The page should iterate through product families, and then display (for the current opportunity) each existing OpportunityLineItem, giving the user capability to edit quantity and sales price. On clicking save, the Apex attempts to iterate through the families and upsert all the OpportunityLineItems... but fails to do so.

The code displays the table correctly (including existing values for quantity and sales price), but the save button does not commit changes back to the database. I added debug statements to my save button, and found that the users' typed edits in the input fields are not being reflected when the debug statements are shown in the log. I am interpreting this to mean that I have not successfully bound the input fields back to the objects I am trying to upsert (or the ones I am dumping in the debug statments). Here is my code:

VF Code:
<apex:page tabStyle="Opportunity" controller="addOpportunityProductsController">
  <apex:sectionHeader title="Products Wizard" subtitle="Step 1"/>
  <apex:form >        
      <apex:pageBlock title="Available Products">

        <apex:pageBlockButtons >
            <apex:commandButton action="{!saveChanges}" value="Save"
            rerender="main" status="ajaxStatus" />
        </apex:pageBlockButtons>

        <apex:pageBlockSection title="Opportunity Information"> 
            <apex:panelGrid columns="2"> 
                <apex:outputText value="Opportunity Name:"/> 
                <apex:outputText value="{!opportunity.name}"/> 
            </apex:panelGrid> 
        </apex:pageBlockSection>
        
            <apex:repeat value="{!families}" var="family" id="theSection">
                <apex:pageBlockSection title="{!family.name}">

  <apex:actionStatus id="ajaxStatus" startText="Updating schedules...">        

   <apex:facet name="stop">
   <apex:outputPanel id="main">
    <table border="1" width="100%">
    <tr>
     <apex:repeat value="{!headers}" var="h">
      <td class="hdr">{!h}</td>
     </apex:repeat>
    </tr>
    <apex:repeat value="{!family.oppLineItems}" var="o">
     <tr>
      <td><apex:inputField value="{!o.Id}" /></td>
      <td><apex:outputText value="{!o.PriceBookEntry.Product2.Name}" /></td>
      <td><apex:inputField value="{!o.Quantity}" /></td>
      <td><apex:inputField value="{!o.UnitPrice}" /></td>
     </tr>
    </apex:repeat>
    </table>
   </apex:outputPanel>
   </apex:facet>
  </apex:actionStatus>
                </apex:pageBlockSection>
            </apex:repeat>
 </apex:pageBlock> 
 </apex:form>

</apex:page>

 And my Apex Code:
Code:
public class addOpportunityProductsController {

  // Class Variables
   Opportunity opportunity;
   //flag1 is a custom field on Opportunity and also Product2 used to filter which products are applicable for this kind (flag1) of opportunity
   private String flag1;
   private String opportunity_id;
   OpportunityLineItem[] oppLineItems;
   public List<myFamily> mfmlys = new List<myFamily>();
      public List<myFamily> families = new List<myFamily>();
         
  // Constructor
 public addOpportunityProductsController() {
  //this.opportunity = (Opportunity)controller.getSubject();
     this.opportunity_id = System.currentPageReference().getParameters().get('opportunity_id');  
     this.flag1 = System.currentPageReference().getParameters().get('flag1');
     this.oppLineItems = [ SELECT  oli.Id, oli.PriceBookEntry.Product2.Name, oli.Quantity, oli.UnitPrice
      FROM OpportunityLineItem oli 
      WHERE oli.opportunityId = :opportunity_id ];
 }

    public class myFamily {
        private String name;
        private List<Product2> prods = new List<Product2>();
        private List<OpportunityLineItem> oppLineItems = new List<OpportunityLineItem>();
        //flag1 is a custom field on Opportunity and also Product2 used to filter which products are applicable for this kind (flag1) of opportunity
        String flag1 = System.currentPageReference().getParameters().get('flag1');
        String opportunity_id = System.currentPageReference().getParameters().get('opportunity_id');  
     
        public List<Product2> getProds() {
            this.prods = [Select Id, Name from Product2 where Product2.flag1__c = :flag1 and Family = :name];
            return prods; 
        }

        public List<OpportunityLineItem> getOppLineItems() {
            List<OpportunityLineItem> oppLineItems = [ SELECT 
              oli.Id, oli.PriceBookEntry.Product2.Name, oli.Quantity, oli.UnitPrice
              FROM OpportunityLineItem oli 
              WHERE oli.opportunityId = :opportunity_id 
              AND oli.PriceBookEntry.Product2.Family = :name];
            return oppLineItems;
        }
        
        public String getName() { return name; }         

    }

    public List<myFamily> getFamilies() {

        Schema.DescribeFieldResult productFamilyField = Schema.sObjectType.Product2.fields.Family;
        List<Schema.PicklistEntry> productFamilyValues = productFamilyField.getPickListValues();

        for(Schema.PicklistEntry entry:productFamilyValues){
            System.Debug('entry is ' + entry.getLabel());
            myFamily mfmly = new myFamily();
            mfmly.name = entry.getLabel();
            mfmly.oppLineItems = mfmly.getOppLineItems();
              families.add(mfmly);
            mfmlys.add(mfmly);

        }
           return families;
    }

    public Opportunity getOpportunity() { 
        return [select id, name from Opportunity where id=: opportunity_id];
    }
        
         // Action Method called from page button
         public pagereference saveChanges() { 
                System.debug('----------1------------');
                  for (myFamily m : families ) {
                    System.debug('----------2------------');
                    System.debug('FAMILY NAME IS ' + m.name);
                    for (OpportunityLineItem o : m.oppLineItems) {
                        System.debug('NAME IS ' + o.PriceBookEntry.Product2.Name + '; QUANTITY IS ' + o.Quantity);
                    }
                    upsert (m.oppLineItems);

            }
            return null;
           /* PageReference opptyPage = new PageReference('/' + opportunity_id);
                                opptyPage.setRedirect(true);
                                return opptyPage;
*/
         }

 // public Getter to provide table headers 
 public string[] getheaders() { 
   return new string [] {'Id','Name','Quantity','Sales Price'} ;
 }
 
 public opportunityLineItem[] getOppLineItems() { 
  return this.oppLineItems; 
 } 

}