You need to sign in to do that
Don't have an account?
dmcheng
View state and system log show different results
Hello. I'm troubleshooting a custom grid editing VF page that was built by someone else. I'm getting conflicting results when I look at View State vs. the system log. Also I'm wondering what commandLink and commandButton do behind the scenes.
The grid displays custom related records to a Case record. There is a delete commandLink on each row. Clicking the delete link does not immediately delete the record - you need to click a Save commandButton to commit all changes and deletions. The Save button is a pageBlockButton.
I have found that if there are 3 or more rows in the grid and I delete one row at or near the top and click Save to commit the changes, one field in all the rows below the deletion is updated with wrong information. However, if I don't click Save, the data looks correct.
The field is Product and it is a lookup field to the Product object.
Example:
Step one:
Row 1, Product is A
Row 2, Product is B
Row 3, Product is C
Row 4, Product is D
Row 5, Product is E
Step two:
I delete Row 2 BUT I DO NOT CLICK SAVE. The screen refreshes and I see, correctly:
Row 1, Product is A
Row 2, Product is C
Row 3, Product is D
Row 4, Product is E
Step three:
Now I click Save and the screen refreshes:
Row 1, Product is A
Row 2, Product is C
Row 3, Product is C
Row 4, Product is D
(Of course the controller is maintaining two lists - one of rows to display, and one of rows for DML deletion.)
Here are things I don't understand:
* In step 2, the view state show 5 items in the related record list, even though the system log shows only 4 items. The system log show each item has the correct Product. Why isn't the view state updated also, especially since I saw the page refresh?
* Why is the delete commandLink rerendering the screen in step 2? Is that typical operation for commandLink? The way it is built, it is calling a public void method in the controller, not a public PageReference, so there is no return statement in it. I have not been able to find any explicit rerender in the controller method.
* I have created a dummy Save button that does nothing. When I click the dummy button immediately after clicking the delete commandLink, I see in the system log that the Product are incorrect as in step 3 above. I can't understand how that happens, since I see the correct values on the screen just before I click the dummy button, and the system log confirms it.
After several hours of removing code sections and testing, I think I found the issue -- when I removed the immediate="true" from the delete commandLink, the problem went away. Makes me curious as to what exactly "immediate" is doing in addition to avoiding field validation.
@sfdcfox - thanks for your sample code, I'll be referring to it when I begin revamping this thing.
All Answers
Can you post the VF and the controller code?
Well the code is a monster. I didn't post it initially because I didn't think anybody would wade through it.
Here is the grid portion of the Visualforce page:
I can't post the whole controller because it exceeds the character limit for a message. Here are the getters/setters, the constructor, and the methods I've been studying -- saveTEST and deleteCaseProductLink. caseProducts is the list of items in the grid.
I think the problem is this:
<apex:column headerValue="{!$ObjectType.CaseProduct__c.fields.Serial__c.label} (or not required)" width="90px">
<apex:inputField id="caseProductSerial" value="{!caseProduct.Serial__c}" style="width:90px"/>
<apex:inputField id="caseProductSerialNotRequired" value="{!caseProduct.Serial_Not_Required__c}"/>
<!-- Row counter incremented here so it is visible to all record types. -->
<apex:variable var="rowNbr" value="{!rowNbr + 1}" />
</apex:column>
You cannot update an apex:variable inside of an iteration component like a pageBlockTable
One way of doing this would be to change this to
<apex:commandLink action="{!deleteCaseProductLink}" immediate="true" value="Delete" onclick="return confirmProductDelete()">
<apex:param name="{!ROW_NBR}" value="{!rowNbr}" />
</apex:commandLink>
<apex:commandLink action="{!deleteCaseProductLink}" immediate="true" value="Delete" onclick="return confirmProductDelete()">
<apex:param name="id" value="{!caseProduct.Product__c}" />
</apex:commandLink>
In the controller
There is a better way of doing this using Map<Id, Product2> but the above should work.
Arizona
Hi dmcheng,
Your logic seems perfectly ok to me. I had got stuck in similar problem some time back and it took hours and hours of debugging & resolving it.
So, I think there could be two approached for resolving this:
1. Delete the record on click of delete link:
- Actually delete the record on click of delete link, and refresh the product list displayed in UI by querying the records.
2. Mark the records for which delete link were clicked and delete them on click of save button:
a) Delete Link action -
- On click of delete link remove the records from that list(which you are already doing.).
- Store the Id's of all the deleted(Not actually deleted) records in a listToDelete.
b) Save button action -
- perform delete operation on the listToDelete.
- Save buttons's last action would be to refresh the product list displayed in UI by querying the records.
Hope this helps.
Just be wary of the Visualforce Map bug when you go about it.
For reference, here's one way how I "delete" items on a page, deferred until save:
You could use map or set as well (thanks to the new features in Apex Code for custom wrappers), which saves the iteration in "delete(lineitem)", but since the cycle count is usually small, I don't see much point in that optimization.
After several hours of removing code sections and testing, I think I found the issue -- when I removed the immediate="true" from the delete commandLink, the problem went away. Makes me curious as to what exactly "immediate" is doing in addition to avoiding field validation.
@sfdcfox - thanks for your sample code, I'll be referring to it when I begin revamping this thing.
@sfdcfox - btw, which map bug are you referring to?
"Unpredicatable ordering of map keys"? Looks like this was fixed in Winter 12:
http://success.salesforce.com/issues_view?id=a1p30000000RmuRAAS
or referencing a map field with outputField or outputLabel?
http://forceguru.blogspot.com/2011/07/binding-values-of-map-on-visualforce.html
Nice that it got resolved.
Thanks for sharing the solution, was curious a bit. :)
Based on recent experiences, I'd say that maps are still broken. For example, see:
http://boards.developerforce.com/t5/Visualforce-Development/How-can-I-call-innerclass-method-in-visualforce-page/m-p/565827#M60291
Until they fix it, you'll probably just want to stick to using lists when you can.