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>
<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!