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> |
<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!
Nice post Sam! Very clean and thorough.
ReplyDeleteAnother good approach.
ReplyDeleteHi Sam.
ReplyDeleteI'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?
Hi Samson
ReplyDeleteTake 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.
Oops!
ReplyDeleteTurns out all I did was mispelled the method called into the javascript. Thanks a bunch, Sam!
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:-
ReplyDeleteif(accountMap.containsKey(SelectedAccountId))
delete accountMap.get(SelectedAccountId);
hello Sam,
ReplyDeleteThanks 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?
Hi ashii
ReplyDeleteThis 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
... Why can't be simple like this? You don't need any action method for it...
ReplyDeletehi Sam,
ReplyDeleteThanks 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
Realy Technical Rich Article.
ReplyDeleteWhat 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.
ReplyDeleteI am Using List in that list list i binded with Web Service Output,
ReplyDeletei Followed above mentioned Approach but the Void method is not calling?
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.
ReplyDeleteReally cool stuff. Helped me a lot while writing delete in custom objects.
ReplyDeletemy question is:
ReplyDeletei 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???
Superb Article
ReplyDeleteSuperb Article
ReplyDeleteNice Article Sam. Afarin va Dorood bar to :)) helped me out alot....
ReplyDeleteThank You Sir, your tutorial is very useful.
ReplyDeleteits 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.
ReplyDeletethanks
regards Atif ali
atifalikhan@hotmail.com
how i pass the table record to the vf page and edit it in the page
ReplyDeletehi thanks
ReplyDeleteHi your post is very nice,
ReplyDeletei 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
Thanks for this post.It help me a lot
ReplyDeleteif 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
Great post... worked like charm. You saved my day :) Thanks a lot.
ReplyDeleteHi,
ReplyDeleteUsing schema methods with wrapper class how to delete objects and related fields from database using visualforce page and apex class
please help me.......
its ok ok
ReplyDeletesuperb
ReplyDeleteSuperb It's really helpful..
ReplyDelete