Stop reading!
I improved this control in a later post... Find it here.
As part of my ongoing effort to spruce up the look and feel of salesforce pages, I created a rating control that allows users to easily rate a contact. The finished control looks like the following image, where each star is clickable and will set the rating to that many stars.
A few things you should know about this code sample:
- This code example is using the control with the Contact object although you can adjust it to use with any object.
- To get the nodes to the visualforce page, I construct and populate a list of Contacts with the node information. I did that because I am lazy - instead, you can easily create a class to hold the right info and make the field names make more sense.
- The way I set it up, the rating range is set up by the control as opposed to hard code a one to five range.
- The page reloads after a user selection. That's not great, and at some point I'll try to make this dynamic and repost.
Step one:
Create a zip with two images called RatingOn.png and RatingOff.png (I created a colored star and a grey star), and upload as a static resource.
Step two:
Create an apex class Rating.cls to hold the code for the visualforce components:
public with sharing class Rating {
// set up properties to get the values from the calling vf page
public string contactId { get; set; } // used by Rating.component and RatingNode.component
public integer maxRating { get; set; } // used by Rating.component
public integer Location { get; set; } // used by RatingNode.component
public string ImageURL { get; set; } // used by RatingNode.component
// Calss constructors
public Rating() { }
public Rating(ApexPages.StandardController stdController) { }
////////////////////////////////////////
// Methods for Rating.component
////////////////////////////////////////
//getter for the current rating of the contact
private integer CurrentRating;
public integer getCurrentRating() {
if (CurrentRating == null) {
Contact c = [SELECT Id, Rating__c FROM Contact WHERE Id = :contactId LIMIT 1];
CurrentRating = (integer.valueOf(c.Rating__c) == null) ? 0 : integer.valueOf(c.Rating__c);
}
return CurrentRating;
}
public List<Contact> Nodes;
public Contact[] getNodes() {
if (Nodes == null) {
Nodes = new List<Contact>();
for (Integer i = 1; i <= maxRating; i++) {
contact c = new Contact(LastName=string.valueOf(i));
c.FirstName = '/resource/MyReatingResource/';
c.FirstName += (i <= getCurrentRating()) ? 'RatingOn' : 'RatingOff';
c.FirstName += '.png';
Nodes.add(c);
}
}
return Nodes;
}
////////////////////////////////////////
// Methods for RatingNodes.component
////////////////////////////////////////
public string getTooltip() {
return 'Set rating to ' + string.valueof(location);
}
// save the new rating and reload the page
public PageReference SaveRating() {
Contact c = [SELECT Id, Rating__c FROM Contact WHERE Id = :contactId LIMIT 1];
c.Rating__c = Location;
update c;
PageReference callPage = new PageReference('/' + contactId);
callPage.setRedirect(true);
return callpage;
}
}
Step three:
Create a visualforce component called RatingNode.component (used for each node in the rating):
<apex:component controller="Rating" allowDML="True">
<apex:attribute name="contactId" type="String" required="true" assignTo="{!contactId}"
description="This is the contact Id to bind the results to." />
<apex:attribute name="location" type="integer" required="true" assignTo="{!Location}"
description="The location of this node in the rating." />
<apex:attribute name="ImageURL" type="string" required="true" assignTo="{!ImageURL}"
description="The url for this node image (already set to off/on)" />
<apex:commandLink action="{!SaveRating}" title="{!Tooltip}" >
<apex:image url="{!ImageURL}" />
</apex:commandLink>
</apex:component>
Step four:
Create a visualforce component called Rating.component (used to assemble all the nodes together):
<apex:component controller="Rating">
<apex:attribute name="contactId" type="String" required="true" assignTo="{!contactId}"
description="This is the contact Id to bind the results to." />
<apex:attribute name="maxRating" type="integer" required="true" assignTo="{!maxRating}"
description="This is the desired maximum rating." />
<apex:form >
<apex:repeat value="{!Nodes}" var="node" id="RatingRepeat">
<c:RatingNode contactId="{!contactId}" Location="{!node.LastName}" ImageURL="{!node.FirstName}" />
</apex:repeat>
</apex:form>
</apex:component>
Step five:
Include the component in your visualforce page like that:
<c:Rating contactId="{!Contact.Id}" maxRating="5" />