+ Start a Discussion
YonathanYonathan 

How to dynamically display SObject in a VF page?

Hi all,

I'd like to dynamically  render an Sobject to a VF page.

Here is my controller:

Code:
private list<SObject> obj;

public void setobj(list<SObject> s){obj=s;} public list<Sobject> getobj(){return obj;}
public PageReference init() {
 obj=[select Name from Account limit 10]; return null; }

Here my VF Page:

Code:
<apex:page Controller="Test" action="{!init}">
<apex:form>
<apex:repeat value="{!obj}" var="field">
   <apex:inputField value="{!field.Name}"/>
</apex:repeat>
</apex:form>
</apex:page>

I've got this error message :
Read access not found for null.
I've also tried with outputText, inputText with no success.The only field I've managed to retrieve is the ID.

Any Idea ?



Best Answer chosen by Admin (Salesforce Developers) 
dchasmandchasman
VF expressions are statically typed and you are binding to the generic SObject type that only supports an id property today. The feature (component binding - will allow 100% dynamic binding, creation of components on the fly, etc) you are looking for has not been released yet - still a few releases away as it stands.

You can take advantage of some of the object oriented features (polymorphism in this case) of apex code to solve this type of problem most of the time. If you want to create a page that can display different types of objects they must all share a common shape. Currently that means using apex code to define an interface (e.g. IFoo) that multiple classes can implement. You then expose a collection of type List<IFoo> or IFoo[] and fill that collection with  set of anything that implements IFoo.

Taking this even further you can make a generic container object in apex code using some new features added in Winter '09 that exposes any sobject in a dynamic fashion - here is working example that illustrates the concept (not complete w.r.t. data types etc - a few custom components that are dynamic sobject aware would be a good idea too):

Code:
<apex:page controller="DynamicSObjectController" showHeader="false">   
    <apex:form id="content">        
        <h1>{!object.sobjectType} [{!now()}]</h1><br/>
        <apex:pageMessages />
        <apex:pageBlock title="{!object.id}">
            <apex:pageBlockButtons location="top">  
                <apex:commandButton action="{!object.save}" value="Save" rerender="content"/>  
            </apex:pageBlockButtons>
            <apex:pageBlockTable var="f" value="{!object.fields}">
                <apex:column value="{!f.name}" headerValue="Name"/>
                <apex:column headerValue="Value">
                    <apex:inputText value="{!f.value}"/>
                </apex:column>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

public class DynamicSObjectController {
public DynamicSObjectController() {
this.dynamicSObject = new DynamicSObject([SELECT name, numberOfEmployees FROM account LIMIT 1]);
}

public DynamicSObject getObject() {
return dynamicSObject;
}

public class DynamicSObject {
public DynamicSObject(SObject delegate) {
this.delegate = delegate;
}

public ID getID() {
return delegate.id;
}

public void save() {
Database.upsert(delegate);
}

public String getSObjectType() {
return getDescribe().getName();
}

public List<Field> getFields() {
if (fields == null) {
fields = new List<Field>();
for (Schema.SObjectField f : getDescribe().fields.getMap().values()) {
if (f.getDescribe().getType() != DisplayType.ID) {
try {
Field field = new Field(delegate, f);
fields.add(field);
} catch (System.SObjectException x) {
// Do nothing - just means that the field was not selected in the select list
}
}
}
}

return fields;
}

private Schema.DescribeSObjectResult getDescribe() {
return delegate.getSObjectType().getDescribe();
}

private final SObject delegate;
private List<Field> fields;
}

public class Field {
Field(SObject delegate, Schema.SObjectField f) {
this.delegate = delegate;
this.f = f;

getValue();
}

public String getName() {
return f.getDescribe().getName();
}

public String getValue() {
Object value = delegate.get(f);
return value != null ? String.valueOf(value) : null;
}

public void setValue(String value) {
// TODO: Handle remaining data type conversions...
if (f.getDescribe().getType() == DisplayType.INTEGER) {
delegate.put(f, Integer.valueOf(value));
} else {
delegate.put(f, value);
}
}

private final SObject delegate;
private final Schema.SObjectField f;
}

private final DynamicSObject dynamicSObject;
}



Message Edited by dchasman on 12-28-2008 12:42 PM

All Answers

dchasmandchasman
VF expressions are statically typed and you are binding to the generic SObject type that only supports an id property today. The feature (component binding - will allow 100% dynamic binding, creation of components on the fly, etc) you are looking for has not been released yet - still a few releases away as it stands.

You can take advantage of some of the object oriented features (polymorphism in this case) of apex code to solve this type of problem most of the time. If you want to create a page that can display different types of objects they must all share a common shape. Currently that means using apex code to define an interface (e.g. IFoo) that multiple classes can implement. You then expose a collection of type List<IFoo> or IFoo[] and fill that collection with  set of anything that implements IFoo.

Taking this even further you can make a generic container object in apex code using some new features added in Winter '09 that exposes any sobject in a dynamic fashion - here is working example that illustrates the concept (not complete w.r.t. data types etc - a few custom components that are dynamic sobject aware would be a good idea too):

Code:
<apex:page controller="DynamicSObjectController" showHeader="false">   
    <apex:form id="content">        
        <h1>{!object.sobjectType} [{!now()}]</h1><br/>
        <apex:pageMessages />
        <apex:pageBlock title="{!object.id}">
            <apex:pageBlockButtons location="top">  
                <apex:commandButton action="{!object.save}" value="Save" rerender="content"/>  
            </apex:pageBlockButtons>
            <apex:pageBlockTable var="f" value="{!object.fields}">
                <apex:column value="{!f.name}" headerValue="Name"/>
                <apex:column headerValue="Value">
                    <apex:inputText value="{!f.value}"/>
                </apex:column>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

public class DynamicSObjectController {
public DynamicSObjectController() {
this.dynamicSObject = new DynamicSObject([SELECT name, numberOfEmployees FROM account LIMIT 1]);
}

public DynamicSObject getObject() {
return dynamicSObject;
}

public class DynamicSObject {
public DynamicSObject(SObject delegate) {
this.delegate = delegate;
}

public ID getID() {
return delegate.id;
}

public void save() {
Database.upsert(delegate);
}

public String getSObjectType() {
return getDescribe().getName();
}

public List<Field> getFields() {
if (fields == null) {
fields = new List<Field>();
for (Schema.SObjectField f : getDescribe().fields.getMap().values()) {
if (f.getDescribe().getType() != DisplayType.ID) {
try {
Field field = new Field(delegate, f);
fields.add(field);
} catch (System.SObjectException x) {
// Do nothing - just means that the field was not selected in the select list
}
}
}
}

return fields;
}

private Schema.DescribeSObjectResult getDescribe() {
return delegate.getSObjectType().getDescribe();
}

private final SObject delegate;
private List<Field> fields;
}

public class Field {
Field(SObject delegate, Schema.SObjectField f) {
this.delegate = delegate;
this.f = f;

getValue();
}

public String getName() {
return f.getDescribe().getName();
}

public String getValue() {
Object value = delegate.get(f);
return value != null ? String.valueOf(value) : null;
}

public void setValue(String value) {
// TODO: Handle remaining data type conversions...
if (f.getDescribe().getType() == DisplayType.INTEGER) {
delegate.put(f, Integer.valueOf(value));
} else {
delegate.put(f, value);
}
}

private final SObject delegate;
private final Schema.SObjectField f;
}

private final DynamicSObject dynamicSObject;
}



Message Edited by dchasman on 12-28-2008 12:42 PM
This was selected as the best answer
David VPDavid VP
I was trying to do something very similar to this.

Doug : I see where you're going with the above example. Neat trick.

This still won't solve the problem of dynamically instantiating an SObject right ?
I'm talking about the equivalent of Java's Class.forName(..) or in Apex something that could be like SObject so = new SObject('runtimeObjectName');


You wouldn't happen to have anything in your trick bag for that, would you ?

Thanks,

-david-
David VPDavid VP
Never mind.

Straight from the docs :

Code:
// Get a new account
Account A = new Account();
// Get the token for the account
Schema.sObjectType tokenA = A.getSObjectType();

// The following works because the token is cast back into an Account
Account B = (Account)tokenA.newSObject();

Getting the tokens dynamically can be done with Schema.getGlobalDescribe() so I guess I'm good here.

-david-
 

rohit.mehtarohit.mehta

Hi,

 

I was trying a similar thing with displaying dynamic SObject. My dynamic SObject has a lookup field to User. is there a way I can render a lookup field in the VF page using SObject.

 

Something like this - 

 

 

<apex:inputField value="{!mySobject.User__c}' />

 

Thanks,

Rohit

 

 

OlaOla

Hi all,

I am new to SalesForce so I appologise in advance for any newbie mistakes.
I have tried to take the accepted solution to this question and iterate over a list of DynamicSObjects instead.

When I try the original solution everything works fine.
The iterative code below uploads ok without any error messages.
When I try to view the page with the new iterative version I get the following runtime error:


 System.Exception: Too many fields describes: 11

Class.queryTestController.DynamicSObject.getFields: line 61, column 46 External entry point


The position mentioned in the error message is
                                            v
for (Schema.SObjectField f : getDescribe().fields.getMap().values()) {

What am I doing wrong?

Thank you for your time!

/Ola

 

 

<apex:page controller="queryTestController">

<apex:form >
<apex:inputText value="{!dynamicQuery}" id="dynamicQuery"/>
</apex:form>

<apex:form id="content">
<apex:pageMessages />
<apex:repeat var="object" value="{!unknowns}">
<h1>{!object.sobjectType} [{!now()}]</h1><br/>
<apex:pageBlock title="{!object.id}">
<apex:pageBlockButtons location="top">
<apex:commandButton action="{!object.save}" value="Save" rerender="content"/>
</apex:pageBlockButtons>
<apex:pageBlockTable var="f" value="{!object.fields}">
<apex:column value="{!f.name}" headerValue="Name"/>
<apex:column headerValue="Value">
<apex:inputText value="{!f.value}"/>
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:repeat>
</apex:form>

</apex:page>



public class queryTestController {

private List<DynamicSObject> dynamicSObjects;
public string dynamicQuery { get; set;}

public queryTestController () {
dynamicQuery = 'select name,closedate from Opportunity';
}

public List<DynamicSObject> getUnknowns() {
dynamicSObjects = new List<DynamicSObject>();
ApexPages.StandardSetController setCon = new ApexPages.StandardSetController(Database.query(dynamicQuery));
List<SObject> base = (List<SObject>) setCon.getRecords();
for (SObject obj : base) {
dynamicSObjects.add(new DynamicSObject(obj));
}
return dynamicSObjects;
}

public class DynamicSObject {
public DynamicSObject(SObject delegate) {
this.delegate = delegate;
}

public ID getID() {
return delegate.id;
}

public void save() {
Database.upsert(delegate);
}

public String getSObjectType() {
return getDescribe().getName();
}

public List<Field> getFields() {
if (fields == null) {
fields = new List<Field>();
for (Schema.SObjectField f : getDescribe().fields.getMap().values()) {
if (f.getDescribe().getType() != DisplayType.ID) {
try {
Field field = new Field(delegate, f);
fields.add(field);
} catch (System.SObjectException x) {
// Do nothing - just means that the field was not selected in the select list
}
}
}
}

return fields;
}

private Schema.DescribeSObjectResult getDescribe() {
return delegate.getSObjectType().getDescribe();
}

private final SObject delegate;
private List<Field> fields;
}

public class Field {
Field(SObject delegate, Schema.SObjectField f) {
this.delegate = delegate;
this.f = f;

getValue();
}

public String getName() {
return f.getDescribe().getName();
}

public String getValue() {
Object value = delegate.get(f);
return value != null ? String.valueOf(value) : null;
}

public void setValue(String value) {
// TODO: Handle remaining data type conversions...
if (f.getDescribe().getType() == DisplayType.INTEGER) {
delegate.put(f, Integer.valueOf(value));
} else {
delegate.put(f, value);
}
}

private final SObject delegate;
private final Schema.SObjectField f;
}
}

 

 
GhilliGhilli
Hi Guys, Good and neat approach to display SObject dynamically. I am trying to implement this one. But i am getting the error saying '

Error: Could not resolve the entity from <apex:inputField> value binding '{!f.value}'. inputField can only be used with SObject fields.

 

I want to display the fields in edit mode so only using inputField tag.

 

Please tell me to rectify this error.

 

Need it urgently.

 

Thanks Guys.

 
SteveBowerSteveBower

 

 

This should really be documented somewhere in the VisualForce documentation.

 

 

NaishadhNaishadh

Hi,

 

Anyupdate on dynamic binding? I am trying to display field value using outfield and getting following error.

Could not resolve the entity from <apex:outputField> value binding '{!f.FieldVal}'. outputField can only be used with SObject fields.

 

Please guide! 

Seema ASeema A


Yes , please give some information about how to access sObject fields on vf page dynamically ?

supriyaksupriyak

Error: Could not resolve the entity from <apex:inputField> value binding '{!acc.name}'. <apex:inputField> can only be used with SObject fields.

 

how to solve this error?

Cdolan04Cdolan04

I'm also getting the error recently posted on a page of mine. Looks as though it might be something new to the 2012 Releases? 

 

The original post here is only from 2008