Find your content:

Search form

You are here

What are good design patterns for dynamic visualforce components for a data-driven input form?

 
Share

I am designing a custom input form that will be data-driven and will display things like input fields, input text components, custom inputs that I create, etc. The elements on the form will be largely data-driven so I could create it with dynamic vf binding like so:

<apex:repeat... var="inputobj">
  <apex:outputpanel rendered="{!inputobj.type == 'standardinputfield'}"
    <apex:inputField required="{!inputobj.isRequired}" value="{!inputobj.data[f.apiName]}"
  </apex:outputpanel}"
  <apex:outputpanel rendered="{!inputobj.type == 'something'}"
  ...
  </apex:outputpanel}" 
  <apex:outputpanel rendered="{!inputobj.type == 'something else'}"
  ...
  </apex:outputpanel}"
  ...
</apex:repeat>

I've used this pattern in the past and it works reasonably well but the vf becomes quite unwieldy as you create more types (both from a development and performance perspective) and you can't really reuse and extend the vf like you can with abstract and virtual classes.

My idea is that the inputobjects will have a method which generates a dynamic vf component for itself and then just repeat over those components. Has anyone built something similar or run into any issues with this?

My main concern is ending up with an unholy mess of UI mixed in with business logic or running into performance issues with dynamic vf components.


Attribution to: Greg Grinberg

Possible Suggestion/Solution #1

In a solution I'm building, I adopted the following as pattern.

In the controller, I have a couple of publicly visible elements:

  1. A public Map<String,Object> returned from a method called getViewBag().

  2. a public method named getControlTree() that returns the dynamically generated control tree.

The page looks like this:

<apex:page controller="MyPageController">
  <apex:sectionHeader title="My Section Title" />
  <apex:pageMessages id="pageMessages" />
  <apex:form id="theForm">
    <apex:dynamicComponent id="dynComp" componentValue="{!ControlTree}" />
  </apex:form>
</apex:page>

In the getControlTree Method, I first check if my Map is null. If it is, I initialize it by adding field names and objects. These field names are read from another custom object. The values are chosen based on a "type" of the field, so I add a boolean for example if the field is a checkbox.

With the Map built, I then build the control tree. My getControlTree method returns a PageBlock component which is the container for the hierarchy of form controls.

The knack is configuring the right binding expression on the dynamically created forms. For example, I do this:

Component.Apex.InputText ctl = new Component.Apex.InputText();
ctl.label = field.label;
ctl.expressions.value = buildBindingExpression(field.name);

with the helper function:

private string buildBindingExpression(string fieldName) {
  String expr = '{!ViewBag[\'' + fieldName + '\']' + '}';
  return expr;
}

So the binding expression {!ViewBag['Surname']} refers to the object obtained by m_viewBag.get('Surname')

I encountered four main problems:

Firstly, SelectLists required special handling. The options part has to be bound to a List<SelectOption> and cannot be bound to List<String> or string[] as I expected. The value part of the SelectList must be bound to a String. If you choose to make the SelectList multiselect, the value might have to be a String[] or a List<String>. Haven't tried. Don't know.

Secondly, the list of possible controls seems limited to Checkbox, Radio, SelectList and Textbox and there doesn't seem to be a way of marking a control as mandatory (prove me wrong).

Thirdly, validation. If a textbox is numeric only, or an email address - or if a field is a datepicker, there doesn't seem to be a way of rendering the right control. Component.Apex.InputField will render the right control(s) but can only be bound to sObjects.

Fourthly, there doesn't seem to be a way of programmatically setting html5 pass-through attributes, so I can add an InputText and set the type to "email", for example.

For the above reasons, I'll probably soon re-write my dynamic page to do the control-tree generation on the client with heavy JavaScript support.

It just occurred to me I could create a custom object with a property for every type of possible property, and bind InputField instances to the appropriate property on instances of my custom object - but that would be desperate and possibly a bit dumb.


Attribution to: IanT8

Possible Suggestion/Solution #2

I think the first things you should look to use are Dynamic Visualforce Components. The introduction of this feature was meant to address some of the challenges that you are facing. This allows you to simplify your Visualforce markup by moving the logic of the markup (view) rendering to your Apex Controller. Some will argue that this is bad because it mixes View responsibilities into your Controller, thus breaking the MVC design, but this can also be a really powerful feature if used appropriately.

There is a good example here of returning a outputPanel from the controller.

Aside from this, you can also look to create some of your own Visualforce Components to keep your markup pages a little cleaner.


Attribution to: Kevin O'Hara

Possible Suggestion/Solution #3

Another thing to watch out for is that input fields do not retain their field values upon your controller receiving an error. More information can be seen here. I actually had to go back with VisualForce because the client didn't want to re-enter information if an error occurred. So that is a major issue for me.


Attribution to: Programmable Medley
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/5588

My Block Status

My Block Content