4

How can I build a complex Visualforce Dynamic Binding dynamically? Yeah, I realize the question seems a bit redundant, but I couldn't think of a better way to describe what I'm trying to do. The short version of my question is that I need to build a dynamic binding for both lookup (relationship) fields and standard fields in a Visualforce page, but I'm stuck on an error; how can I accomplish this?

Background

I have a few different sObjects to display, and I have a controlling Custom Setting object with records that indicates which fields should be displayed. The reason for this is to satisfy a requirement to display multiple fields from separate sObjects on a single Visualforce page with an administrative interface to update or add to those fields.

Here's some more detail. The sObjects are contained in a map using the sObject API Name as the key (Map<String, sObject> sObjectsByType). Next, I have a list of Custom Setting records that contain the sObject API Name, the API Name of a Lookup Relationship (if applicable), and the API Name of the field (List<Field__c>).

Issue

I ran into an issue with lookup fields and dynamic bindings while implementing this. Sometimes the f.Lookup_sObject__c field is null, and that causes a Visualforce error (Error evaluating dynamic reference '').

<apex:repeat value="{!Fields}" var="f">
    <apex:inputfield value="{!sObjectsByType[f.sObject__c][f.Lookup_sObject__c][f.Field_API_Name__c]}" />
</apex:repeat>

What I've tried

I tried to dynamically render the inputfields, but even when I do that, I receive the same error because Visualforce cannot resolve the dynamic reference.

<apex:repeat value="{!Fields}" var="f">
    <apex:inputfield value="{!sObjectsByType[f.sObject__c][f.Lookup_sObject__c][f.Field_API_Name__c]}" rendered="{!f.Lookup_sObject__c != null}" />
    <apex:inputfield value="{!sObjectsByType[f.sObject__c][f.Field_API_Name__c]}" rendered="{!f.Lookup_sObject__c == null}" />
</apex:repeat>

Question

How can I use dynamic bindings on an <apex:inputField> to display fields that are lookups to fields on other objects as well as fields that are not? Is this even possible?

  • 1
    Maybe I'm missing something. If f.Lookup_sObject__c is null would it make sense to just hide that inputfield using something like rendered="{! f.Lookup_sObject__c != null}"? – Daniel Ballinger Dec 4 '13 at 23:16
  • I tried that, but I still receive an error. I updated the question to reflect that. – Matt K Dec 5 '13 at 14:18
  • 1
    Have you looked into Fieldsets? These seem to offer what you want without the need for you to build a custom setting? salesforce.com/us/developer/docs/pages/Content/… – Andrew Fawcett Dec 5 '13 at 15:50
  • While you ponder my question re: Fieldsets approach... It could be due Visualforce resolving the 'value' attribute regardless as to the value of 'rendered'. Maybe try wrapping it in a apex:outputPanel and place the 'rendered' attribute to conditionally display it on that? – Andrew Fawcett Dec 5 '13 at 15:54
  • Yeah, I thought of fieldsets, but the fields span multiple objects, and there's additional information that we need to store on each field (to determine when it shows up based on another requirement not mentioned in this question). I'll try your idea about the outputpanel, though; that sounds like it might work. – Matt K Dec 5 '13 at 16:05
5

The expression value can be set in an Apex controller using a dynamiccomponent. Because the expression is built outside of Visualforce, it can be dynamically set based on a Custom Setting. Here's what I ended up with:

Visualforce

<apex:dynamicComponent componentValue="{!SectionsAndFields}"/>

Apex

public Component.Apex.OutputPanel getSectionsAndFields() 
{
    Component.Apex.PageBlockSection PageBlockSection 
        = new Component.Apex.PageBlockSection();

    PageBlockSection.title = 'Section';
    PageBlockSection.collapsible = false;

    for(Field__c f : Fields)
    {
        Component.Apex.InputField InputField = new Component.Apex.InputField();

        if(f.Lookup_sObject__c != null 
        && sObjectsByType.get(f.sObject__c).getSObject(f.Lookup_sObject__c) != null) 
        {
            // use dot notation to reference fields on an sObject
            // use brackets and escaped quotes around a key 
            //   to get a value from a map (sObjectsByType in this example)
            InputField.expressions.value = '{!sObjectsByType' 
                + '[\'' + f.sObject__c + '\']' 
                + '.' + f.Lookup_sObject__c + '.' + f.Field_API_Name__c + '}';

            PageBlockSection.childComponents.add(InputField);
        }

        if(f.Lookup_sObject__c == null) 
        {
            InputField.expressions.value = '{!sObjectsByType' 
                + '[\'' + f.sObject__c + '\']' 
                + '.' + f.Field_API_Name__c + '}';

            PageBlockSection.childComponents.add(InputField);
        }
    }

    return PageBlockSection;
}
| improve this answer | |
  • Nice approach, i was actually wondering if that would work, but didn't have time to try it. +1 :-) – Andrew Fawcett Dec 5 '13 at 22:24

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.