2

I have a Visualforce component that contains an apex:dynamiccomponent to render various controls based on a configured Custom Metadata type.

editMode Component:

<apex:component controller="componentController">
    <apex:attribute name="value" type="String" description="Sample component." assignTo="{!selectedValue}"/>
    <p>
    Value = {!value}<br/>
    selectedValue = {!selectedValue}<br/>
    </p>

    <apex:dynamiccomponent componentvalue="{!dynamicControls}" />

</apex:component>

editMode Component Controller:

public class componentController {
    public String selectedValue {
        get {
            System.debug(LoggingLevel.Debug, 'componentController.selectedValue get: ' + selectedValue);
            return selectedValue;
        }
        set {
            System.debug(LoggingLevel.Debug, 'componentController.selectedValue set: ' + value);
            selectedValue = value;
        }
    }

    public componentController() {
        System.debug(LoggingLevel.Debug, 'componentController constructor called.');
    }

    public Component.Apex.PageBlockSection getDynamicControls() {

        System.debug(LoggingLevel.Debug, 'componentController.getDynamicControls() called.');

        Component.Apex.PageBlockSection pbSection = new Component.Apex.PageBlockSection();
    pbSection.title='Custom Controls';
    pbSection.showHeader = true;
    pbSection.columns = 2;

        Component.Apex.OutputText test = new Component.Apex.OutputText(value='Dyanmic Component selectedValue:' + selectedValue);
        pbSection.childComponents.add(test);

        return pbSection;
    }
}

Visualforce page containing the component:

<apex:page>
    <apex:messages />
    <apex:pageBlock title="Component page block">
        <c:editMode value="foo"/>
    </apex:pageBlock>
</apex:page>

This results in the following. Note how the dynamic OutputText component saw the selectedValue as null but the direct bind got the attribute value.

enter image description here

Debug log extract:

6 14:12:49.000 0.016156909 USER_DEBUG [14] DEBUG componentController constructor called.
7 14:12:49.000 0.016369249 USER_DEBUG [19] DEBUG componentController.getDynamicControls() called.
8 14:12:49.000 0.017155422 USER_DEBUG [4] DEBUG componentController.selectedValue get: null
9 14:12:49.000 0.018336522 USER_DEBUG [8] DEBUG componentController.selectedValue set: Foo
10 14:12:49.000 0.102206825 USER_DEBUG [8] DEBUG componentController.selectedValue set: Foo
11 14:12:49.000 0.10254756 USER_DEBUG [4] DEBUG componentController.selectedValue get: Foo

I've had a similar issue before in Unexpected Visualforce Order of Execution with component attributes on Get Request. That was slightly different as it dealt with the order in which the constructors were called.

3

The debug log shows the issue. The binding expression on the componentController for the apex:dynamiccomponent is called after the constructor and before the values from the attributes are assigned.

To work around this issue, the dynamic components need to use the expressions property to define expression statements that will dynamically bind the property values.

E.g.

modified editMode Component Controller:

public class componentController {
    public String selectedValue {
        get {
            System.debug(LoggingLevel.Debug, 'componentController.selectedValue get: ' + selectedValue);
            return selectedValue;
        }
        set {
            System.debug(LoggingLevel.Debug, 'componentController.selectedValue set: ' + value);
            selectedValue = value;
        }
    }

    public componentController() {
        System.debug(LoggingLevel.Debug, 'componentController constructor called.');
    }

    public Component.Apex.PageBlockSection getDynamicControls() {

        System.debug(LoggingLevel.Debug, 'componentController.getDynamicControls() called.');

        Component.Apex.PageBlockSection pbSection = new Component.Apex.PageBlockSection();
        pbSection.title='Custom Controls';
        pbSection.showHeader = true;
        pbSection.columns = 2;

        Component.Apex.OutputText test = new Component.Apex.OutputText();
        // Modified here to use expressions to bind to the property set by an attribute
        test.expressions.value = '{!selectedValue} Test';
        pbSection.childComponents.add(test);

        return pbSection;
    }
}

Which gives:

enter image description here

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.