I’ve been working on code and content updates to our Force.com Architecture Workshop over the past few weeks. Today I was working on the Visualforce section. It’s easy to forget that Visualforce is a container for just about any web library you can think of. Too often we get stuck on the 80+ tags provided by Salesforce. But in the world of jQuery, HTML5, CSS3 etc, we can do some really cool things.
I’ll be the first to admit I am not a ‘scripting’ expert, but it doesn’t take much work to combine some style, with Salesforce data to produce something like this:
The effect is pretty cool, and amazingly simple to do. Let’s start with the controller extension. There is nothing fancy here, just grab all the Contacts for a particular Account:
public class AccountsVisualContacts { public Account acct {get; set;} public Id acctid {get; set;} public List<Contact> allContacts { get; set;} public AccountsVisualContacts(ApexPages.StandardController stdController) { this.acct = (Account)stdController.getRecord(); allContacts = [select id, Name, FirstName, LastName, Wiki_Link__c, Image_URL__c from Contact where Accountid = :acct.id]; } }
Now, we can add the Visualforce page. I ‘borrowed’ the CSS from the net, and added it to the top of my Visualforce page. The rest of the code is leveraging the styles defined within an apex:repeat tag. The only other trick here to include a variable to dynamically increment a counter to allow me to reference the correct CSS style. For this simple example, I am using a predefined number of styles, but there is no reason I could not use the same incrementing variable trick to dynamically set the width and height of my polaroids.
You will also notice that I am using the standard Account controller, and my custom controller as an extension. Using the standard Account controller allows me to link directly to my Visualforce page from a custom button on the Account layout. We’ll see why this is important a bit later. I also added a custom field on the Contact object, Image_URL__c to point to an image source on the internet (specifically, images from wikipedia as we can be pretty comfortable they are not going anywhere)
<apex:page standardController="Account" extensions="AccountsVisualContacts" sidebar="false" > <style type="text/css"> body { background-color: #E9E9E9; color: #333; font-family:"Lucida handwriting", "Snell Roundhand", "Helvetica Neue",Arial,Helvetica,sans-serif; font-size: 16px; } .amp { font-family:Garamond,Baskerville,Georgia,serif !important; font-style:italic; font-weight:normal; border: none; } a.polaroid { display: block; text-decoration: none; color: #333; padding: 10px 10px 20px 10px; width: 150px; border: 1px solid #BFBFBF; background-color: white; z-index: 2; font-size: 0.7em; -webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); -moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); -webkit-transition: -webkit-transform 0.5s ease-in; } a.polaroid:hover, a.polaroid:focus, a.polaroid:active { z-index: 999; border-color: #6A6A6A; -webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); -moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); -webkit-transform: rotate(0deg); -moz-transform: rotate(0deg); transform: rotate(0deg); } .polaroid img { margin: 0 0 15px; width: 150px; height: 150px; } a img { border: none; display: block; } .photo-album { position: relative; width: 80%; margin: 0 auto; max-width: 70em; height: 450px; margin-top: 5em; min-width: 800px; max-width: 900px; } .photo-album .polaroid { position: absolute; } .photo-album h1 { position: absolute; z-index: 5; top: 150px; text-align: center; width: 100%; line-height: 1.9; } .photo-album h1 span { background-color: white; font-family: "Helvetica Neue",Arial,Helvetica,sans-serif; padding: 0.4em 0.8em 0.3em 0.8em; -webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); -moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3); border: 1px solid #6A6A6A; } .photo-album .small { width: 75px; padding: 6px 6px 12px 6px; font-size: 0.6em; } .photo-album .small img { width: 75px; height: 75px; } .photo-album .medium { width: 200px; padding: 13px 13px 26px 13px; font-size: 0.8em; } .photo-album .medium img { width: 200px; height: 200px; } .photo-album .large { width: 300px; padding: 20px 20px 30px 20px; font-size: 1em; } .photo-album .large img { width: 300px; height: 300px; } .photo-album .img1 { bottom: 10px; right: 365px; -webkit-transform: rotate(10deg); -moz-transform: rotate(10deg); transform: rotate(10deg); } .photo-album .img2 { top: 50px; right: 20px; -webkit-transform: rotate(-4deg); -moz-transform: rotate(-4deg); transform: rotate(-4deg); } .photo-album .img3 { left: 400px; top: 0; -webkit-transform: rotate(-5deg); -moz-transform: rotate(-5deg); transform: rotate(-5deg); } .photo-album .img4 { top: 10px; left: 495px; -webkit-transform: rotate(-20deg); -moz-transform: rotate(-20deg); transform: rotate(-20deg); } .photo-album .img5 { bottom: 0; right: 0; -webkit-transform: rotate(1deg); -moz-transform: rotate(1deg); transform: rotate(1deg); } .photo-album .img6 { bottom: 10px; right: 156px; -webkit-transform: rotate(6deg); -moz-transform: rotate(6deg); transform: rotate(6deg); } .photo-album .img7 { bottom:0; left:400px; -webkit-transform: rotate(-10deg); -moz-transform: rotate(-10deg); transform: rotate(-10deg); } .photo-album .img8 { bottom: -20px; left: 700px; -webkit-transform: rotate(-8deg); -moz-transform: rotate(-8deg); transform: rotate(-8deg); } .photo-album .img9 { bottom: 0; left: 0; -webkit-transform: rotate(-8deg); -moz-transform: rotate(-8deg); transform: rotate(-8deg); } .photo-album .img10 { top: 0; left: 20px; -webkit-transform: rotate(8deg); -moz-transform: rotate(8deg); transform: rotate(8deg); } a:hover, a:focus { z-index: 5; } </style> <div class="photo-album"> <h1><span>Contacts of {!account.name}</span></h1> <apex:variable var="count" value="{!1}"/> <apex:repeat value="{!allContacts}" var="c"> <apex:outputLink value="/{!c.id}" styleClass="medium polaroid img{!Round(count,0)}" target="_blank"><img src="{!c.Image_URL__C}" alt="{!c.Name}" />{!c.Name}</apex:outputLink> <apex:variable var="count" value="{!count + 1}"/> </apex:repeat> </div> </apex:page>
Ok, all the hard stuff is done. All that is left is to add a custom button to our Account Details, and point it to the Visualforce page:
That’s it. Load this into your Salesforce org, and you are good to go!