function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
guyr1981guyr1981 

how to set records from dataTable on a custom controller extension

Hi,

 

I have the following dataTable:

 

 

<apex:pageBlock title="Planing Table" id="thePageBlock" mode="edit">
          <apex:dataTable value="{!TabAccounts}" id="outputTable" var="pitem" columns="10" cellPadding="6">
          <apex:column headerValue="Account" >
          <apex:outputText value="{!pitem.name}" style="width: 80px"/> 
          </apex:column>
          <apex:column headerValue="Jan">
          <apex:inputField value="{!pitem.Jan__c}" style="width: 80px"/> 
          </apex:column>
          <apex:column headerValue="Feb">
          <apex:inputField value="{!pitem.Feb__c}" style="width: 80px"/>
          </apex:column>

 

<apex:pageBlock title="Planing Table" id="thePageBlock" mode="edit">

     <apex:dataTable value="{!TabAccounts}" id="outputTable" var="pitem" columns="10" cellPadding="6">

          <apex:column headerValue="Test1">

          <apex:inputField value="{!pitem.test1__c}" style="width: 80px"/>

           </apex:column>

          <apex:column headerValue="Test2">

          <apex:inputField value="{!pitem.test2__c}" style="width: 80px"/>

          </apex:column>

..........

..........

..........

 

          <apex:column headerValue="Test10">

          <apex:inputField value="{!pitem.test10__c}" style="width: 80px"/>

          </apex:column>

..........

..........

..........

 

 

On the controller I have the getTabAccounts that returns a list of my Table_Account__c objects (from which the dataTable is being built).

 

When the user changes the values on the dataTable input fields I would like to have a "set" API that would recive the changes made by the user.

 

I tried writing a setTabAccounts API but it was not called upon user changes.

Also tried writing a setPitem API but, had the same result.

 

What "set" API should I write to get the user changes?

 

Thanks,

Guy

 

 

 

 

 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
bob_buzzardbob_buzzard

No problem.  This is an important feature to understand, as it is the cornerstone of how forms are backed by variables in visualforce.  For my money its the most elegant solution I've come across.

 

The short answer is that you can't do what you would like to do.

 

The long answer is:

 

Your getter is returning a list of sobjects (Table_Account__c) which is used by a datatable that has the capability to iterate the values and do some stuff with them.  This list is not copied to the page or retained in any way so that it can be sent back to the controller, it is simply iterated and the body rendered once for each element in the list.

 

The body of the datatable is repeated for each element in the list.  Inside the body of the datatable, when you have an inputField component, this is bound to the specific field from a specific instance that the iterator is currently on.  The inputfield knows nothing of your list of sobjects.  When a user updates the field and clicks the go/submit/whatever button, the value that the user has input is updated into the specific field of the specific sobject that it was bound to.  As this object was present in the this.list_tabacc, if you retrieve the object from the list you will find that it has been updated with the value that the user has entered. Thus if you present the existing list_tabacc back to the user, you will find that it has the latest values.  Effectively you are passing a reference to the object field to the page, and when the user submits the form the field value for the object in the list is updated directly, rather than the list itself being sent back and forth.

 

Assuming you have a commandbutton/link with an action attribute of a method, when that button/link is clicked the contents of the list will already have been updated with the latest values by the time your method executes.  

 

This is how the information is returned to your controller.  There is no way to invoke a setter object that will receive an updated copy of the entire list, rather your list will have changed underneath you based on the users inputs.

 

Does this help at all or am I just adding to your confusion?

 

 

All Answers

bob_buzzardbob_buzzard

Setters are called automatically when a page is submitted in order to apply the changes made in the page to the data stored in the controller.

 

However, the setters that are called are on each of the underlying Table_Account__c objects in the list. rather than on the list that is backing the datatable.

 

Presumably you have an action component on the page that is submitted the form back the server.  That being the case, by the time your action method is invoked on the server, all of the changes made by the user will have been applied to the objects from the controller that are backing them. 

guyr1981guyr1981

O.k., but what should be the name for my setting API, so that it would be called and not the Standard API?

bob_buzzardbob_buzzard

I think there's some confusion here.  The setters that are called are the individual field setters for the sObjects used to back the input fields in the datatable.    VisualForce decides which setters to call based on the object itself - its not something that you can override.  

 

The objects in your controller are updated so the values the user has entered are available to your action method.  I'm struggling to understand what your own setters would add to this - can you explain a bit more about the use case.

guyr1981guyr1981

My setters are supposed to get the new fields values after the user changes, compare it to the DB to see which specific fields of my Sobject were changed and then change other fields according to that change.

 

For example:

I have a field called "total" (in each object), the" total" holds the sum of all other fields in that object, if one of the fields was changed I want to update this field.

If the user changed the total field, I would like to go over all other fields and add to each an equal portion so that the sum of all fields after the change would be equal to the new "total" value (that was set by the user) and after I make this changes I want to save it back to the DB.

 

I guess my question is:

Lets say, I don't have a standard controller and I would like the user changes to go back to my controller so that I can manipulate them and only then save the data. what should be the name of my setter?     void setPitem(Table_Account guy)? 

 

Thanks,

Guy

bob_buzzardbob_buzzard

The setting of the values from the input fields has nothing to do with your controller - the setters are invoked based on the objects that are backing the input field  

 

So for this line:

 

<apex:inputField value="{!pitem.test2__c}" style="width: 80px"/>

 

there is a single Table_Account__c object instance in your list from the controller that is backing the input field (pItem being the iterator for that list).  When the page is submitted back. VF effectively executes the setter for the Test2__c field.  You can't do anything about this, as these are standard objects.

 

The only way that you could have your setter called, would be to create a wrapper class (with setter) for the Table_Account__c object, populate your list with instances of this class instead of Table_Account__c records and then carry out processing in the setter as required.  You wouldn't be able to use these classes for inputfield components though, as those only work with sobejct fields.

 

If you need to know what has changed between the page being displayed to the user and submitted back to your controller, you will need to create a snapshot of the list as originally constructed (e.g. create two copies in the getTabAccounts method), and then in your action method traverse the old and new list and compare each item individually. 

guyr1981guyr1981

First of all, thanks for trying to help, I appreciate it.

 

However, I still don't understand.

Lets say I have no standard controller (which is probably going to happen anyway since I don't really need it anymore).

 

So, I have just one Controller=MyController.

 

public MyController {

Table_Account__c tabAcc;

List <Table_Account__c> list_tabAcc;

 

MyController() {

.....

....

  }

 

// getTabAccounts gets all Table_Account__c records into my list: list_tabAcc and return list_tabAcc to the dataTable to iterate on and present its objects (and the objects fields).

public List<Table_Account__c> getTabAccounts() {
        this.TabAccount = [SELECT name, id, account_name__c, jan__c, feb__c, mar__c, April__c, may__c, june__c,
                                        Total__c from Table_Account__c];
        System.debug(Logginglevel.DEBUG, '##### I am in Get ###################################################');
        return this.TabAccount
    }

 

public List<Table_Account__c> getTabAccounts() {

        this.list_tabAcc = [SELECT name, id, account_name__c, test1__c, test2__c, test3__c,Total__c from Table_Account__c];

        System.debug(Logginglevel.DEBUG, '##### I am in Get ####################################');

        return this.list_tabAcc;

}

 

 

 

 

And my attribute code is:

 

<apex:page standardStylesheets="false" sidebar="false" Controller="MyController" tabStyle="Account">

....
....
....

<apex:pageBlock title="Planing Table" id="thePageBlock" mode="edit">

     <apex:dataTable value="{!TabAccounts}" id="outputTable" var="pitem" columns="10" cellPadding="6">

          <apex:column headerValue="Test1">

          <apex:inputField value="{!pitem.test1__c}" style="width: 80px"/>

           </apex:column>

          <apex:column headerValue="Test2">

          <apex:inputField value="{!pitem.test2__c}" style="width: 80px"/>

          </apex:column>

 <apex:column headerValue="Test3">

          <apex:inputField value="{!pitem.test3__c}" style="width: 80px"/>

          </apex:column>

 

  <apex:column headerValue="Total">

          <apex:inputField value="{!pitem.total__c}" style="width: 80px"/>

          </apex:column>

 

...

...

...

 

 

Lets say I want to update "list_tabAcc" with the new values changed by the user so that the next time I present the dataTable, I will have the new values.

 

Is that in anyway possible and if so, how do I do it? should I write a setter API in that case?

if so, what would be the declaration for such an API?

 

 

Thanks again,

Guy

 

 

 

 

 

 

 

 

 

 

bob_buzzardbob_buzzard

No problem.  This is an important feature to understand, as it is the cornerstone of how forms are backed by variables in visualforce.  For my money its the most elegant solution I've come across.

 

The short answer is that you can't do what you would like to do.

 

The long answer is:

 

Your getter is returning a list of sobjects (Table_Account__c) which is used by a datatable that has the capability to iterate the values and do some stuff with them.  This list is not copied to the page or retained in any way so that it can be sent back to the controller, it is simply iterated and the body rendered once for each element in the list.

 

The body of the datatable is repeated for each element in the list.  Inside the body of the datatable, when you have an inputField component, this is bound to the specific field from a specific instance that the iterator is currently on.  The inputfield knows nothing of your list of sobjects.  When a user updates the field and clicks the go/submit/whatever button, the value that the user has input is updated into the specific field of the specific sobject that it was bound to.  As this object was present in the this.list_tabacc, if you retrieve the object from the list you will find that it has been updated with the value that the user has entered. Thus if you present the existing list_tabacc back to the user, you will find that it has the latest values.  Effectively you are passing a reference to the object field to the page, and when the user submits the form the field value for the object in the list is updated directly, rather than the list itself being sent back and forth.

 

Assuming you have a commandbutton/link with an action attribute of a method, when that button/link is clicked the contents of the list will already have been updated with the latest values by the time your method executes.  

 

This is how the information is returned to your controller.  There is no way to invoke a setter object that will receive an updated copy of the entire list, rather your list will have changed underneath you based on the users inputs.

 

Does this help at all or am I just adding to your confusion?

 

 

This was selected as the best answer
guyr1981guyr1981

Hi,

 

o.k., I've reread your answer and only now understood it (I think).

 

But, if I understand correctly, after pushing the "go/submit/whatever" button assuming that the button does not update the data base.

I actually have my list: list_tabacc which is already updated with the values entered by the user.

 

And I also have the DB which was not changed yet and has the old values, so if that is correct, I can compare (on my "go/submit/whatever" button)  the value of each object in my list_tabacc to the values of the corresponding object in the DB, change the values as I want in list_tabacc and then save list_tabacc with my changes to the DB.

 

Hope I understood correctly, going to give it a try now.

 

Thanks again,

Guy

bob_buzzardbob_buzzard

As long as your button isn't invoking any save/quicksave methods from the standard controller, I would expect your plan to succeed.

guyr1981guyr1981

Yes,

 

it did.

 

Thanks a lot for all the help!!!

bob_buzzardbob_buzzard

No problem - glad you got there in the end.