Visualforce Component Getter/Setters from a Base Class

Originally I wanted to attempt using the interface notation as an added layer of abstraction for this project for the time being I’m going with what I believed to be a simpler solution, unfortunately I’m having issues.

The problem is my getter/setter variables are not working properly (at least as I understand them to work). Here’s the code breakdown:

Visualforce Page:

<c:Gauge widget-type="Number" report="Report Name" data-key="T!T" />

Visualforce Component:

<apex:component controller="GaugeController">

    <!-- Define Component Attributes -->
    <apex:attribute name="widget-type" assignTo="{!type}" description="Type of gauge to be used" type="String" required="true"/>
    <apex:attribute name="report" assignTo="{!reportName}" description="Name report data to be used" type="String" required="true"/>
    <apex:attribute name="data-key" assignTo="{!key}"  description="Field or column key used from the DataDetail" type="String" required="true"/>

    <!-- Define Component Body Variables -->
    <apex:variable var="result" value="{!reportName}"/>

    <!-- Define Component -->
    <apex:outputPanel>
        <apex:outputText>{!result}</apex:outputText>
    </apex:outputPanel>

    <apex:componentBody />

</apex:component>

Component Controller:

public class GaugeController extends Widget{

    public String key { get; set; }

    /* Construtor */
    public GaugeController() {
        System.debug(reportName);  //returns NULL
    }

}

Abstract Base Class:

public abstract class Widget implements Data{

    //========= SETUP GETTERS/SETTERS ============//
    public String title { get; set; }
    public String type { get; set; }
    public String reportName { get; set; }

    public Widget() {
        System.debug(reportName);  // returns NULL
    }
}

I don’t understand why my System.debugs show NULL values yet the page gives me the String result I passed as expected. I think I need to be enlightened a little. Any and all help is much appreciated 🙂

Answer

The use of an abstract base class in itself should not cause issue. I think the problem here is bigger.

Visualforce Component getters / setters / constructors have an unusual and extremely lax contract with each other. I think the component controller code is subject to all the following ambiguities:

  • setter order is non-deterministic
  • getter order is non-deterministic
  • construction order is non-deterministic

Unlike Visualforce Pages, which offer an action attribute to give you a second bite at the cherry, you are basically out of luck relying on any kind of ordering with Visualforce Components.

The only thing that is guaranteed is that a property is set before you get it.

Reliability can be achieved by combining all the input parameters into one “super attribute” which is manipulated in a setter, and overwrites itself with the end result. Falling back to the Object primitive reconciles the restraints in the visualforce output layer, while allowing the Apex code to still execute as needed.

I know it sounds far fetched. And I’d be very grateful if anyone can demonstrate reliability some other way. Below, you can see how your component might be re-written to the above ends 🙂


Visualforce Page

<c:Gauge TypeReportKey="Number,Name,T!T" />

Visualforce Component

<apex:component controller="GaugeController">

    <!-- Define Component Attributes -->
    <apex:attribute name="TypeReportKey" assignTo="{!Params}" description="Type of gauge to be used, Name report data to be used, Field or column key used from the DataDetail" type="Object" required="true" />

    <!-- Define Component Body Variables -->
    <apex:variable var="result" value="{!Params}"/>

    <!-- Define Component -->
    <apex:outputPanel>
        <apex:outputText>{!result}</apex:outputText>
    </apex:outputPanel>

    <apex:componentBody />

</apex:component>

Component Controller

public class GaugeController {

    public Object Params;

    public void setParams(Object value) {
        String type   = ((String)value).split(',')[0];
        String report = ((String)value).split(',')[1];
        String key    = ((String)value).split(',')[2];
        this.Params = someWidgetFunction(type, report, key);
    }

    public Object getParams() {
        return this.Params;
    }
}

(The insanity is contained within the setter… someWidgetFunction is where your code goes)

Attribution
Source : Link , Question Author : Xtremefaith , Answer Author : Matt and Neil

Leave a Comment