The effort is minimized this way since you do not want to recreate the layout using a Visualforce page, all you need is to be able to launch a method once the button is clicked on.
In order to do so, you need to write your logic into an Apex class with following conditions:
- Firstly, your class should be marked as "Global"
- Secondly, the logic goes to a static method of this class which is marked as "WebService"
I think by now you have a good idea of where I am going with this, so let's dive into code and examine everything more closely.
Below I have created a Apex Class called "OutboundEmails" and added a method that has a keyword as "WebService".
global class OutboundEmails {
WebService static void SendEmailNotification(string id) {
//create a mail object to send a single email.
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
//set the email properties
mail.setToAddresses(new string[] {'myemail@domain.com'});
mail.setSenderDisplayName('SF.com Email Agent');
mail.setSubject('A new reminder');
mail.setHtmlBody('an object with ID='+ id + ' is just clicked on.');
//send the email
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail } );
}
}
Now let's concentrate on the button that will actually call our WebService method.
Firstly, I create a detail page button let's say on Account object and name it "Send Me ID".
This button's behavior will be "Execute Javascript".
If you need more information about how you can add a custom button to Account object please click here. Then I add the following code to the body of the button edit page:
{!REQUIRESCRIPT("/soap/ajax/10.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/10.0/apex.js")}
sforce.apex.execute("OutboundEmails","SendEmailNotification", {id:"{!Account.Id}"});
window.alert("Account Id is sent." );
Once you click on the button, in case you have not forgotten to change the sample email address with your own in the code, you should receive the email.
Any reason that you've chosen to do this with a WebService and javascript in lieu of a Visualforce page or are these just two means to the same end?
ReplyDeleteThanks Sam, this is exactly what I was looking to do. It's cleaner than the round-about way of calling a visual force page that calls the apex code. The only thing I added was a simple javascript page refresh to see the changes that were made by my Apex code. It's not as nice as returning a PageRefresh object but it doesnt seem like that works in this method anyways.
ReplyDeleteHi,
ReplyDeleteThe code was working fine till i packaged the App .
Now its giving error
faultcode:'soapenv:client' faultstring:'No service available for class 'SendEmailNotification'
please help
You need to prefix the package name in the call.
ReplyDeletesforce.apex.execute("MyPackage/MyClass".....
Many institutions limit access to their online information. Making this information available will be an asset to all.
ReplyDeletegreat i just need it
ReplyDeleteHi Sam,
ReplyDeleteThank you for this post. I'm using this approach to run some custom code on my SFDC system and I was wondering how you test the web service for deployment to production? I've read a few other posts about that but nothing is working for me so far.
Thank you,
Ian
This is good but can you also provide an example of how to write test methods for these classes that have web service methods.
ReplyDeleteThanks
Is this possible in Professional Edition? I know you can't have classes, but is there a way around it?
ReplyDeleteHow do you write a test case for this?
ReplyDeleteThe JavaScript method does not need test code. Only apex classes need test code.
ReplyDeleteSalesforce only enforces testing of apex classes, but that doesn't mean you shouldn't test your JavaScript code as well. QUnit is a simple JavaScript testing framework. Jasmine is also very popular.
DeleteThanks for the reply. I have gone through and added test functions for all of the features in the global class. But SF does not seem to pick it up as it being tested. My button creates a "case" from the "opportunity" and fills in the case with as much details as possible from the opportunity.
ReplyDeleteI think I figured it out... You need to basically call that class and function from one of your test cases.
ReplyDeleteOnce you push the button and the Apex code runs, how do you tell the Apex code to open a page in their browser and point them back the object you just created? In my case I am creating a Case from the Opportunity. Once I press the button the Opportunity, how do I open the Case that I just created?
ReplyDeleteThis is one way to skin the cat, but it seems like a hugely expensive way of making a call to an Apex class from within Salesforce. Instead, just define the button to call a VisualForce page.
ReplyDeleteYou should be able to define a one-line VF page with your Apex as the controller, and your method invoked in the Action of the tag.
It also gives you some flexibility in that the controller can redirect the UI to a different location than the launch page depending on your logic.
There's slightly more work if you want to pass parameters, but it's very simple, less awkward, doesn't cost roundtrips, etc.
Also, you may not want to define all these Apex classes as globals and webservices which have different security considerations.
Best, Steve B.
Hi,
ReplyDeleteI´m not sure if this post is still alive...
I´m trying to make a button just change the value of a field (Picklist). Could you please give me some advise on how to make the web service do this??
Thanks on advance.
Now, with Visual Force, this is much easier. Just create a VF page that is associated with a standard *and* a custom controller. In the custom controller, define a method called "autorun" and in the apex page tag, set action="{!autoRun}". Then define the custom button as a detail page button defined as a VF page. Have the autorun method redirect the page as appropriate if the action succeeds (to a confirmation message, eg), and have it display an error message if the action fails.
ReplyDeleteVery helpful comment and very useful article. The comment should be added as required update for the article.
DeleteThanks
Thank you for this post! I wrote a great Button/Apex combo that solved a huge need.
ReplyDeleteUnfortunately, while it works perfectly in the Developer org I wrote it in, it does nothing in production when clicked. I launched it into production through a Managed Package. Any way to make it work in my installed production environment?
Simple.
ReplyDeleteElegant.
Easy.
One line of code to call a class?! Nice!
Thanks Sam, this is exactly what I was looking to do. It's cleaner than the round-about way of calling a visual force page that calls the apex code. It would be great as per administrative perpose.
ReplyDeleteWhen I use a button to a VF page that run's some apex to create a new case, or a new contact, etc; if the user hits the back button in the browser, it goes to the VF page, which then fires the apex code and creates another new case or contact.
ReplyDeleteUsing JS avoids the VF page, but can I direct the user to the new case or contact page?
If an Org is using name-space then , syntax has to be like this>>
ReplyDeletesforce.apex.execute('OutboundEmails','SendEmailNotification', {id:'{!Account.Id}'});
Hi ,
ReplyDeleteIf your Org has NameSpace then syntax has to be like this >>
sforce.apex.execute('TPSF/OutboundEmails','SendEmailNotification', {id:'{!Account.Id}'});
Thanks,
Mayank Joshi
Hi
ReplyDeleteI need to call batch apex on custom button click.
Can i do it?
Can't tell you how helpful this blog post has been to us. Go to sleep tonight knowing that there is at least one person in the world who is ultra thankful for your contributions to the Salesforce community.
ReplyDeleteThank you Sam!!!!!
David
Very educational, both specifically and generally.
ReplyDeleteHi,
ReplyDeleteVery useful. Is there a way to add an 'Are you sure?' type dialog so the user can accept before going through with the actions performed by the button?
Thanks!
ISVforce partners WARNING!!
ReplyDeleteThis will not work for Professional edition and lower. Salesforce does not allow you to call a webservice from such orgs, does not matter if your app is a managed app.
Works good...but what if email address is not valid?
ReplyDeleteHow to get the respond from mail server about delivery notification failure and inform a sender?
Salesforce is handling the email sending, check Salesforce documentation to see how they handle the invalid emails.
ReplyDeleteif there is possible to call the trigger in the same way
ReplyDeleteNo, You cannot call a trigger, Salesforce database does that when an event takes place such as insert, update or delete.
DeleteHello sam,
DeleteI want to set current time for a field when the new button was clicked.I have some doubt in this,if i click button now with out completion of the event,i have clicked button again after 4 minutes then the old value will replaced with new time value or does the value remain same.
The value remain same
DeleteNice post. Thanks!
ReplyDeleteVery useful post indeeed. I was looking for exactly the same functionality and am a newbie to SF. My org does not want to create VF pages, so they want all these calls to happen in onclick javascript. I spent long time in researching and all the solutions I found needed VF pages. Your post was really helpful.
ReplyDeleteThanks,
MK2013
I am calling my webservice in the same way described in the post. I have one question though. Being a newbie to SF this might be a dumb question. I call the webservice in onclick javascript button which is a custom button on a custom object. This object is placed in standard opportunity layout and does not have a VF page of it's own. The question is I need to refresh the page after the webservice call returns, this will select some checkboxes in the custom object. I tried using:
ReplyDeleteparent.location.href = url;
where url = parent.location.href;
This did not work so I tried
window.location.reload();
This works but the page reload happens before the webservice returns so corresponding UI change in the checkbox does not happen.
Gurus, can you give me some hint on how to get the page refresh AFTER the webservice call returns?
Thanks,
mk2013
Very helpful post Sam.So is it possible to update any field by clicking on this button.
ReplyDeleteThanks,
Baji Shaik
Hi Sam,
ReplyDeleteHow can I pass values which will be recired by a webservice method having list of wrapper class as a parameter.
global without sharing class WebsiteWS
{
global class wsAttendeeDetails
{
webservice String websiteOrderId;
webservice Id productId;
}
webService static wsOutputs wsPopulateOppEventContactDetails(List attendeeDetails)
{
system.debug('Inside wsPopulateOppEventContactDetails');
system.debug('wsAttendeeDetails attendeeDetails - ' + attendeeDetails);
return null;
}
}
How should is populate sforce.apex.execute("WesiteWS", "wsPopulateOppEventContactDetails", "????") for "????" here.
Please reply.
Best Regards,
Rahul