10

I have a visual force page as follows which includes a component call Example one,

Component has a command button and I want to call the standard controller save action of VF page from the VF component. Is this possible?

visualforce page

<apex:page standardController="Account">
  <apex:form>
    <apex:pageBlock title="My Content" mode="edit">
      <apex:pageBlockButtons>
        <apex:commandButton action="{!save}" value="Save"/>
      </apex:pageBlockButtons>
      <apex:pageBlockSection title="My Content Section" columns="2">
        <apex:inputField value="{!account.name}"/>
        <apex:inputField value="{!account.site}"/>
        <apex:inputField value="{!account.type}"/>
        <apex:inputField value="{!account.accountNumber}"/>
      </apex:pageBlockSection>
    </apex:pageBlock>
    <c:exampleOne/>
  </apex:form>
</apex:page>

Component "exampleOne"

<apex:component allowDML="true">
    <apex:commandButton action="{!save}" />
</apex:component> 

Note : I know we can use a separate controller class and use a custom save that would be my second option.

7
  • What's the error you are getting?I am guessing it will work as expected Dec 16, 2015 at 2:06
  • There is no error except the record doesn't get saved
    – Hasantha
    Dec 16, 2015 at 2:07
  • Can you add apex:page messages tag to make sure there is no validation errors? Dec 16, 2015 at 2:08
  • I added the <apex:pageMessages /> as you mentioned still the same.
    – Hasantha
    Dec 16, 2015 at 2:16
  • You cannot do this because a command button has to be in a form and you cannot have nested forms. So to compile the component the button would have to be wrapped in a form tag, then you could not place the component within the form in the main vf page. Thus you would only be submitting the form in the component which is not what you want to do
    – Eric
    Dec 16, 2015 at 2:37

2 Answers 2

14

This should be doable by passing an argument of type ApexPages.Action to the component

<apex:page standardController="Account">
 <apex:form>
   <apex:pageBlock title="My Content" mode="edit">
   <apex:pageBlockButtons>
    <apex:commandButton action="{!save}" value="Save"/>
   </apex:pageBlockButtons>
   <apex:pageBlockSection title="My Content Section" columns="2">
    <apex:inputField value="{!account.name}"/>
    <apex:inputField value="{!account.site}"/>
    <apex:inputField value="{!account.type}"/>
    <apex:inputField value="{!account.accountNumber}"/>
  </apex:pageBlockSection>
 </apex:pageBlock>
 <c:exampleOne saveAction="{!save}"/>
 </apex:form>
</apex:page>

Component: exampleOne

<apex:component >
   <apex:attribute name="saveAction" type="ApexPages.Action" required="true"/>
   <apex:commandButton action="{!saveAction}" />
</apex:component> 

This example is covered in the excellent book Visualforce Development Cookbook by the inestimable Keir Bowden (aka @BobBuzzard)

4
  • 4
    This is awesome !!! Dec 16, 2015 at 2:59
  • 3
    I remember when I read this after buying Keir's book and actually writing Keir saying "what a great insight". Of course @sfdcfox would know this implicitly :-)
    – cropredy
    Dec 16, 2015 at 3:05
  • Actually... I've learned something new today. Although, there's an alternative I've been using for ages that also works, which I'll post independently.
    – sfdcfox
    Dec 16, 2015 at 3:33
  • really cool one :)
    – Ratan Paul
    Dec 16, 2015 at 3:57
8

You can pass the standard controller to the component, allowing you to invoke its functions normally:

public class ComponentControllerClass {
    public ApexPages.StandardController pageStandardController { get; set; }
    // Needed only because we are providing a second constructor
    public ComponentControllerClass() {
    }
    public ComponentControllerClass(ApexPages.StandardController controller) {
        pageStandardController = controller;
    }
}

And now, the component:

<apex:component controller="ComponentControllerClass">
    <apex:attribute name="controller" type="ApexPages.StandardController" assignTo="{!pageStandardController}" description="Standard Controller" />
    <apex:commandButton value="Save" action="{!pageStandardController.save}" />
</apex:component>

And, of course, the page itself:

<apex:page standardController="Account" extensions="ComponentControllerClass">
    <apex:form> <!-- components with commandButton/Link must be in a form -->
        <c:myComponent controller="{!pageStandardController}" />
    </apex:form>
</apex:page>

Also, you can do things like reading the record from the standard controller that's passed in, and modifying it to affect the record outside the component, etc.

1
  • One clarification as to how this works (was mind-bending for me initially): When the component is rendered, a new instance of the component's controller is instantiated. The component must be defined with a controller. This instance happens to be (for convenience) of the same type as the parent controller and as such is not the typical component controller pattern. The new instance (that is, the component's controller) has a reference via the assignTo to the parent controller and thus like normal object refs, the component markup can access the parent controller's methods and properties.
    – cropredy
    Feb 11, 2017 at 3:37

You must log in to answer this question.

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