[SalesForce] Does the assignTo attribute of apex:attribute not work for ApexPages.Action or PageReference types

How would you pass a PageReference attribute to a custom component and have it assign to a variable on the component's controller? It seems like the assignTo attribute of apex:attribute does not work for ApexPages.Action or PageReference types. See the sample code below:

--- Page ---
<apex:page controller="pageController">
  <c:customComponent actionToInvoke="{!doSomething}"/>
</apex:page>

--- Page Controller ---
public class pageController{
  public PageReference doSomething(){
    return Page.SomeOtherPage;
  }
}

--- Component ---
<apex:component controller="componentController">
  <apex:attribute name="actionToInvoke" type="ApexPages.Action" assignTo="{!myAction}" required="false" description=""/>

  <!-- This button executes the supplied action as expected -->
  <apex:commandButton action="{!actionToInvoke}" value="Test1" />

  <!-- This button generates a visualforce error -->
  <apex:commandButton action="{!invokeAction}" value="Test2" />
</apex:component>

--- Component Controller ---
public class componentController{
  public ApexPages.Action myAction {get;set;}

  public PageReference invokeAction(){        
    // myAction is null, so this won't work
    return myAction.invoke();
  }
}

Best Answer

One mechanism for doing this would be to pass the page controller instance as the parameter to the component and then the component can call the page controller's methods itself via that reference.

Here is a functional example:

Page Controller

public with sharing class TestPageController {

    public TestPageController selfReference { get; set; }

    public Integer myInteger { get; set; }
    public String myString { get; set; }

    public TestPageController() {
        this.myInteger = 1;
        this.myString = String.valueOf(this.myInteger);
        selfReference = this;
    }

    public PageReference myStringAction() {
        // increment the integer and add it to the string
        this.myInteger += 1;
        this.myString += ', ' + String.valueOf(this.myInteger);
        return null;
    }
    public PageReference myRedirectAction() {
        // this essentially is doing a 'reset'
        PageReference pRef = Page.TestPage;
        pRef.setRedirect(true);
        return pRef;
    }
}

Component Controller

public with sharing class TestComponentController {

    public TestPageController thePageController { get; set; }

    public PageReference doPageControllerAction1() {
        return thePageController.myStringAction();
    }

    public PageReference doPageControllerAction2() {
        return thePageController.myRedirectAction();
    }
}

Component Markup (TestComponent.component)

<apex:component controller="TestComponentController" >
    <apex:attribute name="PageController" type="TestPageController" assignTo="{!thePageController}" required="true" description="the root page controller reference" />

    <p>The Component Body</p>
    <p>via local component controller methods by reference</p>
    <apex:commandButton action="{!doPageControllerAction1}" value="Fire Page Controller Action 1 (string) through component controller method" /><br />
    <apex:commandButton action="{!doPageControllerAction2}" value="Fire Page Controller Action 2 (redirect) through component controller method"/><br />

    <p>via Component Attribute's Name property reference</p>
    <apex:commandButton action="{!PageController.myStringAction}" value="Fire string action method through attribute name reference" /><br />

    <p>via direct reference through value set by 'assignTo' on attribute</p> 
    <apex:commandButton action="{!thePageController.myStringACtion}" value="Fire string action through controller reference" /><br />

    <p>data retrieved via component controller's pagecontroller reference</p>

    MyInteger: <apex:outputText value="{!thePageController.myInteger}" /><br />
    MyString: <apex:outputText value="{!thePageController.myString}" /><br />
</apex:component>

Page Markup (TestPage.page)

<apex:page controller="TestPageController" >
    <p>the myString value will be concatenated each time an action fires with the current myInteger value</p>
    <apex:form> 
        <c:TestComponent PageController="{!selfReference}" />
    </apex:form>
</apex:page>