8

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.

6
  • This question will probably get closed, @grigriforce, because it has best practice in the title. Check out / Join in on the meta question and answers on bestpractice questions Commented Dec 20, 2012 at 0:10
  • 1
    @PeterKnolle I'LL get in on the meta discussion but there definitely does not appear to be consensus that these types of questions should be closed. Commented Dec 20, 2012 at 4:19
  • Also, while the question title says best practices I am asking for design-patterns or thoughts on a fairly specific implementation of dynamic vfcomponents. Commented Dec 20, 2012 at 4:27
  • Please try to edit your question, it does ask for design patterns, and is too open question IMO, and does not fit the Q&A type of site. I will not close the Q for now, but will wait for your edit.
    – Saariko
    Commented Dec 20, 2012 at 5:18
  • @Saariko I've created a question in meta to discuss whether this question should be closed. It's 1AM where I am but I'll take a look at this question with a fresh brain tomorrow to clean it up. Commented Dec 20, 2012 at 5:59

3 Answers 3

1

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.

2
  • Sorry if it wasn't clear but I'm perfectly aware of dynamic visualforce components. That is what I was referring to in my question when I said dynamic visualforce. I'm looking for best practices and pitfalls I should watch out for if I decide to go down that route. Commented Dec 19, 2012 at 20:58
  • 1
    Oh I see. I was confused by your VF example. I think the only pitfall that I can think of is the complication of the controller logic as I mentioned in my post. I don't know of any performance issues. Personally, I'd opt to create some Custom Visualforce Components and skip the Dynamic Visualforce Components to keep the presentation and logic separated. Commented Dec 19, 2012 at 21:07
0

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.

0

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .