14

VF page

<apex:page>
  <c:MyCustomComponent someString="hello"/>
</apex:page>

MyCustomComponent (component)

<apex:component controller="MyCustomComponentController">
<apex:attribute name="someString" assignTo="{!receivedString}" type="String"/>
..
some more code
..
</apex:component>

MyCustomComponentController (component controller)

public with sharing class MyCustomComponentController{

    public String receivedString {get;set;}

    public MyCustomComponentController() {
        System.debug(receivedString); 
    }

    public initializeComponent() {
        System.debug(receivedString); 
    }
}

The problem is in the first System.debug inside the constructor the receivedString is null. So the constructor is being called before the receivedString's value can be set.

Is there any workaround for this.

Appreciate your help.

  • 2
    It's not clear from your question, are you trying to set the property receivedString based on some value from your page? Have you defined an attribute in your component using the apex:attribute tag? Can you post the code for how you are calling your component on your VF page? – greenstork Mar 22 '13 at 21:23
25

What you have observed is consistent with the documented Order of Execution for Visualforce Page Get Requests. It will always be not yet set in the constructor.

Order of Execution for Visualforce Page Get Requests

  1. The constructor methods on the associated custom controller or controller extension classes are called, instantiating the controller objects.
  2. If the page contains any custom components, they are created and the constructor methods on any associated custom controllers or controller extensions are executed. If attributes are set on the custom component using expressions, the expressions are evaluated after the constructors are evaluated.
  3. The page then executes any assignTo attributes on any custom components on the page. After the assignTo methods are executed, expressions are evaluated, the action attribute on the component is evaluated, and all other method calls, such as getting or setting a property value, are made.
  4. If the page contains an component, all of the information necessary to maintain the state of the database between page requests is saved as an encrypted view state. The view state is updated whenever the page is updated.
  5. The resulting HTML is sent to the browser. If there are any client-side technologies on the page, such as JavaScript, the browser executes them.

One workaround is to do the intialization inside of your setter.

public with sharing class MyCustomComponentController {

    public Boolean initialized { get; set; }

    public String receivedString { 
        get;
        set {
            receivedString = value;
            if (!initialized) {
                initializeThatDependsOnReceivedString();
                initialized = true;
            }
        }
    }

    public MyCustomComponentController() { System.debug(receivedString); }
    public initializeThatDependsOnReceivedString() { System.debug(receivedString); }  
}

Or, you could put the initialization in the getter.

public String receivedString { 
    get {
        if (!initialized) {
            initializeThatDependsOnReceivedString();
            initialized = true;
        }
        return receivedString;
    }
    set;
}

It starts to get a little bit unwieldy if you have some initialization that depends on many attributes being set.

Alternatively, you could create an actionFunction in the component that you call after the component has loaded and do the one time initialization in it. You would just need to make sure that you code the component VF so that the actionFunction is only invoked one time.

1

You may be running into the problem that strings are passed as values and not references and therefore need to wrap it in a class (object)... see this blog post: http://bobbuzzard.blogspot.com/2011/05/updating-attributes-in-component.html

1

I think a cleaner method than putting the initialization in each property is to use a single property that's only purpose is initialization.

1:

Add a boolean property called something like init and have it call your initialization function

global with sharing class MyComponentController {
    public Account myAccount {get; set;}
    public Boolean init{
        get{
            init();
            return false;
        }
    }

    public void init(){
       //put all your initlization logic here.
       //your assigned properties will be set when this executes
       myAccount.name = 'Hello World';
    }
}

2:

In your apex component use add the init property in any <apex:> tag to the TOP of your component. This will cause it to fire your init() function before the getters of all other properties.

<apex:component controller="MyComponentController">
    <apex:attribute name="account"
      type="Account"
      assignTo="{!myAccount}"
      required="true"
      description="" />
    <!-- to ensure it gets called first, it needs to be in an apex component attribute and needs to be at the top -->
    <apex:outputText rendered='{!init}' />
    <apex:outputField value="{!myAccount.name}" />
</apex:component>

This is easier to read, debug and works better if there are dependancies across properties (eg: property A needs property B).

Note: if your component might get reRendered and you don't want to run the init function a second time, use a Boolean field to track if the init function has run already.

  • 1
    This REALLY helped me. The other answer is good for a quick variable, but it was making my code gross. I can now set everything normally. Thank you so much! – Jacob Raccuia May 16 '18 at 16:16
0

A same issue occurred to me and I have found a workaround.

PROBLEM: I have a visualforce page that has a value coming from its constructor, this value is being assigned to a component used in the page and through the component, the value needs to be sent to the component controller.


VF Page:

<apex:page controller="TestCloneController">
    <c:GenericPaginationComponent sObjectAPIName="{!sObjectAPiName}"/>
</apex:page>

Page Controller:

public class TestCloneController {

   public String sObjectAPIName{get; set;}

    public TestCloneController() {
        sObjectAPIName= 'Account';
    }
}

The value sObjectAPIName is set to the component.

Component:

<apex:component controller="ComponentController">
    <apex:attribute name="sObjectAPIName" type="String" required="true" description="API Name of the object" assignTo="{!objectName}"/>

    <p>{!sObjectAPIName}</p>
</apex:component>

Here, you have to use the assignTo attribute to let the controller know in which variable the value of attribute has to be assigned.

Component Controller:

public class ComponentController {

    public Boolean getterSetterFlag = true;

    public String objectName;
    public void setobjectName (String s) {
        objectName = s;
        if(getterSetterFlag) {
            system.debug(objectName); //objectName will not be null here
            getterSetterFlag = false;
        }
    }
    public String getobjectName() {
        return objectName;
    }

    //constructor
    public ComponentController() {

        system.debug(objectName); //objectName will be null here
    }   
 }

Code Explanation: The component constructor is called when the component is loaded because in the first line it says controller="ComponentController". Here the value of the attribute will be null.

The getter and setter for the variable calls a debug, here the values have been set.

NOTE: The getter and setter methods are called twice, and therefore flag variable has been taken to make it run only once. Not sure why this behaviour is possessed.

Please upvote this if it solves your problem, to help others looking for the similar issues.

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.