+ Start a Discussion

Why does one table rerender, and the other doesn't??

I have two datatables in my page. One of them rerenders when the a new search occurs - rendering a table with the search results. I am also able to take the selected items in the table and render them in a new table.

Ths second table also has checkboxes and a button which should remove an item from the list, and rerender the table without that item - this does not work. 


Here's the code for the tables that work. This first part is for the Product Search and displaying the Product Search Results:



<!---PAGE---> ... <apex:form > <apex:pageBlock title="Product Search" id="ProductSearch"> <apex:outputPanel id="Search" title="Search Result" > Search for: <apex:inputText value="{!SearchString}" id="SearchString"/><p/> <apex:commandButton action="{!ProductSearch}" value="Search" id="theButton" rerender="errors,SearchResult"/> </apex:outputPanel> <apex:pageBlockSection title="Product Selections"> <apex:pageBlockTable id="SearchResult" value="{!prodRes}" var="PR" > <apex:column > <apex:facet name="header">Select</apex:facet> <apex:selectCheckboxes value="{!Selections}" > <apex:selectOption itemvalue="{!PR.ProductID}" /> </apex:selectCheckBoxes> </apex:column> <apex:column > <apex:facet name="header">Product Name </apex:facet> <apex:outputText value="{!PR.ProductName}"/> </apex:column> </apex:pageBlockTable> <apex:commandButton value="Add to Cart" action="{!processSelected}" rerender="ShoppingCart" id="AddButton"/> </apex:pageBlockSection> </apex:pageblock> <!-- CONTROLLER --> ... public pageReference ProductSearch(){ String mySearchString = '%' + SearchString + '%'; system.debug('SearchString is '+SearchString); prodQry = [SELECT id, Name, ProductCode, Description, Family, isActive, SVMXI__Product_Cost__c FROM Product2 WHERE Name like :mySearchString OR Description like :mySearchString OR ProductCode = :mySearchString]; ProductSearchResult(prodQry); return null; } public void ProductSearchResult(Product2[] prodQry){ prodRes.clear(); if (prodQry.size() > 0) for (Product2 prd :prodQry){ ProductResult newPR = new ProductResult(prd.id, prd.Name, prd.ProductCode, prd.Description, prd.Family, prd.isActive, prd.SVMXI__Product_Cost__c, '0'); prodRes.add(newPR); system.debug('Prodres size is '+prodres.size()); } }

 Selecting Checkboxes will render a new pageblocktable - Shopping Cart - in a new PageBlock using the {!ProcessSelected} method invoked by the 2nd command button in the page code above.Also, changing the Quantity in the top table will change the Quantity in the bottom table.


More to the point, after selecting products in the first rendering, I can search for products using a different searchstring, which will rerender the table with new products. Then, selecting one of those products and Adding to the cart will add that new line to the Shopping Cart table, keeping the other values intact. Here's the code for that.



<apex:pageBlock id="ShoppingCart" title="Shopping Cart"> <apex:pageBlockSection > <apex:pageBlockTable id="CartTable" value="{!prodCart}" var="PC" > <apex:column > <apex:facet name="header">Remove Item</apex:facet> <apex:selectCheckboxes value="{!selected}" > <apex:selectOption itemvalue="{PC.ProductID}" /> </apex:selectCheckBoxes> </apex:column> <apex:column > <apex:facet name="header">Line Number </apex:facet> <apex:outputText value="{!PC.LineNumber}"/> </apex:column> <apex:column > <apex:facet name="header">Product Name </apex:facet> <apex:outputText value="{!PC.ProductName}"/> </apex:column> </apex:pageBlockTable>

<apex:commandButton value="Remove Items" action="{!removeItems}" id="removeButton" rerender="ShoppingCart"/>

</apex:pageBlockSection> </apex:pageBlock> </apex:form> <!--CONTROLLER--> public PageReference ProcessSelected(){ if (Selections.size() > 0) for (String PS :Selections) for (ProductResult PR :prodRes) if (PS == PR.ProductID) if (!prodSel.isEmpty()){ for (integer i = 0; i < prodSel.size(); i++) if (prodSel[i].ProductID == PR.ProductID){ prodSel.remove(i); prodSel.add(PR); } else { prodSel.add(PR); } } else prodSel.add(PR); createProdCart(prodsel); return null; } CartProduct[] prodCart = new CartProduct[]{ get; set; } public pageReference createProdCart(ProductResult[] prodSel){ for (ProductResult PR :prodSel){ integer Qty = convQty(PR.Quantity); integer LineItem = LineItemGen(prodCart); decimal LineTotal = LineTotalGen(PR.ProductCost, Qty); CartProduct newCP = new CartProduct(PR.ProductID, PR.ProductName, PR.ProductCode, PR.Description, PR.Family, True, PR.ProductCost, Qty, LineItem, LineTotal); } return null; }

In bold font above is the 3rd button for removing items in the 2nd table, selecting the items for removal using another set of checkboxes.


The code for this, with the Selected definitions:

id[] selected = new String[]{}; public id[] getSelected(){ return Selected; } public void setSelected (id[] selected){ this.Selected.addall(selected); } public PageReference removeItems(){ for (integer i = 0; i < prodCart.Size(); i++) for (id S :Selected) if (S == prodCart[i].ProductID) prodCart.remove(i); return null; }

 This last method does absolutely nothing. I saw a Note in the VisualforceDevelopers Guide that says tables cannot be rerendered, yet both of the tables on this page do rerender - when adding Product Search Results, and when displaying selections from that table to the Shopping Cart table below. 


However, removing those Cart items, which would seem to be the easiest thing, doesn't work. Am I missing something obvious, or is this something that VisualForce/APEX just can't do - I cannot believe it is the latter. I do know I've tried it a lot of different ways, but haven't found the one that works yet. 


As always, thanks for any help you can provide. 












Ron HessRon Hess

Apex docs cautions against removing an item from a list while iterating over that same list.



you should rewrite that loop to build a fresh new list with the selected items not it it, rather than remove from a list while looping over it .


here is the doc: 


Iterating Collections

Collections can consist of lists, sets, or maps. Modifying a collection's elements while iterating through that collection is not supported and causes an error. Do not directly add or remove elements while iterating through the collection that includes them.

Adding Elements During Iteration

To add elements while iterating a list, set or map, keep the new elements in a temporary list, set, or map and add them to the original after you finish iterating the collection.

Removing Elements During Iteration

To remove elements while iterating a list, create a new list, then copy the elements you wish to keep. Alternatively, add the elements you wish to remove to a temporary list and remove them after you finish iterating the collection.


The List.remove method performs linearly. Using it to remove elements has time and resource implications.



"Apex docs cautions against removing an item from a list while iterating over that same list."


Thanks for that info!! I made changes to two different sections where I was doing exactly that. However as it turns out the problem was syntactical in nature - sending an array of an incorrect data type to a set method, not adding the "!" in a variable reference - lots of little things that can be easy to overlook when one is convinced the problem is in the design.


However there is still some strange behavior going on. I have two buttons rerendering the same table. When I click the second button - which removes items from the table, the Debug log for that action appears to list actions that are launched from the other button. A lot of these are 'get' functions from public variables. My question is do all of the public getter and setter methods in a controller fire when the associated method on an action component (a button in this case) is fired? It seems like that is what is happening from the Debug log. 




I am developing a similar stuff, is it possible to share the code which you have pasted.