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
    May 8, 2017 at 3:39
  • I'll try that! Would it matter what I name it as?
    – Lachlan B
    May 8, 2017 at 3:41
  • Nope. Any valid name no spaces or dashes etc
    – Eric
    May 8, 2017 at 3:43
  • Dang. I tried 'childIndex' as the param name but it sill doesn't work. Thanks anyway!
    – Lachlan B
    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
    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>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.