2

I did a small test program to dynamically populate a set of fields onto a Visualforce page, which can enable users to enter data from the form.

Created a fieldset as below.

Field set creation

Then I did an Apex page to handle Form inputs.

      <apex:pageBlockSection >
          <apex:dynamicComponent componentValue="{!DynamicFields}"  id="dynamic_pbs"/>
      </apex:pageBlockSection>  

       <apex:pageblockButtons >
           <apex:commandButton value="Submit" action="{!SaveData}" />
       </apex:pageblockButtons>
   </apex:pageBlock>
</apex:form>  

My controller class as below.

public SObject FormObject{get;set;}

public FormGenController(){
        obj_name = ApexPages.currentPage().getParameters().get('obj');
        fieldSetName = ApexPages.currentPage().getParameters().get('fs');
...
}

Below method to generate dynamic object fields,

void Build_Dynamic_SObject(){
    try{
        String field_selection = 'SELECT ID,';
        List<Schema.FieldSetMember> list_of_fields = getFieldsToDisplay();

        for(Schema.FieldSetMember fld : list_of_fields){
            field_selection = field_selection + fld.getFieldPath() +',';
        } 

        field_selection = field_selection.substring(0,field_selection.length()-1);

        if(String.isEmpty(id_str)){
            field_selection = field_selection + ' FROM ' + obj_name + ' LIMIT   1 ';
        }
        else{
            field_selection = field_selection + ' FROM  ' + obj_name + '  WHERE ( id = :id_str )  LIMIT   1 ';
        }

        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, field_selection));

        FormObject = Database.query(field_selection);

Generating the page block for apex page.

public Component.Apex.PageBlock getDynamicFields() {
        System.debug('Get dynamic fields start: ' + FormObject);
        try{
            Component.Apex.PageBlock dynPageBlock = new Component.Apex.PageBlock();

            List<Schema.FieldSetMember> list_of_fields = getFieldsToDisplay();
            Component.Apex.PageBlockSection pbs = new Component.Apex.PageBlockSection();
            pbs.columns = 1;

            for(Schema.FieldSetMember fld : list_of_fields){

                // Add label
                //Component.Apex.OutputLabel display_label = new Component.Apex.OutputLabel();
                //display_label.value = fld.getLabel() ;
                //display_label.for = fld.getFieldPath();
                //pbs.childComponents.add(display_label);

                // input field
                Component.Apex.InputField input_field = new Component.Apex.InputField();
                input_field.expressions.value = '{!FormObject.' + fld.getFieldPath() +'}';
                input_field.id = fld.getFieldPath();
                pbs.childComponents.add(input_field);

            }
            dynPageBlock.childComponents.add(pbs);

Generated Result on Page: Generated result

There is no code in submit button, and I tested without assigning Action as well. When I click sumbit, it gives me below error which seems weird.

Unknown property 'SObject.Description' Error is in expression '{!FormObject.Description}' in component in page formgen_page

Notes:

  • Seems Contact object retrieved via Database.Query() is being reset at some point.
  • If I use Immediate="TRUE" in Apex command button mark up, Action method fires, but with no validations, data is not flushed to bound objects.
  • It seems nothing to do with fieldsets as when I changed the field order, it prompts me with the top most (first entry in field set).

Appreciate and welcome any help on this. Thanks.

  • In Debug Log, it's logged as EXCEPTION_THROWN|[EXTERNAL]|System.VisualforceException: Read access denied for null, controller action methods may not execute – Pasan Eeriyagama Nov 9 '16 at 6:21
1

Found below and it's fixed now,

When binding dynamic input fields to page block you can't use hard binding with '.', instead better to go with below solution.

The way I had used to bind the SObject,

        input_field.expressions.value = '{!FormObject.' + fld.getFieldPath() +'}';

Alternative way used to fix it,

        input_field.expressions.value = '{!FormObject[\'' + fld.getFieldPath() +'\']}';
0

I don't know how much code you've excluded, so can only reply based on what you've shown us.

Your controller for the component is getting the object, not the object Id. From there, you build your field set query beginning with a "try", but I don't see a "catch" part of the block. I'm assuming you have that for it to have compiled? I'm asking in part because of the ApexPages.addMessage line you've located just before you run before your query.

Are you anticipating using this component with a page where a record hasn't been populated? That seems to be the essence of what the error message is telling you. That's also consistent with your code's if-else statement that checks for the id string being empty.

Until the fields are populated, the query won't return fields with null values. That would seem to cause issues for you when that happens. With either of your queries (assuming you do get a random record returned from the first query), if incomplete data is returned, a field may be missing from the query results if it's not populated in the database. That's the essence of why your methods may not execute.

  • Thanks for the response, the code I have given above contains the required code lines, but not all.Try, catch and logging are available in the full method. The page is populated with no errors. The weird exception comes only when you click submit button. This is something to do with System's reading of SObject where content of SObject is Contact, but it assumes it as a generic object, or probably FormObject (SObject type) is being reset at some point. – Pasan Eeriyagama Nov 10 '16 at 23:10
  • @PasanEeriyagama What action method are you trying to associate with the Submit button? Are you doing a Save Action? – crmprogdev Nov 11 '16 at 2:01
  • Own SaveData method is called, Even without no action, exception is thrown. It doesn't go to action method at all. But you can go if you set immediate = "true" in command button. Then no validations, no data has been flushed to underlying Controller object properties. – Pasan Eeriyagama Nov 11 '16 at 3:44

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.