4

I'm trying to build a Visualforce page which lists a set of records (via apex:repeat for a Visualforce 'record' Component), and where each record has a 'delete' button under it.

Hitting delete should remove it from a List variable in the apex controller. My current solution is acquiring the list index via apex:repeat + apex:variable, and passing it into the given commandButton->apex:param->assignTo.

Unfortunately, the indexOfNewChildToRemove variable always comes out as null, so I'm not sure whether I'm doing something wrong or what I'm doing is simply impossible.

This is what I've got:

<!--Counter for number of children - this is important with removals-->
<apex:variable value="{!0}" var="rowNum"/>

<apex:repeat value="{!newChildren}" var="child">

    <!--Heading and child detail component-->
    <h2>{!child.Child_s_First_Name__c} {!child.Child_s_Last_Name__c}</h2>
    <c:ASDetectEvalStudyUpsertChildCmp child="{!child}"/>

    <!--Delete record from list-->
    <apex:commandButton action="{!deleteNewChild}" 
            value="Remove">
            <apex:param name="childIndex" value="{!rowNum}" assignTo="{!indexOfNewChildToRemove}"/>
    </apex:commandButton>

    <!--Increment counter-->
   <apex:variable var="rowNum" value="{!rowNum + 1}"/>

</apex:repeat>

On the apex controller, I have {get; set;} but do not explicitly initialise the variable. Even when I do, the value doesn't change - so it doesn't seem to register that the assignTo is happening.

4
  • Try populating the param name attribute
    – Eric
    Commented May 8, 2017 at 3:39
  • I'll try that! Would it matter what I name it as?
    – Lachlan B
    Commented May 8, 2017 at 3:41
  • Nope. Any valid name no spaces or dashes etc
    – Eric
    Commented May 8, 2017 at 3:43
  • Dang. I tried 'childIndex' as the param name but it sill doesn't work. Thanks anyway!
    – Lachlan B
    Commented May 8, 2017 at 3:48

2 Answers 2

2

You have to rerender the form in commandButton

<apex:commandButton action="{!deleteNewChild}" rerender="yourform"
        value="Remove">
        <apex:param name="indexOfNewChildToRemove" value="{!rowNum}" assignTo="{!indexOfNewChildToRemove}"/>
</apex:commandButton>

Or, otherwise you can also use actionFunction without assignTo

<apex:commandButton onClick="deleteChild({!rowNum});return false;" 
        value="Remove">
</apex:commandButton>

<script type="text/javascript">
      function deleteChild(value)
      {
           deleteFunc(value);
      }        
</script>
<apex:actionFunction name="deleteFunc" action="{!deleteNewChild}" immediate="true" reRender="yourform">
     <apex:param id="indexOfNewChildToRemove" name="indexOfNewChildToRemove" value=""/> 
</apex:actionFunction>

Controller

public void deleteNewChild()
{
      fieldValue = ApexPages.currentPage().getParameters().get('indexOfNewChildToRemove');

}

Or, actionFunction with assignTo

<apex:commandButton onClick="deleteChild({!rowNum});return false;" 
        value="Remove">
</apex:commandButton>

<script type="text/javascript">
      function deleteChild(value)
      {
           deleteFunc(value);
      }        
</script>
<apex:actionFunction name="deleteFunc" action="{!deleteNewChild}" immediate="true" reRender="yourform">
     <apex:param id="indexOfNewChildToRemove" name="indexOfNewChildToRemove" assignTo="{!indexOfNewChildToRemove}"/> 
</apex:actionFunction>

In above scenario no need to use getParameters().get(); in controller method.

1
  • 1
    Excellent answer, thank you! Turns out that changing it to apex:commandlink works as the assignTo binds the variable successfully, but for apex:commandbutton to work it needs one of these solutions. I ultimately went with the first option of re-rendering it.
    – Lachlan B
    Commented May 9, 2017 at 5:38
1

Using apex:variable inside apex:repeat is an undefined operation. While it technically does work, there are known glitches with it. I would expect that you'd have problems with this. The problem occurs because apex:variable doesn't remember its value at a specific point within the loop, which means that I'd expect it to either pass a null value or the wrong row's index even if it did work.

Either use a wrapper class, or consider using the record's Id (if they're already saved).

<apex:repeat value="{!newChildrenWrappers}" var="child">
    <!--Heading and child detail component-->
    <h2>{!child.record.Child_s_First_Name__c} {!child.record.Child_s_Last_Name__c}</h2>
    <c:ASDetectEvalStudyUpsertChildCmp child="{!child.record}"/>
    <apex:commandButton action="{!deleteNewChild}" 
            value="Remove">
            <apex:param name="rowIndex" value="{!child.rowIndex}" assignTo="{!indexOfNewChildToRemove}"/>
    </apex:commandButton>
</apex:repeat>

Which, of course, would be built using a wrapper class:

public class ChildWrapper {
    public Integer rowIndex { get; set; }
    public Child__c record { get; set; }
    ChildWrapper(Integer index, Child__c rec) {
        rowIndex = index;
        record = rec;
    }
}
public ChildWrapper[] getNewChildrenWrappers() {
    Integer row = 0;
    ChildWrapper[] results = new ChildWrapper[0];
    for(Child__c record: newChildren) {
        results.add(new ChildWrapper(row++, record));
    }
    return results;
}

Alternatively, I prefer not using an apex:param at all. Just bake it in to the wrapper:

public class ChildWrapper {
    public Integer rowIndex { get; set; }
    public Child__c record { get; set; }
    Controller ctrl;

    ChildWrapper(Controller controller, Integer index, Child__c rec) {
        rowIndex = index;
        record = rec;
        ctrl = controller;
    }
    public void removeChild() {
        ctrl.removeChild(rec);
    }
}
public void removeChild(Child__c recordToRemove) {
    for(Integer index = 0, size = newChildren.size(); index < size; index++) {
        if(newChildren[index] == recordToRemove) {
            newChildren.remove(index);
            break;
        }
    }
}
public ChildWrapper[] getNewChildrenWrappers() {
    Integer row = 0;
    ChildWrapper[] results = new ChildWrapper[0];
    for(Child__c record: newChildren) {
        results.add(new ChildWrapper(row++, record));
    }
    return results;
}

Which simplifies your page a bit:

<apex:repeat value="{!newChildrenWrappers}" var="child">
    <!--Heading and child detail component-->
    <h2>{!child.record.Child_s_First_Name__c} {!child.record.Child_s_Last_Name__c}</h2>
    <c:ASDetectEvalStudyUpsertChildCmp child="{!child.record}"/>
    <apex:commandButton action="{!child.removeChild}" 
            value="Remove" />
</apex:repeat>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .