You need to sign in to do that
Don't have an account?
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?
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
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.
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
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
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
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,
Thanks, it fixed the issue
Cool. I am glad it worked for you.
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?
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.
Can you please send me the code.
I am getting similar issue
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>