I’ve been doing a lot more client side dev lately and a big piece of this is working with JavaScript Remoting. While doing this work I found that doing basic CRUD operations with JavaScript Remoting was not as simple as it could be. I didn’t want to have to wire up insert, query, update, and delete functions for each type of salesforce.com object I was working with. I wanted a lightweight and fast JavaScript library that was completely dynamic and allows you to build and entire app client side utitlizing basic CRUD operations.
The result of this need is a small JavaScript library I created called sObject-Remote.
sObject-Remote is up on github now and I encourage you to take a look and kick the tires. There should be plenty of examples in the readme to give you an idea of how this library can be used. Here are a couple basic examples:
Create and Insert a record:
var acct =new sObject('Account',{Name:'test', Industry:'Aerospace'});
acct.insert(function(result,event){
console.log(result[0].Id);});
Query Records:
sObject.query('select Id, Name, Owner.Name from Account where Industry = \'Aerospace\' limit 5',function(sObjects,event){//Loop through the records returned by the queryfor(var i =0; i < sObjects.length; i++){
console.log(sObjects[i]);}});
Some of the highlights:
- Super simple sObject management for JavaScript
- Currently supports 4 basic CRUD operations (more DML operations to come)
- Supports DML options
- Works with any JavaScript framework
- No 3rd party JavaScript library dependencies
A good portion of this development was me becoming more familiar with JavaScript so I am definitely open to community feedback on how to make it better. Please report issues and let me know what you think.
First off, I’m back! You may have noticed my blog posts and community involvement over the past 6 months has been almost nonexistent. In my previous job I had slowly started to move alway from being involved in app development and lacked the daily intercourse with code that inspired so many of my previous posts. If you haven’t heard I left my previous job of six years to join Arrowpointe and together with Scott Hemmeter we plan to do great things in the geolocation space. In my new position as Director of Application Development I am more involved once again with building apps and solving technical problems. So now…let’s talk about JavaScript Remoting and formatting dates on the client side.
If you don’t already know, JavaScript Remoting is awesome. It allows you to directly call Apex methods from JavaScript, returns the response in JSON, and is super snappy and reponkinsive (sound). You can read more about it here. What is not awesome about JavaScript Remoting is how it handles Date and DateTime fields. In actuality it handles these fields fine but the platform itself leaves something to be desired. Let’s take a look at the following JavaScript that returns the CreatedDate field from an Account.
sObject.query('select Id, Name, CreatedDate from Account limit 1',function(result,event){
console.log(result[0].CreatedDate);var d =newDate(result[0].CreatedDate);
console.log(d);});
The output will be the following.
1362028097000 //The number of milliseconds since January 1st, 1970
Wed Feb 27 2013 21:08:17 GMT-0800 (PST)
(Oh…and if you are wondering what that sObject.query() magic is, stay tuned….that is the subject of an upcoming blog post.)
Hmm… well neither of those are too pretty and being the astute global developers we are we want to make sure the Date/Time is formatted to the users locale. Something like “2/27/2013 9:08 PM” in America and “27/02/2013 21:08″ in UK. Let’s go follow the options I went through trying to figure this out.
1) JavaScript Date.toLocaleString() The Date object in JavaScript does have a toLocaleString() method but don’t even think about using it. Frist, it’s inconsistent and doesn’t always work like you would expect it to. Second, when salesforce.com returns the time portion of a date it is already adjusted based on the users selected time zone, which is nice, but toLocalString() method shifts it again making the time wrong.
2) Use a Library like jQuery Localize or Date.js Yes, this will be perfect! False. I thought I could use the salesforce.com locale values in tandem with these libraries but the values salesforce.com uses for locales are in a non-standard format. It’s actually not salesforce.com’s fault as I think (hope) they created their whole localization engine and rules before these standards where in place. The two options here is to have users enter and store another locale value that is standards compliant. Or for each of the existing salesforce.com locale values (100+) create a little JavaScript file that has the formatting rules. Neither of which is ideal.
3) Go to stackexchange.com and as for help Here someone pointed me to a JavaScript object called UserContext (can view by entering UserContext in browser console when logged in to salesforce.com) and it provides all sorts of great info about the user but specifically it included the Date and DateTime masks for date formatting, M/d/yyyy and M/d/yyyy h:mm a. Progress! Yet after looking for a library that could take this format and parse it correctly I once again came up dry. After looking at UserContext object in more detail and digging through some other salesforce.com JavaScript files I found the solution.
Buried within the salesforce.com JavaScript files there is an object called DateUtil with two very useful methods, getDateTimeStringFromUserLocale(date) and getDateStringFromUserLocale(date). These two methods do exactly what they look like. You pass in a normal JavaScript date variable and they will return a nice formatted String based on the user’s selected locale.
So we are good now right? Not exactly. At the very top of the JavaScript File in which these methods exist is this nice little message from salesforce.com:
/*
* This code is for Internal Salesforce use only, and subject to change without notice.
* Customers shouldn't reference this file in any web pages.
*/
Well, that’s no fun! But rules were meant to be broken right…..right? Ya! We are hardcore rule breaking developers! In fact I’ve got some NWA and Wu Tang Clan playing right now… at the same time! Let’s be safe about our rule breaking though, and seriously, this could break at anytime so proceed at your own risk.
First we need to have some default date/time formatting should our call to the DateUtil method encounter any issues. Let’s revisit the original example with some slight modifications. Make note of the comments in the code.
sObject.query('select Id, Name, CreatedDate from Account limit 1',function(result,event){//Convert CreatedDate to a JavaScript date ojectvar d =newDate(result[0].CreatedDate);//Remove first 4 characters (day), the seconds and the GMT offsetvar dateString = d.toString();
dateString = dateString.substring(4,dateString.lastIndexOf(':'));//Attempt to format datetime using sfdc DateUtil methodtry{
dateString = DateUtil.getDateTimeStringFromUserLocale(d);}catch(err){//Fail silently or alert devs DateUtil method is no longer working}//Output the formatted date string
console.log(dateString);});
The result will be the following output:
2/27/2013 9:08 PM //If DateUtil method was success
Feb 27 2013 21:08 //DateUtil failed and we used default formatting
Beautiful!
There is one last issue that may creep up. If your Visualforce page has the showHeader attribute set to false these salesforce.com JavaScript files will not be loaded into the page. The easy fix is to simply include a Visualforce inputField component that is bound to a Date or DateTime field in salesforce.com and hide it. This will load the date formatting JavaScript files. Like so:
So this little company called salesforce.com is going to be hosting an event in Seattle on Tuesday November 13th. If you happen to be in the area it may be worth checking out. It’s only a half day event so it may also be easier to convince your boss you can attend since you won’t miss an entire day of work.
I’ve been to these smaller events in the past and they are a great place to network but are also very useful for individuals to get exposure to salesforce.com that may not be familiar with the platform in their day to day duties. It could be great for that manager expressing interest in learning more about salesforce.com but hasn’t got around to it yet or perhaps good for a new employee just entering the salesforce.com community.
You can register by clicking on the big image below. If you will be there let me know and perhaps we can meet in person.
Keeping Apex dry huh? This may be the first thing that comes to mind…
While this is a magnificent piece of MS Paint art this post will actually address a different kind of dry. DRY to be precise, or Don’t Repeat Yourself, and how this relates to Apex code on the force.com/salesforce.com platform. In addition to keeping things DRY I am also a religious believer in the KISS principle (Keep It Simple Stupid), and YAGNI (You ain’t gonna need it) to round off the acronym smorgasbord. With all of this in mind the content of this post may not be the “best” way to do something, and is surely not the only way, but it is definitely a stupidly simple and DRY way to design and architect Apex code. If you have been programming for years this is probably basic stuff but if you are new to Apex, or programming in general, this should be perfect for you.
Let’s pretend we have a database with thousands or maybe even million of Contact records. Now let’s pretend there is a business rule in place where the owner of all Contact records must be the same as the Account Owner. Ah ha! Let’s do this with a trigger! Yes, let’s! Here is what this bad boy trigger might look like.
trigger updateOwner on Contact (before insert, before update){//Create and populate a list of Account Ids the contacts are related to
Set<Id> acctIds =new Set<Id>();for(Contact con : trigger.new){
acctIds.add(con.AccountId);}//Query the related account owners and put in may where key is account Id, value is owner Id
Map<Id,Id> accountOwnerMap =new Map<Id,Id>();for(Account acct :[select Id, OwnerId from Account where Id IN : acctIds]){
accountOwnerMap.put(acct.Id,acct.OwnerId);}//Set the owner Id field on the account, only do this if they are differentfor(Contact con : trigger.new){if(con.OwnerId!= accountOwnerMap.get(con.AccountId)){
con.OwnerId= accountOwnerMap.get(con.AccountId);}}}
Looks good right? Well…it’s okay, but we can make it better. The main issue is the logical code is inside the trigger itself and it can’t be reused anywhere, like from a Visualforce page or a Batch Apex. So Rule #1, Don’t put logical code in a trigger. A better way to structure this would be to put the change owner logic in a separate class and call this class method from the trigger.
I have a confession to make. I have broken Rule #1 a lot. I can already hear the cries of outrage but hear me out. Sometimes I know for a fact that a trigger’s code is so unique to the problem it is solving it will never be reused anywhere else. For triggers this happens a lot as they are usually in place to address a very specific need that could not be addressed in the declarative areas of the platform. Part of what I am about to say goes back to me being a crazy KISS believer. Why should I put my code in another class just because this is the “right” way to do it even if there is no additional benefit? It adds complexity, albeit minor, and it also unnecessary bloats the number of classes. Often I have a trigger and a test class, that’s it. If I have a trigger, class for code, and a test class I’ve now increased the number of classes in my org by a third with no real benefit. So maybe…(wait, putting on flame suite) Rule #1 should be, Don’t put logical code in a trigger if you think there is a chance it could be reused somewhere else.
Usually code is going to be reused so that rant was much to do about nothing and this example is no different. This code could totally be reused so we will move the logical code to it’s own class called ContactAssignment which includes a method called changeOwner() and this method will change the owner field on the contact.
publicClass ContactAssignment{publicstaticvoid changeOwner(List<Contact> contacts){//Create and populate a list of Account Ids the contacts are related to
Set<Id> acctIds =new Set<Id>();for(Contact con : contacts){
acctIds.add(con.AccountId);}//Query the related account owners and put in may where key is account Id, value is owner Id
Map<Id,Id> accountOwnerMap =new Map<Id,Id>();for(Account acct :[select Id, OwnerId from Account where Id IN : acctIds]){
accountOwnerMap.put(acct.Id,acct.OwnerId);}//Set the owner Id field on the account, only do this if they are differentfor(Contact con : trigger.new){if(con.OwnerId!= accountOwnerMap.get(con.AccountId)){
con.OwnerId= accountOwnerMap.get(con.AccountId);}}}}
Next we need to update the trigger to utilize this new class and method.
trigger updateOwner on Contact (before insert, before update){//Call the changeOwner() method in the ContactAssignment class and pass over the records being processed by this trigger.
ContactAssignment.changeOwner(trigger.new);}
At this point the trigger now behaves the same but our logical code has been removed from the trigger and can be utilized from many different places within Apex.
In a perfect word Apex triggers will fire all the time with every CRUD operation and we would have no issues with the current setup but, I hate to break it to you, we do not live in a perfect world. Sometimes Apex Triggers won’t always be successful due to user permission issues, validation rules, code bugs (your’s and salesforce.com’s), and who knows what else. Maybe in our org this trigger works perfectly but let’s again pretend we are super paranoid about this business rule and it is critical that all contact owners match account owners. So in addition to this trigger we also want a nightly Batch Apex job that queries and updates all contacts and changes the owner as necessary.
Here is what the batch job looks like and this brings us to Rule #2, a Batch Apex class should never included any of the code/business logic, it should only query the records and send them somewhere else for processing. Looking at the class below we are doing just this. The only thing this batch class does is query the records and then sends them to the ContactAssignment class for processing. The same class and method used by the trigger.
global class ContactAssignmentBatch implements Database.batchable<sObject>{String query ='select OwnerId, AccountId from Contact';
global Database.QueryLocator start(Database.BatchableContext bc){return Database.getQueryLocator(query);}
global void execute(Database.BatchableContext bc, List<Contact> contacts){//Send queried contacts to the ContactAssignment class for processing
ContactAssignment.changeOwner(contacts);}
global void finish(Database.BatchableContext info){
system.debug('All done. Hooray!');}}
There is one very important change we need to make to the ContactAssignment.changeOwner() method. In the context of a trigger when you edit a record the code doesn’t need to perform any explicit update on the record. As the records are going through the normal save workflow and any changes you make to the record in the code will be reflected after save. Calling this method from batch apex is totally different. We are passing records over to the class to be processed that are outside of the normal save workflow, where a trigger would normally execute, and therefore we must update the contact records our self with an actual update statement. For this class to work in both trigger and non-trigger contexts we have to make some changes. The comments in the code should explain these.
publicClass ContactAssignment{publicstaticvoid changeOwner(List<Contact> contacts){//Create and populate a list of Account Ids the contacts are related to
Set<Id> acctIds =new Set<Id>();for(Contact con : contacts){
acctIds.add(con.AccountId);}//Query the related account owners and put in may where key is account Id, value is owner Id
Map<Id,Id> accountOwnerMap =new Map<Id,Id>();for(Account acct :[select Id, OwnerId from Account where Id IN : acctIds]){
accountOwnerMap.put(acct.Id,acct.OwnerId);}//Set the owner Id field on the account
List<Contact> consToUpdate =new List<Contact>();//This is new.for(Contact con : contacts){if(con.OwnerId!= accountOwnerMap.get(con.AccountId)){
con.OwnerId= accountOwnerMap.get(con.AccountId);//If being processed by the trigger this will work fine but..../*...if not being processed by a trigger such as batch job or a future we need
to explicitly update the records, first step is adding them to a list*/
consToUpdate.add(con);}}//If we are not currently within the context of a trigger (batch, @future, etc) we need to update the recordsif(Trigger.isExecuting==false&& contacts.size()>0){
update consToUpdate;}}}
At this point our code is nice and DRY. All of our logic is in once place and this is good for many reasons. If we need to change the reassignment logic we only have to do it one place and our batch class and trigger don’t need to be touched. It’s also easier to test and debug because all the code is is one place and not duplicated within the system.
Now let’s make this interesting. Let’s pretend (pretending is fun right?) we have too many triggers on the Contact and Account objects and we are starting to hit limit errors such as too many query rows or too many DML statements. After looking at all the triggers on these two objects we realize the changeOwner trigger doesn’t need to be real time. What we can do is change the code in the in the ContactAssignment class to fire asynchronously or not real time. The salesforce.com system will queue this job up and run it when system resources become available. You can read more about @future and asynchronous jobs here. Easy, just add the @future keyword to the changeOwner() method and we are good…right? Something like this:
publicClass ContactAssignment{
@future //This is new, but bad.publicstaticvoid changeOwner(List<Contact> contacts){//Create and populate a list of Account Ids the contacts are related to
Set<Id> acctIds =new Set<Id>();//Same as above...}
Unfortunately it’s not this simple as our Batch Apex job will completely implode and fail the next time it tries to run. Batch Apex jobs are also considered an asynchronous operation and you can’t call one async job from another; future to future, batch to future, future to batch, etc.
What we need to do is create a special method in the class that can handle the @future request, prepare the records that need to be processed, and then sends these records to the changeOwner() method that we use in the trigger and use from the Batch Apex class. This is what it might look like. First the modified ContactAssignment class and then the trigger.
publicClass ContactAssignment{
@future
publicstaticvoid changeOwnerFuture(Set<Id> contactIds){//We can't pass the list of objects, we can only pass the contact Ids//Since all we have are the IDs we actually need to query the contact records we are going to process
List<Contact> contacts =[select OwnerId, AccountId from Contact where Id IN :contactIds];//Now send these contact records over to the main method for processing
ContactAssignment.changeOwner(contacts);}publicstaticvoid changeOwner(List<Contact> contacts){//same as above, this is where all our core logic is}}
In the trigger there is another thing we have to worry about, the infamous infinite loop, dun dun DUNNNNNNNNN! I actually discussed this in a previous post and it required creating your own static variables to fix this. Since then salesforce.com has made addressing this issue much easier. It’s also not really an infinite loop and the issue really boils down to calling an @future method in the context of an @future execution, a no no. Fortunately there is now a system method to check for this and it’s in the trigger below.
trigger updateOwner on Contact (before insert, before update){//Only call the future method if we are not currently processing a future or batch jobif(system.isFuture()==false&& system.isBatch()==false){//Call the changeOwnerFuture() method in the ContactAssignment class and pass over the records IDs being processed by this trigger.
ContactAssignment.changeOwnerFuture(trigger.newMap.keySet());//Neat little trick to pass record Ids}}
The comments in the code explain a lot but here are some things to note. When you call an @future method you can only pass over primitive datatypes like Id, Strings, etc so in our special @future changeOwnerFuture() method we actually need to query the records to process as they are not automatically passed over. Then we send these records to the changeOwner() method and all is good!
Nothing I’ve talked about in this post is mind blowing amazing (except for maybe the art) but rather focuses on some core design guidelines to think about when working with Apex code, or any programming language for that matter, and keeping it DRY. Like I said before what I’ve laid out here is just one way to accomplish this and there are ways to make what I have done here even better. Please add comments below if you have other insights.
Oh, and if you are one of the people that is like, “Uh ya, your @future implementation is totally lame because you could like totally actually not execute two queries and that is such a waste n00b”, you are absolutely correct, and we need to keep in touch.
As you may have noticed the amount of content on this blog over the past few months has been a little lacking. Fear not! I am alive and all is well. In fact, I’ve got some exciting news. One of the reasons for not much happening here recently is that I’ve been focusing a decent amount of time spinning up a little company. And with that I’d like to introduce you to Envy Apps.
Right now Envy Apps is focused on building apps on and for the salesforce.com / force.com platform. The idea is to build apps that you can’t live without, that you are envious of if you don’t have them. Man… I should have gone in to marketing .
The first product released is Schema Surfer and it’s totally free, but payment in Belgium beers is totally accepted. It’s a great admin/dev tool that allows you to browse and view object metadata from directly within a 100% native app. No longer is there a need to open the Force.com IDE or some other 3rd party tool to view metadata information. Schema Surfer comes directly from my nearly 6 years of experience on the salesforce.com and force.com platforms. There have been many times, especially when I am wearing my developer hat, when I need to quickly view metadata information. Is CollaborationGroup queryable? How is ProcessInstance related to ProcessInstanceStep? What object starts with the prefix 019? If you find yourself asking these types of questions this is the app for you. I built this app so maybe I am a little (a lot) biased but this is one of those apps that you may not think you need but then once you try it you can’t imagine being without it. I encourage you to take it for a spin, the test drive on the AppExchange listing is a great way to do this. If you like the app and you can’t contain your excitement feel free to leave a super friendly and helpful review. If you happen to have missed all of the links above you can get Schema Surfer from the AppExchange by clicking on the big image below:
You may have also notice that in the logo the [apps] is in bracket, like an array (I’m so crafty), which would seem to indicated there will be more than one app. There will, so stay tuned!
First part if you missed it:
Let me tell you a story.
Once upon a time there was an admin/dev/general all around nice guy. He was working on a validation rule of epic proportions. This was not your normal validation rule. This was a validation rule that would literally take the human race to the next level of enlightenment. He made sure this validation rule looked beautiful. Spacing, line breaks, and indents where something to be marvel at. The comments included were as if they had been written by a great early 20th century author, along the likes of Ernest Hemingway perhaps. As you read the comments it was as if they transformed you into the computer system itself as the validation occurred.
Continued part here:
Finally, one special day arrived. To an outsider this day looked no different than any other. The weather was miserable rainy day and people went about their business like every other day of their normal lives. Yet for this one man, this day, was the day he had waited his entire life for. He had dreamed and fantasized about a day like this since his childhood. He always wondered what it would be like. What it would be like to move the most magnificent validation rule ever created to a production environment. His skin tingled with excitement. He was so very tempted to implement this validation rule directly in production but he resisted these urges with all of his might. He had waited too long to be rash with his actions. I must do this the right way he thought. The process of deploying had begun.
This individual was full of excitement but a bit of trepidation was also present as he began the deployment process. He constructed a change set. It in itself was a thing of beauty. A single change set with a single validation rule contained within. It was almost a shame to move on from this calm and tranquil place but he knew what he had to do. He uploaded the change set. Almost instantly he received an email letting him know his change set had been success fully uploaded. It felt as if his heart skipped a beat in this moment of excitement as he read these words. He quickly logged in to production and navigated to Inbound Change sets. A wave a panic overtook him. The change set was not there. How could this be he thought! The email said this change set had been successfully uploaded. In a flurry of mouse clicks and key strokes he re-read the email. Yes, it says the change set has been successfully uploaded but where is it! In a desperate attempt to find relief to the anxiety that was beginning to overtake his entire body he frantically reloaded the inbound change sets page ever two seconds. Finally after what felt like days and nights the change set became visible. His body became limp as the stiffening anxiety left his body. He wiped the thin layer of perspiration from his brow and prepared to proceed.
Usually he would validate before deployment but not this time. After waiting so long for the change set to become available he knew he was not mentally prepared to handle the suspense of waiting for a deployment validation to complete. He had to go straight for the deploy, it was the only way. The moment had come and it was as if time slowed down. His senses became heightened yet not overwhelmed and unimaginably focused. He heard the noises of an office around him. He felt the cool breeze of the air conditioning system blowing down the back of his neck. The mouse beneath his hand felt like the most powerful tool known to man. The mouse button clicked…it had begun.
During the deployment this individual had an unusual calmness. Everything was perfect. The rule was perfect. The logic was perfect. The testing was perfect. He was a master of his craft and he knew the deployment would be successful. It was. The next step was to view this validation rule live in production. Few things gave him more satisfaction than seeing his work live in a production org. Simply looking at the setup page for a given component would give him a high unlike anything else. As he navigated the setup menu to the validation rule he deployed only seconds ago the excitement rose. There it was, the link to open the validation rule, he clicked it.
Yes, the name looked wonderful. There is no better name for this validation rule than the one it has now he thought. It was active, beautiful. Yet has he viewed the body of the validation rule confusion overtook him. What has happen he thought! The formatting of the rule was completely destroyed! All of the time and effort put in to this validation rule to make it look beautiful was lost! It felt like the world around him was failing apart. Why would this happen, why to me? This rule was supposed to be the chosen one! Anger quickly filled his mind. Who did this? I will find them and I will make them fix it. He was consumed with rage but with no place to release these mountainous waves of anger he quickly began to lose control of his thoughts. His rage quickly transformed to paranoia. Somebody or something was out to get him. There was no other explanation. Why else would this happen to him. This paranoia ate away at him. He lost friends and avoided family. People that saw him said he was always mumbling something about formatting and validation rules but no one could fully understand what he was saying. This individual was never the same.
As you can see this story unfortunately came to a very tragic end for this man. Once, one of the greatest validation rule creators of our generation, he became reduced to a man with crazed thoughts and the inability to maintain a normal life. And to think…all of this could have been avoided if change sets maintained formatting. We can only hope that one day they do so this story does not repeat itself and others can continue with bringing wonderful validation rules to production orgs all around the world.
The salesforce.com Summer 12 Release Notes are out and once again is like Christmas…or Hanukkah…or Kwanzaa…or your birthday…(or some awesome event with presents!) for salesforce.com admins and devs. It’s this wonderful time of year when we once again get to read about all the great new features coming up in the next release. I’ve read through the release notes and there are tons of great features coming but from a developer perspective these are the features that excite me most.
Sorting Lists of sObject and other complex data types
This feature is near and dear to my heart. People have been trying to figure out the best way to do this for 5+ years! I even put together a little how to article on the developer.force.com wiki before I even had this blog. This is a great feature and anyone who has built custom sorting methods will truly appreciate this feature.
Dynamic Instantiation of Apex Classes
Also known as Reflection but you have got to admit “Dynamic Instantiation of Apex Classes” sounds way cooler. I’ll be the first to admit that I don’t know the best ways to utilize this feature but I know it is a massive improvement and starts to give Apex features that make it a true fully fledged programming language.
JavaScript Remoting Enhancement for Manage Packages
Let’s say you have two versions of an app. A managed package version and an unmanaged package version. The manage package version will add a namespace to all of the Apex classes. With JavaScript Remoting you previously had to hard code the Apex class name and method. This required either managing two different versions of the JS code or hacky workarounds in the JavaScript to detect namespaces and call the correct class name with or without the namespace. In Summer 12 you now don’t have to worry about this namespace problem.
In this truly superb example we have the following AccountUtil class with the namespace SuperProduct and it has a method that returns a list of Accounts.
global class AccountUtil{
@RemoteAction
global static List<Account> getAccountList(){return[select Id, Name from Account limit 10];}}
Previously you would have to hardcode the namespace in the JavaScript code like so:
Hooray! No more hardcoding the namespace! It will be interesting to see if the merge field syntax of {!$RemoteAction.AccountUtil.getAccountList} also works in static resources. As of today it’s not possible to user merge field syntax in a static resource.
Metadata REST API
You may not know this but the current Metadata API sucks. Sorry salesforce.com. I’m sure at one point in time it was really cool, but it is not anymore, and I’m sure a ton of people at salesforce.com would totally agree with me. If you have ever tried to use it you will know it requires building zip files and sending them to salesforce.com to deploy or modify components. It also requires unzipping file responses. This is simply not very cloud friendly and it’s a pain to work with. Oh, and did I mention it’s terribly slow. I have high hopes for this new REST Metadata API and I’m really looking forward to it.
Also, as the owner of an org with 15,000+ reports I’m hoping this API finally provides a better way to find reports and views that are using a specific field in the output or the criteria.
As of posting this it doesn’t look like the reference guide for the Metadata REST API is out yet.
EDIT: This first version of this API will also be read only.
Run Apex on Package Install/Uninstall
Need some Apex code to run when a user or customer installs a Managed Application? Now you can do this. Previously you had to either A) make the user going through a structured setup process or B) in your application code check to see if everything is setup properly the first time it runs and if not call some sort of setup method. Both not very elegant.
Allow Reparenting Option in Master-Detail Relationship Definitions
What is that? Do you hear something? Yup, I hear something…IT’S ANGELS SINGING!!!!! This nugget of incredible awesomeness was buried at the very bottom of the release notes but it is freaking HUGE as far as I’m concerned!
In Summer ’12, administrators can now allow child records in master-detail relationships to be reparented to different parent records by selecting the Allow reparenting option in the master-detail relationship definition. By default, records in master-detail relationships can’t be reparented.
This was such a pain before. First you need to build Apex code to clone the the child record. Then you also had to clone everything else attached to this child such as tasks, events, attachments, other child custom objects, and more! This feature makes be very very happy.
New Lookup Relationship Options
This bad boy was also buried at the end of the release notes but it also gets me very excited. Lookup relationships now have a ton of enhanced functionality when it comes to deleting the “parent” record in a Lookup relationship. Let’s check out the official wording:
• Clear the value of this field. This is the default.
• Don’t allow deletion of the lookup record that’s part of a lookup relationship.
• Delete this record also.
Here is an example. On an Opportunity we have a lookup field to the Account object called Primary Partner. Now if the Account in the lookup field ever gets deleted the value on the opportunity will get wiped out. Currently the only way to prevent this from happening is with an Apex trigger on the Account being deleted. Now this can be accomplished with clicks not code! This feature also help prevent the orphaning of “child” records.
The ability to enable cascade delete functionality with Lookup relationships will also be available. The line between Lookup relationships and Master-Detail relationships just got very very blurry, in a totally great kind of way. Now the only significant difference between Lookup and Master-Detail fields is Roll-Up Summary field functionality and based on these changes I would guess that Roll-Up Summary functionality for Lookup fields is not far away. Actually, I wouldn’t be surprised to see these two field types rolled in to one and the behavior controlled with field specific settings.
[EDIT] Another difference is Master-Detail relationships also inherit record access permissions. Lookups do not.
The last few feature release from salesforce.com have had one or two great features but the Summer 12 release is the first release in quite some time that has a treasure trove of great new features that will immediately have an impact on day to day admin and force.com development. Can’t wait for Dreamforce and Winter 13!
Wouldn’t it be glorious if you could upload multiple file to salesforce.com at one time. Wouldn’t it be magically delicious if there were status bars for each file indicating the progress of these uploads? Yes, this would be a glorious and magically delicious feature that should exist so today I give you the first release of the Multi File Uploader Tool for salesforce.com and force.com! It still needs some improvements but it’s off to a good start so far.
If you wish to jump to the end of this magical and captivating journey that is the story of how this tool came to be you can check it out on github here. Yet if you want to captivated and intrigued with the story of how this tool came to be stay tuned and hang on because this is a fun one!
It is clear to everyone the current upload functionality in salesforce.com leaves something to be desired. The user flow today involves navigating to a new page and uploading files one at a time. In Visualforce there is a file upload component but it also lacks pizzaz and polish. Like the native upload functionality this component only allows uploading one file of time. One other issue with this component is you can not perform a rerender on this component as the page will crash hard. The problem is clear and the need for something better is obvious.
Why Now
Aside from the obvious need for something like this what really got my interests piqued in a multi file upload tool was a Seattle Force.com Meetup in which @greenstork asked if anyone had built a multi file upload tool for salesforce.com. No one had. After the meeting I took a stab at this and made progress quickly. I put out a teaser video but then I went silent. As I started to make progress on this app I knew this should go on github but I knew nothing about git. After taking some time to get up to speed on git I think I’m now ready to get this app out in the wild.
Another reason for me delaying the release of this tool is the larger heap size limit that come with the Spring ’12 release. The heap size has gone from 3MB to 6MB and this will help greatly with the size of files that can be uploaded.
How
So how to do this? If you google “file upload status bar” you will seen many options. Some of these solutions use AJAX and Javascript. Other use Flash and some even use a combination of each. The problem with every single one of these solutions is you need full access to the server so you can install a piece of code that monitors the status of the upload. The client can then ask the server for the status of the upload and update the status bar on the page. In salesforce.com magical unicorn land this is not on option as you have no control over the server. Server control is all salesforce.com. I’m okay with this as it’s part of the whole value proposition they deliver, no servers to manage, but for the sake of a multi file uploader tool with progress bars this presents a major problem.
The only thing we can execute in a custom manner on salesforce.com servers is Apex code. So somehow we need Apex to facilitate the processing of files, this we know, as there is no other option. How we get this data to the Apex code on the server is a more open ended question.
Chunking with HTML5 File API
In researching how I might accomplish this I came across a spectacular find that might just make a multi file upload widget possible. In researching some other neat HTML5 features I found the site html5rocks.com and a page on the File API. (Side note, this site has some pretty fan freaking-tastic examples of what you can do with HTML5.) Using the File API you are able to process files on the client side. You can read, get file meta data, and splice files in the browser! At this point the light bulb above my head started to glow. Maybe I can cut the file up in to small junks client side, send it to salesforce.com piece by piece, and then reconstruct the entire file server side using Apex code!
This does in fact work but here lies caveat #1. The File API is not supported by all browsers. It is no surprise the big blue E doesn’t support the File API but even the current version of Safari doesn’t support the ability to read files. Chrome and Firefox are of course good to go.
Using the File API we are able to split up the files and send them to salesforce.com where the entire file can be reconstructed but what is the best way to send the file chunks to the server? Maybe an Apex exposed web service with the SOAP or REST API? Maybe utilizing the existing file upload component or JavaScript remoting? All are potential options but it didn’t take long to narrow down the list. This is going to be used in a Visualforce page… all of the core code and logic is JavaScript… and it needs to be fast. Hmm….JavaScript Remoting it is!
Binary Data and the Woes of Base64
One of the first issues I uncovered with JavaScript Remoting and even the SOAP/REST APIs is the data must be sent to salesforce.com as a Base64 encoded string. There is currently no way for the platform to receive binary data (what files are made of). Even if we could receive the raw binary data Apex code doesn’t really have any way to manipulate binary data other than converted to Base64. So what is wrong with Base64 encoding the data before you send it? Well, when you Base64 encode something it actually bloats the data by 33%. If you want to upload a 1MB file you will actually send 1.33MB of data. Here we introduce caveat #2! Base64 encoding the data makes the uploads slower than they would normally be by 33%. Yet the time saved by not having to upload files individually is probably worth the 33% file bloat when transmitting the data.
Chunking and the Woes of Base 64
Oh, you though we where done with the “woes” didn’t you, nope! At this point I actually had an almost working example. I could splice the file in the browser, send individual blobs to salesforce.com, and then rebuild the file on the server, except there was one major problem. Every file uploaded became corrupt. The first chunk of a file would be good but then everything after that first piece was a complete mess. Why! I spent more time trying to figure out this corruption issue than anything else while developing this tool. Then laying in bed one night it hit me. Stupid Base64!!! Or maybe stupid Jason for not seeing this sooner? To understand why improperly encoding something in base64 will cause data corruption we first need some basic understanding of how Base64 works. Let’s pretend we have a simple text file with the word ‘shark’ in it. We will split this file in to 2 bytes chunks, send them to salesforce.com, and then rebuild the file by combining the Base64 chunks. Easy!
The data chunks sent to salesforce.com Base64 encoded would look like this:
sh -> c2g=
ar -> YXI=
k -> aw==
On the server we should be able to combine these Base64 values, decode it, and get the word shark….right? Wrong!
c2g=YXI=aw== decoded is sh\°
Hmm, that does not look like the word shark. I’m not going to get deep into the details of Base64 encoding but the main problem is for every 3 input bytes encoded you get 4 output bytes. In our example we are only passing in 2 bytes at a time to get Base64 encoded so our output doesn’t actually use the full 4 bytes. If there aren’t 4 output bytes after encoding is complete padding characters are added, the “=” symbols. This ensures the output is 4 bytes for every 3 inputted. To prevent these padding characters in the middle of our file we need to make sure every chunk we process is divisible by 3. This way for each encoded chunk of 3 bytes we will get exactly 4 output bytes with no padding character in the middle to mess everything up. Lets try again using a chunk size of 3 bytes instead of 2.
sha = c2hh
rk = cms=
You can see the last part of the file still contains the “=” padding characters but this is okay because it is at the end of the file where there really isn’t any data to represent. If these padding characters end up in the middle of a encoded file, this is where we get the data corruption. So combing the encoded values and the decoding the result will look like this.
c2hhcms= decoded is shark !
This may not make a lot of sense at first as it took me some time to fully wrap my brain around this problem but in the end we must make sure when why splice a file in to chunks they must be in a byte size divisible by 3. If not, our data will get corrupted when the file is rebuilt.
Heap Schmeap!
At this point we have a theoretical multi file uploaded. I say theoretical as this post still hasn’t shown any code! We will get to the code, I promise, but in this particular case I think it is better to understand the concepts first. The final concept we need to understand is how the Apex heap size limits affects the upload process. Sharks are cool so let’s continue with our shark example. Below is a walk through of the upload process considering how heap size is affected throughout.
Upload Chunk #1)
Receive Base64 encoded ‘sha’ from remoting call, c2hh. 4 bytes
Decode to binary blob, set as attachment body and insert 3 bytes
Total heap for this operation, about 7 bytes.
Upload Chunk #2)
Receive Base64 encoded ‘rk’ from remoting call, ‘cms=’. 4 bytes
Query body from Attachment object uploaded in part #1, ‘sha’. 3 bytes.
Convert body from Attachment to base64, c2hh, 4 bytes.
Combine the two base64 strings, c2hhcms=. 8 bytes.
Decode base64 combination and set to attachment body, ‘shark’. 5 bytes
Total heap for this operation, 24 bytes! Eeek, especially considering all we are really doing is adding 3 bytes (sha) to 2 bytes (rk). That is a lot of overhead!
This brings us to caveat #3. Even though we have a 6MB heap to work with most of this is used up with the encoding and decoding of Base64 data. In the end the largest file we can upload is approximately 1.27MB. This could be a deal breaker for some use cases but unfortunately there is no way around this. If anyone has some super crafty ways to lower the heap usage please fork this project!
GIVE ME CODE!!!!
Enough talk, give code now! There are still several improvements I want to make with this tool so I don’t want to post the code on this page as it will probably be different one week from now. What I am going to do is put it up on github and you can find it here. I would highly encourage you to check it out, fork it, improve it, and make it awesome. This is the first time I have put something on github and its my first real foray into git so be prepared for me to totally mess something up. While I won’t post the code here I will post a code walk through that should help with some basic understandings of how everything works. It was late and I was tired when I made this so apologies in advance for the spurts of incoherent babbling…
Still Pretty Cool
Let’s review the important caveats.
- Doesn’t work with Internet Explorer or Safari.
- Uploading 1MB file sends 1.33MB of data.
- Heap limits prevent files larger than 1.27MB from being uploaded.
In the end, even with all these limitations this tool is pretty freaking rad I think. If you need to upload lots of smaller files this is a great tool but if your files are larger in size it may not work for you. If I had to give this tool a version number in its current state I would say it’s around 0.6 as there are several features I want to add in the very near future. Some of these are:
- Detect browser support and provide graceful degradation.
- If upload fails because upload is to big provide modal window to upload single large file.
- Appify to tool
This can be found on the issues tab in github as well.
Cloudspokes.com recently wrapped up a challenge titled “Magical Disappearing Salesforce Button with jQuery” and I couldn’t resist not entering. For starters it’s a pretty rad name for a challenge as anything with the words “magical” and “jQuery” in it get me excited. This challenge is now closed for new entries and I’d like to share my submission. Rather than the typical blog type post I’m going to vlog it! This is the first time I have posted something with this much detail so I’d love to get feedback. Are vlogs the way of the future or should I stick to the tried and true formula of written posts? Let me know.
The best way to watch these is to go full screen and the change the playback quality to 720p.
First up is a high level overview:
Next is a 15 minute deep dive that pulls back the covers and gets into all the juicy details.
If you had trouble seeing the markup and script in the video above here is the sidebar html component that makes the magic happen:
<!-- We can locate this div with the script below, traverse up the DOM and then hide the entire sidebar component. -->
<div id="sidebarComponentLocator">If you see this, something is broke with Dynamic Button functionality.</div>
<!-- Import jQuery from google CDN, could also be static resource-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">/*Immediately create a unique alias for this version of jQuery to prevent conflicts with other js libraries*/var j$ = jQuery.noConflict();/*Determine what type of object is currently being displayed on the page. Only confident way to do this is get object prefix */var objectPefix = window.location.href.substring( window.location.href.indexOf('.com/')+5, window.location.href.indexOf('.com/')+8);/*Query the records from Dynamic_Button__c object as soon as possible, don't wait for DOM ready event*/var result = sforce.connection.query("Select Button_Name__c, Field_Id__c, Field_Value__c from Dynamic_Button__c where Object_Prefix__c = '"+ objectPefix +"'");var records = result.getArray("records");/*Execute this code block once page DOM has fully loaded*/
j$(document).ready(function(){/*Hide the sidebar last as this is lowest priority. First priority is show/hiding the buttons.
Find the sidebarComponentLocator, then find parent div with class 'sidebarModule' and the hide it*/
j$("#sidebarComponentLocator").closest(".sidebarModule").hide().prev().hide();/*First loop through the dynamic button records and hide any that are on the layout. We must first hide all the buttons
as the Dynamic Button records only contan the 'show' logic*/for(var i =0; i< records.length; i++){/*Hide buttons define in Dynamic_Button__c object*/
j$("input[name='"+ records[i].Button_Name__c.toLowerCase()+"']").hide();}/*Now loop through the Dynamic_Button_Records and show button if field value matches that define in record*/for(var i =0; i< records.length; i++){/*Get the value from the field on the page layout*/var recordValue = j$("#"+records[i].Field_Id__c+"_ileinner").text();var showValue = records[i].Field_Value__c;/*Show the button if the value of the field on this record matchs the setting in the Dyamic_Button__c*/if(recordValue == showValue){
j$("input[name='"+ records[i].Button_Name__c.toLowerCase()+"']").show();}}/*Everything above is cool and provides an easy to use interface for the logic but you could always hardcode the logic like so...
First get value from the ninja field, this Id would need to be changed
var ninjaSkillValue = j$("#00NE0000000d4hc_ileinner").text();
/*Hide Ninja Attack button if Ninja Skill picklist value is not 'High'
if(ninjaSkillValue != 'High'){
j$("input[name='ninja_attack']").hide();
}
*/});/*If you are wondering why this file has multiline comments for one one...for some reason single line
comments cause the script to break, something funky with salesforce and javascript in the sidebar.*/</script>
I recently came across the requirement for a workflow rule to fire on the update of a record but only if the update occurred more than a couple seconds after the record was created. Why this crazy requirement? Here is a example of the problem I was faced with.
Image you have two custom objects, Exam and Question. There is a master detail relationship between the two where many Questions can be associated with one Exam. There are also some roll-up summary fields on the Exam that count number of questions, number answered correctly, etc. Now also image (lots of imagining today so I hope you can harness your inner child imagination skills) you have a Visualforce page that assigns exams to users. This Visualforce page essentially clones a an Exam and Questions and assigns these to a user. First it inserts the Exam and then all related Questions. On the Exam object there is a workflow rule that need to execute when an Exam is updated. The problem is this workflow rule kept firing every time I inserted Exams and Questions for a user. Why is this happening? I am only inserting records, right? Wrong-a-long-a-ding-dong!!!
So what is happening here? Why is an update occurring on the Exam record when all I am doing is inserting records? The culprit are those very cool, but pesky, roll-up summary fields. When you have a roll-up summary field on a record any time you insert or update a child record an update will occur on the parent. So in the example above every time I would insert the the Questions it would cause on update action on the Exam. This is documented but it might not be something you come across every day. It’s also not obvious as everything happens so fast in the code the Created Date and Last Modified Date on the Exam show the same values in the user interface. If you queried these two date fields you would see they are milliseconds apart.
So how to solve this? There are a few options. We could set a flag, a checkbox field, on the Exam called Disable Workflow that we set to true on the insert of the Exam and then after inserting the Questions we could uncheck this box. In our workflow rule we could prevent the rule from executing by referencing this field. This would work but I don’t like it for a couple reasons. The first is that I hate adding fields to the database that aren’t really needed. Secondly, it’s less efficient as it requires multiple DML (update) operations to make everything flow smoothly. Another options is to get super crafty with our workflow rule.
The insert of the Exam and Questions is going to be finished in milliseconds. What if we make the rule fire on updates but only on updates a few seconds after the record is created. What we need to do is determine the time between the created date of the record and now in seconds. When working with formula fields or validations rules the duration of one day is represented as the value of 1. If we know this we can use our superior deductive reasoning skills to figured out the value for one second.
1 / 24 = 0.0416667 is one hour
0.0416667 / 60 = .00069444 is one minute
0.00001157 = one second
Now in our workflow rule we have have a formula like this:
…and it will evaluate to true on updates but only if those updates are greater than 1 second after the record was created.
So I work at a little company called F5 Networks…well we just had a $1 billion dollar year so I guess it is not that small, but we are looking for a Salesforce.com Solution Developer. Salesforce.com Solution Developer? What the heck is that? Knowing Apex, Visualforce, APIs, web services, JSON, XML, AJAX, databases, etc are all perfect skills for this position. Yet this position is not all development all the time. It is about developing full well rounded solutions. This means in addition to some of the more technical skills I already listed you also should know and enjoy working with custom objects, workflows, validation rules, formula fields etc. Heck, let’s throw in some integration work as well while we’re at it. A big piece of this role involves working directly with business units to understand their requirements, guide them at times, and quickly build applications that can be tweaked and adjusted based on feedback (think super agile). This also involves post deployment support. We also run very efficiently here as we currently have two admins/analysts and myself managing 1200+ users. This means there is also some day-to-day admin duties as well. As you can see this isn’t a pure development role so if you are looking for that… keep looking.
If this is something that sounds exciting to you please apply below and reach out to me with the contact form link above.
Starting this weekend salesforce.com will begin the seasonal upgrade to Winter ’12. As usual this release is packed with tons of great new features. So what is the best new feature? Chatter approvals, nope. Native JSON support, nope. Schema builder, nope. API improvements, nope. By far the most important feature of the Winter 12 release is this:
General Availability During Major Release Upgrades
Starting with Winter ’12, Salesforce will be generally available during major release upgrades.
General availability means you should only expect to experience up to a five minute disruption of service as your Salesforce
organization is upgraded. Users trying to access Salesforce during this time receive an error message that the service is unavailable
and they can log back in momentarily. In addition, users logged into Salesforce during a major release upgrade may be
temporarily logged out.
This may not seem like a big deal but it is (insert Boston accent here) absolutely frickin HUGE! You may think that 6 hours of downtime three times a year is not that much, and honestly its not, but 6 hours in a row is a big deal. Also, assuming salesforce.com has no other downtime (we can wish, right?) this still only gets you between two and three 9s up uptime. This is a huge problem for customer service call centers. Having your customer service system go down for 6 hours is simply not an option and is a deal breaker for large enterprise companies. There are many examples of salesforce.com not being able to successfully close Service Cloud deals because of this one issue alone. Reducing these upgrades times to less than five minutes places salesforce.com in the elite 4-5 9s range of uptime.
This change is important because it brings down one of the few remaining, and legitimate, barriers to cloud adoption. Sure there will still be unscheduled downtime and emergency maintenance but if you have noticed in the communications from salesforce.com a lot of these are also being performed under that status of being “General Available”. salesforce.com and cloud computing in general definitively has it’s weaknesses. Most of these are minor and feature specific weaknesses yet downtime during upgrades was a weakness that could totally prevent the adoption of salesforce.com. Now this will no longer be an issue… and I’m sure the salesforce.com account executives will have a much easier conversation with potential customers when asked about down time for upgrades.
I wanted to get this notice out as soon as possible and currently there are no specific demos or speakers planned so it will be more of an open forum. If you have something awesome you’d like to show off please let me know. I could always whip something up as well if we want to get down and dirty with some code dives.
Dynamic Visualforce Components are coming and they scare me…a lot. Before we can even begin to discuss why we need to review the basics of the model-view-controller pattern. If you aren’t familiar with the MVC pattern here is the basic run down when it comes to web apps that use MVC. Model, this represents your database and contains information on they fields in your database, validation rules, relationships, etc. View, this is the visual display of a model or many models. It says output the fields of a record in HTML on some web page and style them this way. This is where you can visually control how the data is displayed to the user. Controller, this is the code that retrieves and manipulates data from the Model and then prepares it for the View to use. A web app will most likely consist of many Models, Views, and Controllers. Some people may want to overcomplicated the MVC pattern, and it does vary slightly based on the platform or framework, but for the most part this is exactly how it works 97% of the time. So in the force.com world it looks a little like this:
Model
These would be custom objects, you can add fields, create validation rules, and build relationships with other objects.
View
Visualforce pages are the view and allow you to output information from the model as an HTML webpage.
Controller
One or many Apex classes associated with a Visualforce page. Here you can query and manipulate data before it is sent to the view for output.
So what does any of this have to do with Dynamic Visualforce Components (DVCs)? The reason is that DVCs allow developers to completely mangle and destroy the model-view-controller design pattern. They allow you to completely define the display from the controller. So basically MVC becomes M(VehCiv) or perhaps Model Viewroller as the View and the Controller start to get all mixed up. Big flipping deal you might say, all of the other hip cool web technologies (Rails) allow you to inject markup from the controller. Yes, they do, and there are people in those communities that feel the exact same way I do about mixing aspects of MVC, just because you can doesn’t mean you should.
Here is a very basic example of how mixing up the MVC can cause trouble. Let’s say you are working on a project where you have a web designer who is great at making spiffy looking websites and a developer that understands all the back end work. If you keep the View and the Controller separate these people can work individually on their part of the project. When you start to combine the View and the Controller the web design guy might be waiting for the developer to get the output setup so he can style it. Or maybe even worse you end up have a web designer starting to poke around in controller code, not ideal.
Mixing the View and the Controller also makes things more complicated and difficult for others to understand. When looking through a controller you now have to start mentally separating the view code and the logic code. For the consultants out there that sometimes have to inherit other’s code I do not envy you when you have to dig through someone else messy code, DVCs will make it messier.
Okay Jason, we get the point, but there are times when we need DVCs to meet the project requirements! Are there? Do you really need to use them? Doubtful. Yes, there are some use cases out there that can only be solved with DVCs but these are actually pretty rare. So if you think you need to use DVC’s, think again, and the ask someone else, perhaps on the force.com developer forums, and then if you still think you need them go for it. Personally, I still can’t think of a super great example for DVCs, but I’m sure after this post I’ll be inundated with use cases where only DVCs can save the day!
So here is an example of using DVC vs not using them. This is actually the example taken from the recent Force.com Summer 11 webinar. The goal is to get an output that looks like this:
An unknown number of tabs is the dynamic piece that needs to be solved. Here is one way to do it with DVCs. First the page. One thing I will point out right way is that when you look at this page you have no idea what constitutes the dynamic components. There is nothing that lets you know if they are a table, a div, text, etc. You would need to go look in the controller to see exactly what the dynamic component is made of. It is less markup but it is also inherently less descriptive and as mentioned before more difficult to understand. This is one of the core advantages to staying true to the MVC pattern. On a View it should be dead simple to understand the generated markup and output.
<apex:pagecontroller="TopOpportunityController"><apex:stylesheetvalue="{!$Resource.CustomTableCSS}"/><apex:sectionHeadertitle="Top 10 Open Opprtunities"/><h1>Total Expected Revenue from Top 10 Opportunities:</h1> $
<apex:dynamicComponentcomponentValue="{!OppTotal}"/><br/><br/><apex:dynamicComponentcomponentValue="{!tabbedView}"/></apex:page>
Next the controller. Notice that we are querying the data but also directly manipulating and defining the view in this controller. For me, when these two areas start to combine it can become blurring on exactly what code is interacting with the Model and what code is interacting with the View.
public with sharing class TopOpportunityController {private List<Opportunity> topOpportunities;publicDouble opportunityTotal {get;set;}public TopOpportunityController (){
topOpportunities =[SELECT closeDate, TotalOpportunityQuantity, Id, Name,
AccountId, Account.Name, Probability, ExpectedRevenue,
stageName, Amount, Account.Open_Opportunities_Total__c FROM
Opportunity where isClosed=false ORDER BY
Account.Open_Opportunities_Total__c DESC, Account.Name,
ExpectedRevenue DESC LIMIT 10];}publicComponent.Apex.TabPanel getTabbedView(){Component.Apex.TabPanel panel =newComponent.Apex.TabPanel(
switchType ='client',
title ='Top 10 Opportunities');String lastAccountId;
opportunityTotal =0;for(Integer i =0; i< topOpportunities.size(); i++){
Opportunity o = topOpportunities[i];
opportunityTotal += o.ExpectedRevenue;//If we have a new Account record, need to create a new Tabif(lastAccountId != o.AccountId){Component.Apex.Tab acctTab =newComponent.Apex.Tab();
acctTab.label= o.Account.Name;
panel.childComponents.add(acctTab);}Component.Apex.OutputText oppText =newComponent.Apex.OutputText(escape =false);
oppText.value='';//If this is a new tab, start a new <table>if(lastAccountId != o.AccountId){
oppText.value+='<table class="customT"><thead class="customT"><tr class="customT"><th class="customT">Opportunity Name</th><th class="customT">Probability</th><th class="customT">Stage</th><th class="customT">Expected Revenue</th></tr><tBody class="customT"></thead><tBody class="customT">';}
oppText.value+='<tr><td class="customT">'+ o.Name+'</td>';
oppText.value+='<td class="customT">'+ o.Probability+'</td>';
oppText.value+='<td class="customT">'+ o.StageName+'</td>';
oppText.value+='<td class="customT">'+ o.ExpectedRevenue+'</td>';
oppText.value+='</tr>';if((i == topOpportunities.size()-1)|| o.AccountId!= topOpportunities[i+1].AccountId){
oppText.value+='</tBody></table>';}
panel.childComponents.get(panel.childComponents.size()-1).childComponents.add(oppText);
lastAccountId = o.AccountId;}return panel;}publicComponent.Apex.OutputText getOppTotal(){Component.Apex.OutputText oppTotal =newComponent.Apex.OutputText();
oppTotal.expressions.value='{!opportunityTotal}';return oppTotal;}}
So how would you do it mister smarty pants? Oh, I’m glad you asked. I would still use Dynamic Visualforce, just not Dynamic Visualforce Components. In my approach I want to look at the controller first. Notice that this controller has nothing related to the structure and style of the View. It only queries and process information from the Model. This should make it easier to understand exactly what the code is doing from a logic standpoint as you do not need to mentally separate the View logic. Some of the variables control how the information will be displayed on the page and this is okay, this is exactly what a controller should do. One thing to notice is the use of Maps. Map support was recently added to Visualforce as Dynamic Visualforce and is it is fan-freakin-awesome. I would bet it will solve 95% of your dynamic Visualforce needs. The other 5% is for Dynamic Visualforce Components.
publicclass TopOppsController {public List<Id> accountIds {get; set;}public Map<Id,String> acctIdNameMap {get; set;}public Map<Id,List<Opportunity>> acctIdToOppsMap {get; set;}public Opportunity totalOppAmount {get; set;}//User Opportunity object amount field as wrapper for total, outputField will format currencypublic TopOppsController(){
buildOppList();}publicvoid buildOppList(){//Instantiate variables
accountIds =new List<Id>();
acctIdNameMap =new Map<Id,String>();
acctIdToOppsMap =new Map<Id,List<Opportunity>>();
totalOppAmount =new Opportunity(Amount =0);//Query the Opps
List<Opportunity> opps =[SELECT CloseDate, Id, Name, AccountId, Account.Name, Probability, StageName, Amount
FROM Opportunity
WHERE isClosed=false ORDER BY Account.Open_Opportunities_Total__c DESC, Account.Name, Amount DESC LIMIT 10];//Process the oppsfor(Opportunity opp : opps){//Increment total amount
totalOppAmount.Amount+= opp.Amount;//Add accountId and Name to map, these Id -> Name map values can be used in Visualforce page
acctIdNameMap.put(opp.AccountId,opp.Account.Name);//Add Opps to Account ID -> List<Opps> map, the List values in this map can be use in Visualforce datatablesif(acctIdToOppsMap.get(opp.AccountId)==null){
acctIdToOppsMap.put(opp.AccountId,new List<Opportunity>());
accountIds.add(opp.AccountId);//We add account Ids to list and then we can iterate over this in page and pull values from Maps in the controller}
acctIdToOppsMap.get(opp.AccountId).add(opp);}}}
Next up is my Visualforce page. One of the first things you will notice is that I had to use a little bit if jQuery magic. In my first attempt I tried to use an apex:repeat component inside of an apex:tabPanel but this simply does not work. So a bit unwillingly I turned to jQueryUI to address the tab issue. The good news is that using the jQuery UI tab panel is ridiculous easy: 1) Create a div that will represent the tab panel. 2) In this div create list of <a> links where href is the Id of a seperate div representing the corresponding panel. 3) Four lines of jQuery javascript. I’ll admit this is not 100% native where as DVCs are but this approach is actually very simple. It also makes it easier to see what the view is doing, and actually provides complete control over the tab panel style, and interaction. Even if you did use the native tab panel, it is doing the tab switching with javascript anyway ;-P .
So all that aside the page should be pretty easy to understand. With out any type of inline comments you’d be able to see we have a list of <a> links that is outputting an account name. Then for each one of these names we also have a corresponding data table. Keeping the View out of the controller makes it much easier to see how the page is formatted….or at least I think it makes it easier. You lose this type of natural documentation when using DVCs.
<apex:pagecontroller="TopOppsController"><scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"/><scriptsrc="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.12/jquery-ui.min.js"/><apex:stylesheetvalue="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/themes/ui-lightness/jquery-ui.css"/><scripttype="text/javascript">
var j$ = jQuery.noConflict();
j$(document).ready(function() {
j$("#tabs").tabs();
});
</script><apex:sectionHeadertitle="Top 10 Opps!"/><h1>Total Amount from Top 10 Opportunities: </h1><apex:outputFieldvalue="{!totalOppAmount.Amount}"/><br/><br/><divid="tabs"><ul><apex:repeatvalue="{!accountIds}"var="acctId"><!-- Loop through list of account Ids --><li><ahref="#tabs-{!acctId}">{!acctIdNameMap[acctId]}</a></li><!-- use account Id to get values from acctIdNameMap --></apex:repeat></ul><apex:repeatvalue="{!accountIds}"var="acctId"><!-- Look through list of account Ids --><divid="tabs-{!acctId}"><apex:pageBlockmode="edit"><apex:pageBlockTablevalue="{!acctIdToOppsMap[acctId]}"var="opp"width="100%"><!-- use account Id to get list of opps from acctIdToOppsMap --><apex:columnvalue="{!opp.Name}"/><apex:columnvalue="{!opp.Probability}"/><apex:columnvalue="{!opp.StageName}"/><apex:columnvalue="{!opp.Amount}"/></apex:pageBlockTable></apex:pageBlock></div></apex:repeat></div></apex:page>
So in the end DVCs have a purpose but more often then not you will probably not need them to meet your specific requirements. The real danger is using them when you really don’t need to and I’m hoping everything I’ve listed in this post explains why you should make a conscious effort to only use them when absolutely necessary. In the end this will make your apps easier to understand and then when you leave your company to go start the next million dollar start up the poor sap that inherits your code won’t go insane trying to understand the mess that is your controllers.