[SalesForce] Apex form load select list based on selected item in previous select list

I have a VisualForce / Apex form. There is a dropdown SelectList input field for object. I want to load the list of fields for the selected object in a second select list on the same form.

I've tried to use rerender but cannot get it to work so far. The Field select list starts empty and remains empty when changing the Object dropdown.

Page:

<apex:page controller="MyClass">
 <apex:form >
    <apex:pageBlock title="Create" mode="edit">
        <apex:messages />
        <apex:pageBlockButtons >
            <apex:commandButton action="{!step2}" value="Next"/>
        </apex:pageBlockButtons>
        <apex:pageBlockSection title="Select details" columns="2">
            <apex:pageBlockSectionItem >
                <apex:outputLabel for="Object" value="Object"/>
                <apex:selectList id="Object" value="{!myobj.Object__c}" size="1">
                  <apex:selectOptions value="{!ObjectNames}"/>
                  <apex:actionSupport event="onchange" reRender="Field"/>
                </apex:selectList>
            </apex:pageBlockSectionItem>
            <apex:pageBlockSectionItem>
                <apex:outputLabel for="Field" value="Field"/>
                <apex:selectList id="Field" value="{!myobj.Field__c}" size="1">
                  <apex:selectOptions value="{!SobjectFields}"/>
                </apex:selectList>
            </apex:pageBlockSectionItem> 
        </apex:pageBlockSection>
    </apex:pageBlock>
    </apex:form>
</apex:page>

Controller:

public List<SelectOption> getSobjectFields()   
{  
    List<SelectOption> fields = new List<SelectOption>(); 

    if (myobj.Object__c != null) {

    Map<String,Schema.SObjectType> gd = Schema.getGlobalDescribe();  
    Schema.SObjectType sobjType = gd.get(myobj.Object__c);  
    Schema.DescribeSObjectResult r = sobjType.getDescribe();  
    Map<String,Schema.SObjectField> M = r.fields.getMap();  

    for(String fieldName : M.keyset())  
        {  
            Schema.SObjectField field = M.get(fieldName);                                                      
            Schema.DescribeFieldResult fieldDesc = field.getDescribe();  
            fields.add(new SelectOption(fieldName.toLowerCase(),fieldName.toLowerCase())); 
        }  
    }

    return fields;  
}

Best Answer

This appears to work depending on which objects are selected in the first list. My guess at this point is that there is some permissions issue where my user doesn't have access to the fields on all objects when accessed via -

Map<String,Schema.SObjectType> gd = Schema.getGlobalDescribe();  
Schema.SObjectType sobjType = gd.get(myobj.ObjectName);  

Updated with full solution:

I added a filter to my code that builds the Object list in the first dropdown. For each object type I try to describe it and if that returns null I don't add it as an option. Now whatever object I select the field dropdown is dynamically updated with the associated fields.

I think this solution will work for me. There may be a better answer to select the filtered list of objects using the describe methods?

public List<SelectOption> getSobjectFields()   
{  
    List<SelectOption> fields = new List<SelectOption>(); 

    Schema.SObjectType sobjType = gd.get(myobj.ObjectName);  
    if (sobjType != null) {
    Schema.DescribeSObjectResult r = sobjType.getDescribe();  
    Map<String,Schema.SObjectField> M = r.fields.getMap();  

    for(String fieldName : M.keyset())  
        {  
            Schema.SObjectField field = M.get(fieldName);                                                      
            Schema.DescribeFieldResult fieldDesc = field.getDescribe();  
            fields.add(new SelectOption(fieldName.toLowerCase(),fieldName.toLowerCase())); 
        }  
    }


    return fields;  
}      

public List<SelectOption> getObjectNames()
{
   List<Schema.SObjectType> gdo = Schema.getGlobalDescribe().Values(); 
   List<SelectOption> options = new List<SelectOption>();

   for(Schema.SObjectType f : gdo)
   {
       Schema.SObjectType sobjType = gd.get(f.getDescribe().getLabel());  
       if (sobjType != null) {
          options.add(new        SelectOption(f.getDescribe().getLabel(),f.getDescribe().getLabel()));
       }
   }
   return options;

}