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
crop1645crop1645 

VF pagination - 'Modified rows exist in the records collection' with StandardSetController next()

I've written a custom controller that exploits the StandardSetController class to return a page of records and allow pagination.

 

VF controller has three (relevant) methods:

 

getPage()  - returns List of records in current pageset (pagesize = 10)

previousPage() - executes the previous() method on the standard set controlller object

nextPage() - executes the next() method on the standard set controller object

 

The StandardSetController is constructed using a Database.getQueryLocator per the doc

 

setCtlr = new ApexPages.StandardSetController(

Database.getQueryLocator(

[Select id, name, Account__r.name from Foo__c where id in :fooIdSet]));

When I set up my APEX test method:

 

1.  Insert 11 rows into database  //works ok

2.  Assert that getPage() returns 10 rows  // assertion passes

3.  Execute nextPage() on the controller

4.  Assert that getPage() returns 1 row (the 11th)

 

Here's the problem: step #3 fails because VF comes back with:

 

System.VisualforceException: Modified rows exist in the records collection!

 

I checked the creation datetime and lastmodify datetime just prior to executing Step3 and all is OK, the records haven't mysteriously been changed underneath.

 

I get the same error (in a non-Sites test whilst logged in as admin and simply executing the VF page in the browser.

 

What would cause this error?

Message Edited by crop1645 on 04-08-2009 05:12 PM
Message Edited by crop1645 on 04-08-2009 05:14 PM
Best Answer chosen by Admin (Salesforce Developers) 
crop1645crop1645

SOLVED

 

I should have read the error message more explicitly:

'Modified rows exist in the records collection'

This error means that the records collection in your VF controller's memory have been modified, not that the underlying records in the database have been modified between paging requests.

 

And, in fact, I was post-processing the Foo__c.name field in my controller to 'friendlify' it before showing to the user on the VF page.  Since the StandardSetController only works with pagination on a Database.getQueryLocator object, you can't use a List<customClass> to hold your friendlified field values.

 

So, the only alternative I came up with were additional Formula fields on Foo__c - which do the friendlification before the records are made available to the controller.

 

All Answers

crop1645crop1645

SOLVED

 

I should have read the error message more explicitly:

'Modified rows exist in the records collection'

This error means that the records collection in your VF controller's memory have been modified, not that the underlying records in the database have been modified between paging requests.

 

And, in fact, I was post-processing the Foo__c.name field in my controller to 'friendlify' it before showing to the user on the VF page.  Since the StandardSetController only works with pagination on a Database.getQueryLocator object, you can't use a List<customClass> to hold your friendlified field values.

 

So, the only alternative I came up with were additional Formula fields on Foo__c - which do the friendlification before the records are made available to the controller.

 

This was selected as the best answer
Swarup VSwarup V

Eric,

i have also got same error when i am moving on next page after updating values(selected values using check box) in first page.can u provide me some sample code for this resolution.

 

thanks

sfdevloper 

chandan.maruthichandan.maruthi

Hi,

 

I got this error "Modified rows exist in the records collection!". Can anyone help me with this issue?

 

 crop1645, Can you please explain what do you mean by friendlification?

 

Thanks & Regards,

 

Chandan

x-men-CRMx-men-CRM

Hi Eric,

 

I am doomed figuring out a solution for the same.

could you please send in the solution . It's pretty urgent

 

thanks in advance,

Priaynka

PaqsPaqs

The best way to "Friendlify" in that case, is to simply make sure you use the Controller's .save() method and not try to update the underlying SObjects in the list which is what will give you that error on next.

 

Basically in terms of overall steps:

 

Step 1. controller.getRecords() -> To return the records into the List<SObject> for display in VF with the respective inputfield tags for the updatable values.

 

Step 2.<.... do your pagination and record update on the VF page...>

 

Step 3. Calling controller.next(); requires you to insure that there were no changes and if there were, you should check by looking at the return of controller.getRecord() which will return something if there is a change somewhere in the record set. In which case, you call controller.save() and then controller.next()

 

This will fix the issue listed in the post..

 

Cheers,

 

 

sarathsarath

Thanks, it fixed the issue

PaqsPaqs

Cool. I am glad it worked for you.

calvin_nrcalvin_nr

When I try the con.save() solution, I get Insufficient Privileges error.

 

Does con.save() make these changes back into the actual object records themselves?

Larry LeonidasLarry Leonidas

This was Definitely the FIX. But I notice the Save() didn't perform the DML Update operation. Is Save() only to save the controller state or to perform the DML? Apex Docs stated below:

 

Inserts new records or updates existing records that have been changed. After this operation is finished, it returns a PageReference to the original page, if known, or the home page.

Srikanth44Srikanth44

Can you please send me the code.

I am getting similar issue

ashoknaglikar1.3945534997003162E12ashoknaglikar1.3945534997003162E12
hi @Paqs , Its not working in my case . I am still getting the same error message? can you paste your code please
Shruti VishShruti Vish
Hi All,
Even me getting this error "Modified rows exist in the records collection!" .Please can anyone help me for this Below is my code

global class TaskUpdateController {
    
    public List<Task> tasklst{get;set;}
    public String userId { get; set; }
    public  Set<String> OwnerIdSet {get;set;}
    public Set<String> OppIdSet{get;set;}
    public List<Opportunity> listOpp{get;set;}
    public List<String> sendTo {get;set;}
    public Set<String> setToaddress{get;set;}
    public Map<String,String> ownerMap{get;set;}
    Public static  Integer size{get;set;} 
    Public Integer noOfRecords{get; set;} 
    public integer OffsetSize = 0;
    public List<SelectOption> paginationSizeOptions{get;set;}
    
   
   
    public  TaskUpdateController(){
        tasklst=new List<Task>();
         size=20;
        paginationSizeOptions = new List<SelectOption>();
        paginationSizeOptions.add(new SelectOption('20','20'));
        paginationSizeOptions.add(new SelectOption('50','50'));
        paginationSizeOptions.add(new SelectOption('100','100'));
               userId  = ApexPages.CurrentPage().getparameters().get('userid');

        System.debug('USerInfo in apex'+userId);
        
        //String userId =ApexPages.currentPage().getParameters().get('userid');
        //System.debug('UuserId  !!!!!!!!!!!'+userId); 
        sendTo = new List<String>();
        ownerMap = new Map<String,String>();
        OwnerIdSet =new Set<String>();
        OppIdSet   = new Set<String>();
        setToaddress = new Set<String>();
        listOpp =[Select id, Owner.Email,OwnerId  FROM Opportunity ];
        system.debug('listOpp size***'+listOpp.size());
        try{
            if(listOpp.size() > 0){
                System.debug('Inside if');
                for(Integer i=0; i< listOpp.size();i++){
                    OwnerIdSet.add(listOpp[i].OwnerId);
                    setToaddress.add(listOpp[i].Owner.Email);
                    OppIdSet.add(listOpp[i].Id);
                    
                    if(!ownerMap.containsKey(listOpp[i].Owner.Email)){
                        ownerMap.put(listOpp[i].Owner.Email,listOpp[i].OwnerId);
                    }
                }
                system.debug('OwnerIdSet***'+OwnerIdSet);
                System.debug('OppIdSet************'+OppIdSet);
                sendTo.addAll(setToaddress);
                
              // if(sendTo != null)
                    //sendEmailFunction(sendTo);
               //getTaskList();
            }  
        }
      
        catch(Exception e){
            System.debug('Exception Occured'+e.getLineNumber());
        }
        
    }
   
  public void getTaskList(){
        //System.debug('USerInfo'+userid);
       // this.userId = Apexpages.currentPage().getParameters().get('userid');
        
        System.debug('userId >>>>> ' + userId);
        System.debug('OwnerIdSet!!!!!!!!!!!!'+OwnerIdSet);
        System.debug('OppIdSet!!!!!!!'+OppIdSet);
        tasklst=new List<Task>();
        tasklst=[SELECT Id,Subject, Status, ActivityDate,OwnerId,
                 Owner.Name,Owner.Email,What.Name,LastModifiedById   FROM Task  Where WHATID IN:OppIdSet AND
                 OwnerId IN: OwnerIdSet AND OwnerId =:userId LIMIT:size];
        System.debug('tasklst!!!!!!!!!Size!!!!!!'+tasklst.size());
        System.debug('tasklst!!!!!!!!!details!!!!!!'+tasklst);
        
    }
     public ApexPages.StandardSetController setCon {
        get {
            if(setCon == null) {                
                setCon = new ApexPages.StandardSetController(Database.getQueryLocator(
                    [SELECT Id,Subject, Status, ActivityDate, OwnerId,
                     Owner.Name,Owner.Email,What.Name FROM Task  Where WHATID IN:OppIdSet AND
                     OwnerId IN: OwnerIdSet AND OwnerId =:userId]));
                setCon.setPageSize(size);  
                noOfRecords = setCon.getResultSize();
            }            
            return setCon;
        }
        private set;
    }
    //Changes the size of pagination
    public PageReference refreshPageSize() {
         setCon.setPageSize(size);
         return null;
    }
 
    // Initialize setCon and return a list of record    
     public List<Task> getTasks() {
        System.debug('sdasd'+(List<Task>) setCon.getRecords());
         return (List<Task>) setCon.getRecords();
    }   
  

   public void save() {
        List<Task> lstTask =new List<Task>();
        lstTask =setCon.getRecords();
        System.debug('lstTask'+lstTask);
       try{
           if(!lstTask.isEmpty())
               update lstTask; 
       }
        catch(Exception ex){
            ApexPages.addMessages(ex);
        }
        
    }
    public void nextFunction(){
        setCon.save();
        setCon.next();
    }
    public void prevFunction(){
        setCon.save();
        setCon.previous();
    }

}

VF Page
<apex:page controller="TaskUpdateController">
    
  
    <apex:form >
        <!--apex:actionFunction name="setUserLocation" action="{!getTaskList}" rerender="">
<apex:param name="userid" value="" />
</apex:actionFunction-->
      <apex:actionFunction name="refreshPageSize" action="{!refreshPageSize}" status="fetchStatus" reRender="pbId"/>

        <apex:pageBlock mode="inlineEdit"   id="pbId">
            
            <apex:pageMessages id="showmsg"></apex:pageMessages>
            <apex:pageBlockButtons >
                <apex:commandbutton value="save" action="{!save}"/>
                <apex:commandButton value="reset" action="{!reset}" ></apex:commandButton>
                
            </apex:pageBlockButtons>
            <!-- calling getAccounts() methods in pageblock table -->
            <apex:pageBlockTable value="{!Tasks}" var="task">
                <apex:column Headervalue="Id">
                    <apex:outputfield value="{!task.Id}"/>
                </apex:column>
                <apex:column Headervalue="Opportunity Name">
                    <apex:outputfield value="{!task.What.Name}"/>
                </apex:column>
                <apex:column Headervalue="Owner">
                    <apex:outputfield value="{!task.Owner.Name}"/>
                </apex:column>
                
                <apex:column headervalue="Completion Date">
                    <apex:outputfield value="{!task.ActivityDate}">
                    </apex:outputfield>
                </apex:column>
              
            </apex:pageBlockTable>
           <apex:panelGrid columns="8"> 
                 
                <apex:selectList value="{!size}" multiselect="false" size="1" onchange="refreshPageSize();">
                    <apex:selectOptions value="{!paginationSizeOptions}"/>
                </apex:selectList>
                 
                <!--apex:commandButton status="fetchStatus" reRender="pbId" value="First" action="{!setCon.first}" disabled="{!!setCon.hasPrevious}" title="First Page"/--> 
  
                <apex:commandButton status="fetchStatus" reRender="pbId" value="Previous" action="{!prevFunction}" disabled="{!!setCon.hasPrevious}" title="Previous Page"/> 
  
                <apex:commandButton status="fetchStatus" reRender="pbId" value="Next" action="{!nextFunction}" disabled="{!!setCon.hasNext}" title="Next Page"/> 
  
                <!--apex:commandButton status="fetchStatus" reRender="pbId" value="Last" action="{!setCon.last}" disabled="{!!setCon.hasNext}" title="Last Page"/--> 
  
                <apex:outputText >{!(setCon.pageNumber * size)+1-size}-{!IF((setCon.pageNumber * size)>noOfRecords, noOfRecords,
                     (setCon.pageNumber * size))} of {!noOfRecords}
                </apex:outputText> 
                       
                <apex:outputPanel >                      
                    <apex:actionStatus id="fetchStatus" >
                        <apex:facet name="start" >
                          <img src="/img/loading.gif" />                    
                        </apex:facet>
                    </apex:actionStatus>
                </apex:outputPanel> 
  
            </apex:panelGrid> 
        </apex:pageBlock>
        
    </apex:form>
</apex:page>