Pages

Wednesday, January 7, 2009

Allow Mass Update of your Object Records Even Without Having a Save Button!

Today we want to explore one of the beauties of the Visualforce technology.
Those who have a history of witting ASP or ASP.NET code will admit that Visualforce has made it all too simple to develop business applications.

Let's say we want to be able to update several records of a certain Object all together providing ability for users to quickly make modifications and save them.

This is particularly useful when the data in nature changes often and it's time consuming to do the updates on a record by record basis.

In this example, I will demonstrate a Visualforce page which has a search box through which user can search Accounts and view a list of records where he or she can modify the information inline and then proceed to the next record without clicking on a save button of some sort!

This is very much as easy of entering data into cells of an excel sheet! The only difference is that the Excel needs you to click on the save button at the end but our Visualforce page won't!

I will use an actionFunction tag to create a javascript function which in turn will trigger a method of my Apex controller class. This way when the value of one the input controls changed I can call that javascript function and boom the change would get posted back to the controller and will be saved.

This the how Visualforce page is like:

<apex:page tabStyle="Account" controller="massAccountUpdateCon">
<apex:sectionHeader title="Accounts Mass Update"></apex:sectionHeader>
<apex:form >
<apex:pageBlock title="" id="pageBlock">
<!-- This block will show the search textbox and the Search button -->
<apex:pageBlockButtons location="top">
<apex:inputText value="{!keywords}" style="height:15px;"></apex:inputText>
<apex:commandButton value="Search" action="{!ViewData}" id="theButton" rerender="pageBlock" status="status"></apex:commandButton>
</apex:pageBlockButtons>
<!-- To show page level messages -->
<apex:pageMessages ></apex:pageMessages>

<!-- The below tag will provide a javascript method which when is called in turn will call a controller's method -->
<apex:actionFunction action="{!UpdateRecords}" name="updateRecords" rerender="pageBlock" status="status"></apex:actionFunction>

<!-- This table contains columns which have inputfield components -->
<apex:pageBlockTable value="{!accounts}" var="a" rendered="{!NOT(ISNULL(accounts))}">
<apex:column>
<apex:facet name="header">Name</apex:facet>
<apex:inputField value="{!a.Name}" onchange="updateRecords();"></apex:inputField>
</apex:column>
<apex:column >
<apex:facet name="header">Phone</apex:facet>
<apex:inputField value="{!a.Phone}" onchange="updateRecords();"></apex:inputField>
</apex:column>
<apex:column>
<apex:facet name="header">Billing City</apex:facet>
<apex:inputField value="{!a.BillingCity}" onchange="updateRecords();"></apex:inputField>
</apex:column>
<apex:column>
<apex:facet name="header">Billing Country</apex:facet>
<apex:inputField value="{!a.BillingCountry}" onchange="updateRecords();"></apex:inputField>
</apex:column>
<apex:column>
<apex:facet name="header">Industry</apex:facet>
<apex:inputField value="{!a.Industry}" onchange="updateRecords();"></apex:inputField>
</apex:column>
</apex:pageBlockTable>

</apex:pageBlock>

<!-- The action status to show when the AJAX postback is wroking. -->
<apex:actionStatus id="status" startText="Requesting..."/>
</apex:form>
</apex:page>





Now the Controller, this is where the simplicity of coding can be visibly seen!
As you can see in the Controller's source code, I do not need to write any code to find which column's value was changed! All I need to do is to update my List of Accounts!
Visualforce will take care of all those details!


public class massAccountUpdateCon {

private List<Account> accounts;

public List<Account> getAccounts() {
return accounts;
}

public string keywords {
get;
set;
}

public PageReference ViewData() {
//dynamically build the query to insertthe filter values
String query = 'Select id, name, type, ownership, industry, phone, BillingCity, billingCountry FROM account WHERE name LIKE \'' + keywords + '%\'';

accounts = Database.query(query);

return null;
}

public PageReference UpdateRecords() {
// this simple line of code finds out which column was changed and update the
// relevant account record accordingly!
update accounts;
return null;
}
}




11 comments:

  1. I know this is probably the last thing you care about- but it would be helpful if you placed test cases for your examples. Some of us just aren't as awesome as you are. Another great post! I visit this blog at least 2-3 times a week.

    ReplyDelete
  2. Hi Mike,
    Thanks for your support.
    I think I have that convered as well, kindly see one of my older posts on how you can get a good coverage on your test methods

    ReplyDelete
  3. One thing which remain unnoticed is that if we update fields very fast then all your changes are not reflected.

    As soon as you update a field, on change event for that field is called which further in turns call controller function and which re renders your pageblocktable. During this entire process if you updated any other field then obviously it will be emptied back or not reflect because of re render of pageblocktable

    ReplyDelete
  4. Hi CM,
    I had noticed that problem, even though most often users quickly learn to give time for the data to be saved, if you are very exception savvy, then I suggest using the below action Status component:

    Visualforce Component that Beautifies actionStatus!

    This component blares out the page and make all input components inaccessible while doing AJAX post-backs.

    ReplyDelete
  5. Is there a way to do something similar but rather than a simple update do a merge?
    How about a merge assistant for Accounts and Contacts?

    ReplyDelete
  6. Sam,

    Thanks for this blog. It's often very useful. I have adapted your code for a similar purpose (a list of Campaign Members) but, oddly, have found that the when the "UpdateRecords" function is called, the changes are not saved. Instead the original values re-assert themselves.

    Does anybody know why this may be?

    Andy

    ReplyDelete
  7. Thank You very much Now I can update the records easily. rajesh

    ReplyDelete
  8. This is awesome do you have a way to when they search to do like an autocomplete?

    ReplyDelete
  9. This is exactly what I was looking for!! You saved me a great deal of headache! Thank you

    ReplyDelete
  10. Hi,
    This a nice bit of code.I have one question .How and when is your actionfunction firing.In other words which even is firing your actionfunction

    If u can give some explanation on this.

    Thanks
    Trish

    ReplyDelete
  11. your code are best in industry but if you include a apex class and page in which paging,sorting,indexing,grouping,searching for the records done altogether then i think that post make your blog extraordinary. Thanks

    ReplyDelete