3

I have a custom VF component that gets passed in an ApexPages.Action (=passedInAction()) that needs to be invoked when the user performs some changes inside the component (=doInvokeAction() method).

My problem is that the passed in action always is null when it's going to be invoked. What is that so?

Component markup:

<apex:component controller="ComponentCtrl" allowDML="true"> 
  <apex:attribute name="onAction" assignTo="{!passedInAction}" type="ApexPages.Action" required="false" description="" />
  <apex:attribute name="rerenderOnPage" type="String" required="false" description="" /> 

  ...
          <apex:inputCheckbox...>
              <apex:actionSupport event="onclick" action="{!doInvokeAction}" rerender="componentIds,{!rerenderOnPage}" />
          </apex:inputCheckbox>
  ...            
</apex:component>

Component controller:

public with sharing class ComponentCtrl {

  public ApexPages.Action passedInAction { get; set; }

  ...


    public PageReference doInvokeAction() {
        someInternalComponentsStuff()

        return passedInAction.invoke(); <--- NULLPOINTER HERE!
    }
}

VF Page using the component and passing in its Action:

<apex:page standardController="CustomObject__c" extensions="PageCtrlExt" recordSetVar="dlis" sidebar="false">

  ...
  <apex:form id="form">

        <c:myComponent onSelect="{!actionToPassIn}"
                       rerenderOnSelect="selectedItem" />   


  </apex:form> 
</apex:page>

VF Page Controller Extension:

public with sharing class PageCtrlExt {

  ...


    public PageReference actionToPassIn() {
        ....

        return null;
    }
}
8
  • The component controller is named SObjectTableComponentCtrl but the class name is ComponentCtrl. Also the attribute labels are different between VF page and component. Are you sure this is the right component? Commented Apr 28, 2015 at 12:45
  • I think the problem here might be that the onSelect parameter is using the actionToPassIn function, not mentioning it. You know in javascript the difference between: element.onclick = doSomething; and element.onclick = doSomething(); I think your problem might be you want to do the first one, but you're doing the second one. I think onSelect is being set to the return value of the actionToPassIn method (which is null) and not the method itself. I don't know if passing methods around as variables works in Apex.
    – Dominic
    Commented Apr 28, 2015 at 12:59
  • @guy You are right. When minimizing my code I did not rename everything right. I corrected the original question. Commented Apr 28, 2015 at 13:50
  • 1
    Passing methods as component attributes should work, according to documentation: salesforce.com/us/developer/docs/apexcode/Content/… But I'm wondering if it also supports assignTo properly. Does it work if you don't use the assignTo but directly use the action="{!onAction}" instead of action="{!doInvokeAction}" in the component? Not that it would solve the issue (as you'd loose the someInternalComponentsStuff), but it might point you towards a solution.. Commented Apr 28, 2015 at 14:02
  • 2
    @RobertSösemann I've done this before: 1. define an interface with the method for the action; 2. make the controller implement the interface; pass the whole controller into the component; 4. in the component, call the method in the controller
    – Dominic
    Commented Apr 28, 2015 at 14:18

2 Answers 2

6
+50

This is an alternative strategy that should achieve the same goal.

  1. Define an interface with one method
  2. Implement the interface in the controller
  3. Make the component parameter be type [the interface you defined]
  4. Pass the controller into the component
  5. Call the method on the passed in controller

Some minimal bits of code:

public interface ActionInterface {
  PageReference actionToInvoke()
}

public class PageCtrl implements ActionInterface {
  public PageReference actionToInvoke() {
   //do things
   return null;
  }
}

public class ComponentCtrl {
  public ActionInterface aif {get; set;}

  public PageReference invokeAction() {
    return aif.actionToInvoke();
  }
}

To get the PageCtrl reference into the ComponentCtrl, pass "this" in your VF page like this:

<c:myComponent actionInterfaceParamName="{!this}" />  
1
  • This looks like a cool technique - I'll def try it next time I create a new component. Commented Apr 29, 2015 at 2:26
1

Create an actionFunction on component document.ready method and call the doInvokeAction() method in there. Don't know whether it is a salesforce issue but the values to the component always are reflected as null during the first page load

3
  • Do you mean calling the action from a Javascript context? document.ready wouldn't work but another event handler would. I upvoted your helpful answer but the real answer should eigther solve this without the JS detour or explain why it only will work that way. Commented Apr 21, 2015 at 12:05
  • document.ready would definitely work in the component body. You can write that inside the script tag of your component. For the reason for the same, please refer to the similar question: salesforce.stackexchange.com/questions/9941/… Commented Apr 21, 2015 at 12:41
  • You are not getting my point. My problem is not the one from the other question where something is null in the constructor. Therefore calling something on document.ready is also not a solution for me. The page and component and its classes have been constructed right, but when the user calls an action a variable assigned from component attributes stay null. Commented Apr 28, 2015 at 11:21

You must log in to answer this question.

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