Pages

Saturday, September 12, 2009

Edit and Delete Command For Your Datatable In Visualforce


Most often that not you need to show data to the users in form of a table, such as Salesforce List Views or Related Lists.

Visualforce allows us to easily implement such page with a few lines of code. However, most often you would want to be able to add more interactivity/functionality to your page by supporting Edit or Delete command or any other custom operation that is required by design.

Today's article will show you how you can get that working for you.



Above picture depicts how the final result will look like, very much like Salesforce.com standard pages, isn't it?.

In this example I list the first twenty Account records that Apex finds and show then on the screen and then I would like to have actions such as "Edit" that forwards the user to Account edit page and brings the user back to my page again after the modifications are done and also "Delete" action that receives a confirmation from user and removes an Account record from Salesforce CRM.

So let's talk about the User Interface (UI) components required first and then we will look into the controller's code to see how it all works together.

Steps to add the page tags:
  • I first add a PageBlock component and set the title to "Accounts"
  • Then I want to put a PageBlockDataTable in the my Visualforce page.
  • In this step I simply add all the data columns I want to show to the user and leave the first column empty for the next step.
  • Now we are ready to add the action items to the first column of the dataTable.
Below is the page tags that I have used to make it all happen:


<apex:page controller="DataTableEditRemoveController">
<apex:form id="form" >
<apex:pageBlock title="Accounts">
<apex:pageMessages ></apex:pageMessages>
<apex:pageBlockTable value="{!accs}" var="row">
<apex:column >
<apex:outputLink title="" value="/{!row.id}/e?retURL=/apex/{!$CurrentPage.Name}" style="font-weight:bold">Edit</apex:outputLink>&nbsp;|&nbsp;
<a href="javascript:if (window.confirm('Are you sure?')) DeleteAccount('{!row.Id}');" style="font-weight:bold">Del</a>
</apex:column>
<apex:column value="{!row.Name}"/>
<apex:column value="{!row.BillingStreet}"/>
<apex:column value="{!row.BillingCity}"/>
<apex:column value="{!row.BillingPostalCode}"/>
<apex:column value="{!row.BillingCountry}"/>
</apex:pageBlockTable>
</apex:pageBlock>

<apex:actionFunction action="{!DeleteAccount}" name="DeleteAccount" reRender="form" >
<apex:param name="accountid" value="" assignTo="{!SelectedAccountId}"/>
</apex:actionFunction>
</apex:form>
</apex:page>



How It Is Done For Edit Action:
Since the standard Account edit page is good enough for me I used an outputLink component to link the record to its Standard Edit page. In Salesforce for standard objects you can follow this format to get the URL for their edit page: /{RecordId}/e

I also wanted this to work in such a way that would return the user back to my Visualforce page once the user clicks on "save" or "cancel" in the Account edit page. In order to do that I need to add the following to the URL: /{RecordId}/e?retURL={returnURL}

In the Page's source code (above) you see that for return URL I have used {!$CurrentPage.Name} merge field, where I could simply put my page name. I like writing code clean! By doing this if you later on decided to change your page name, you do not need to worry about breaking anything! You page will continue to work with no problems!

How It Is Done For Delete Action:
In order to support this action in your page you need to do a bit of coding. The key is to be able to find out which account was selected by the user to be deleted.

In this example I have used a actionFunction component that triggers a Controller's Apex method call "DeleteAccount".

Before getting more into the coding part I wanted this link to get a confirmation from the user about deleting the record before actually we remove it.
In order to do so, we need to use a little bit of Javascript "window.confirm", the javascript function returns true if the user clicks on OK and false if the user selects "Cancel".

In order to capture the user's selection as to which account should be deleted I have added a "param" tag to the actionFunction component which passes the Account ID to the controller and as soon as user clicks on the link.

Now let's take a look at the code:


public class DataTableEditRemoveController {

public List<Account> accs { get; set; }

//used to get a hold of the account record selected for deletion
public string SelectedAccountId { get; set; }

public DataTableEditRemoveController() {
//load account data into our DataTable
LoadData();
}

private void LoadData() {
accs = [Select id, name, BillingStreet, BillingCity, BillingPostalCode, BillingCountry from Account limit 20];
}

public void DeleteAccount()
{
// if for any reason we are missing the reference
if (SelectedAccountId == null) {

return;
}

// find the account record within the collection
Account tobeDeleted = null;
for(Account a : accs)
if (a.Id == SelectedAccountId) {
tobeDeleted = a;
break;
}

//if account record found delete it
if (tobeDeleted != null) {
Delete tobeDeleted;
}

//refresh the data
LoadData();
}


}




Funny thing is that in my tests if you do not set the "reRender" attribute of the "actionFunnction" component the param is not passed to the controller and the "SelectedAccountId" property is not populated. Go figure....

Also I really wanted to get it working with commandLink or commandButton components instead using actionFucntion and javascript, but was not so lucky!

Enjoy!

31 comments:

  1. Nice post Sam! Very clean and thorough.

    ReplyDelete
  2. Another good approach.

    ReplyDelete
  3. Hi Sam.

    I've integrated your great solution into my own Visualforce page but I can't seem to get it to actually delete the record. I'm having trouble understanding something.

    In the acionFunction apex code, how does the Account ID value populate SelectedAccountId? Where is Account.Id being read?

    ReplyDelete
  4. Hi Samson
    Take a close look at the link HTML tag that creates the "Del" hyperlink. That Hyperlink calls our actionFunction javascript and passed the account Id to it. In turn the actionFunction component writes that value to one of our controller's properties.

    ReplyDelete
  5. Oops!

    Turns out all I did was mispelled the method called into the javascript. Thanks a bunch, Sam!

    ReplyDelete
  6. probably would be better to put accounts in a map rather than a list. then the for loop to check if account exists to be deleted can be converted to:-

    if(accountMap.containsKey(SelectedAccountId))
    delete accountMap.get(SelectedAccountId);

    ReplyDelete
  7. hello Sam,

    Thanks for this post, it really helped me out. As a fresher to Salesforce its really very helpfull.I like the way you explained, its very simple and easy to understand. But I hv one doubt in the above post. Could u please clease my doubts?

    Please find the code shown below:-

    DeleteAccount('{!row.Id}');" style="font-weight:bold">Del.
    .
    .
    .
    param name="accountid" value="" assignTo="{!SelectedAccountId}"/>

    My question:-

    How do that {!row.id} assign value to "accountid" param? I am not understanding hows it possible?

    "Deleteaccount" is a method, and more over its not expecting any parameter, then how we pass that value?

    ReplyDelete
  8. Hi ashii
    This exactly the point, in Visualforce your methods can not accept arguments. Therefore, you write your methods with no parameters and define a property on your controller class which will be accessible within your method.

    By using the "apex:param" tag you can set that property and access the value of it within your method code.

    cheers
    Sam

    ReplyDelete
  9. ... Why can't be simple like this? You don't need any action method for it...

    ReplyDelete
  10. hi Sam,

    Thanks for the wonderful code. But I am facing some issues when I try to use your approach from a component. Please help me out to solve this.

    I am calling a "custom component" in one VF page. My "delete" functionality is in custom component. the problem I am facing is:

    When I try to assign a value through which is in custom controller, its not setting the value.

    Hope you will help me out to solve the issue.

    Please find below some codes for your reference:

    My VF Page (this will call the component)
    ------------------------------------------







    ------------------------------------------------

    My custom component (here is my delete functionality)

    ------------------------------------------------




    //**** Action function, here I am assign value using param






    // **** Delete call
    Delete


    Hope the above code will help for your reference.
    Thanks in advance!!

    Asish S

    ReplyDelete
  11. Realy Technical Rich Article.

    ReplyDelete
  12. What if your account did not yet have an id? Say you had created a table that can be used to do a mass insert. Inserts a new account for each row upon save.

    ReplyDelete
  13. I am Using List in that list list i binded with Web Service Output,

    i Followed above mentioned Approach but the Void method is not calling?

    ReplyDelete
  14. Hi, My project includes a page where in I have to allow the records to be editd or deleted. But when i delete a particular record it should only be deleted from the page displaying the records and not from the standard objects records. so how do i go about doing it?? Please help.

    ReplyDelete
  15. Really cool stuff. Helped me a lot while writing delete in custom objects.

    ReplyDelete
  16. my question is:
    i have a look up of class ...and on the behalf of that i want to show the name and city in the data table...hows this possible???

    ReplyDelete
  17. Nice Article Sam. Afarin va Dorood bar to :)) helped me out alot....

    ReplyDelete
  18. Thank You Sir, your tutorial is very useful.

    ReplyDelete
  19. its is really good article.. i used this in my scenario it works fine but i m facing an issue, record is deleted from the system but page doesn't refresh so user think it was not deleted but in actually it is..what i missed to refresh page after delete record.. can any one guide me.
    thanks
    regards Atif ali
    atifalikhan@hotmail.com

    ReplyDelete
  20. how i pass the table record to the vf page and edit it in the page

    ReplyDelete
  21. Thank you for this amazing post... it really helped me a LOT!!

    ReplyDelete
  22. Hi your post is very nice,
    i want when i click on edit i want edit the particular row record in the visualforce page only how can i do it ,can you help on this


    Regards
    venkatesh

    ReplyDelete
  23. Thanks for this post.It help me a lot

    if i want to approach edit button functionality with Apex code juz lik Del in above.How cud i approch it?
    Thanks in advance
    Ishan K Sharma

    ReplyDelete
  24. Great post... worked like charm. You saved my day :) Thanks a lot.

    ReplyDelete
  25. Hi,
    Using schema methods with wrapper class how to delete objects and related fields from database using visualforce page and apex class

    please help me.......

    ReplyDelete