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
Javier CGJavier CG 

How to update records on VF page with pagination?

I have a VF page with a custom controller that uses a standard set controller to get records of one custom object. The page uses pagination to show the records.
The controller loads the page records in a list that is showed on the VF page.
Now I have to change the page to let the users update one field of some records, but when I try to use the buttons for pagination, always receive an error:

Modified rows exist in the records collection!
External entry point


Even when I haven't changed any record.
The error raises in the .next() method of the standard set controller.
I have tried to call the .save() method of the standard set controller before calling the next() method with the same result.

This is a snippet of the VF page:
<apex:page controller="myCustomController" docType="html-5.0" tabStyle="mycustomObject__c" sidebar="false">
   <apex:sectionHeader title="Prices" subtitle="Prices" />
    <apex:form id="theForm">
        <apex:pageBlock title="Precios" rendered="{!RecordList.size!=0}" id="pbId" >
        <apex:pageBlockTable value="{!RecordList}" var="rec">
            <apex:column >
                <apex:facet name="header">Name</apex:facet>
                <apex:outputText value="{!rec.Name}"/>
            </apex:column>
            <apex:column >
                <apex:facet name="header">Ammount</apex:facet>
                <apex:outputText value="{!rec.Ammount__c}"/>
            </apex:column>     
        </apex:pageBlockTable> 
       <apex:outputPanel style="text-align:center;" layout="block">
          <apex:commandButton value="First" reRender="pbId" action="{!first}" disabled="{!NOT(hasPrevious)}" status="paginationStatusBottom"/>
          <apex:commandButton value="Previous" rerender="pbId" action="{!previous}" disabled="{!NOT(hasPrevious)}" status="paginationStatusBottom"/>&nbsp;Page {!pageNumber} of {!totalPages}&nbsp;
          <apex:commandButton value="Next" rerender="pbId" action="{!next}" disabled="{!NOT(hasNext)}" status="paginationStatusBottom"/>
          <apex:commandButton value="Last" rerender="pbId" action="{!last}" disabled="{!NOT(hasNext)}" status="paginationStatusBottom"/>
          <apex:actionStatus id="paginationStatusBottom">
             <apex:facet name="start">
                 Please wait...<img src="/img/loading32.gif" style="width: 18px;"/>
             </apex:facet>
          </apex:actionStatus>
       </apex:outputPanel>
 </apex:pageBlock>
 </apex:form>
</apex:page>
And this is a snippet of the custom controller:
public with sharing class myCustomController
{ 
    Public Integer size{get;set;}
    Public Integer noOfRecords{get; set;}
    public List<SelectOption> paginationSizeOptions{get;set;}
    public static final Integer PAGE_SIZE = 50;

    public List<mycustomObject__c> RecordList {get;set;}

    public myCustomController()
    {
        init();
    }
    
    public void init()
    {
        RecordList = (List<mycustomObject__c>)setCon.getRecords();
    }
    
    public ApexPages.StandardSetController setCon
    {
        get
        {
            if(setCon == null)
            {
                setCon = new ApexPages.StandardSetController(Database.getQueryLocator('select Id, name, Ammount from mycustomObject__c'));                
                setCon.setPageSize(PAGE_SIZE);
            }
            return setCon;
        }
        set;
    }
        
    public Boolean hasNext
    {
        get
        {
            return setCon.getHasNext();
        }
        set;
    }
    
    public Boolean hasPrevious
    {
        get
        {
            return setCon.getHasPrevious();
        }
        set;
    }
    
    public Integer pageNumber
    {
        get
        {
            return setCon.getPageNumber();
        }
        set;
    }
    
    Public Integer getTotalPages()
    {
        Decimal totalSize = setCon.getResultSize();
        Decimal pageSize = setCon.getPageSize();
        Decimal pages = totalSize/pageSize;
        return (Integer)pages.round(System.RoundingMode.CEILING);
    }
    
    public void first()
    {
        setCon.save();
        setCon.first();
        init();
    }
    
    public void last()
    {
        setCon.save();
        setCon.last();
        init();
    }
    
    public void previous()
    {
        setCon.save();
        setCon.previous();
        init();
    }
    
    public void next()
    {
        setCon.save();
        setCon.next();
        init();
    }
}

If I change the <apex:inputText on the VF page by an <apex:outputText the page works well (but the user can´t change the field). But with outputText the error is always launched when I push any pagination button, even without any change on the record list.
Some suggestion to fix the problem?

Regards
Agustin BAgustin B
Hi javier, try this approach:
https://salesforce.stackexchange.com/questions/222669/modified-rows-exist-in-the-records-collection-in-visualforce-using-inputtextar

If it helps please mark as correct as it may help others asking the same.
Javier CGJavier CG
Thanks for your answer Agustin

Yes, I had tried that approach, as you will see in the code above, but it doesn´t work for me either.
Finally, I have managed to do it with:
  1. Creating a wrap class in the controller to store the custom records and an extra variable to store the changed value of the field. I use this variable to show in the VF page instead of the current field value in the record.
  2. Creating a map of the wrap class in the controller to store the changed records.
  3. When I want to update the changes in SF, I add all the records in the map into a list, replacing the field value with the variable value and then update the list.
I don´t know if there is any way to get this done easier, but this works for me now.
Agustin BAgustin B
HI Javier, thats a good workaround, you are not even wasting any DML statement. I think you have a good option working there.