
This sample demonstrates how to create a simple blog application. It shows how to write a several interesting uses of Visualforce, including rich text widget from Yahoo, onload events in the page, custom controller, onclick and action complete handlers, one in javascript and the other inside a custom controler.
It consists of two pages and uses a custom object called Blog, which has one custom field for the body.
Custom Object Name: Blog Post
Custom Field: Post
The first page is the viewer for the blog. This page is called /apex/blog. It incorporates both inline CSS and an external CSS to render the look-and-feel of the page. In the following code, the apex:repeat tag iterates through all blog post records and formats the display of each post as defined by the CSS. Each title is hyperlinked to allow editing of the blog entry (more on this later). The commandButton tag renders a button to bring up the blog editor for a new blog entry.
<apex:page showHeader='false' controller="blogController">
<head>
<style type="text/css">
html body #header { margin-top:0px;
font-family:"trebuchet MS", helvetica; }
a.title, a.title:hover {color:#f60;text-decoration:none;}
#header a, #header a:hover {font-size: 26px; color: #fff; text-decoration:none;}
#header .right {float: right;font-size: 18px; color:#258;}
.sub{font-size:11px;text-align:right;}
input.groovybutton { font-size:12px;
width:85px; height:30px;
border-style:ridge;
border-color:#643200; }
</style>
<link rel="stylesheet" type="text/css" href="http://www.blogger.com/css/blogger.css" />
</head>
<div id="body">
<div id="main-wrap" >
<div id="main" style="margin-top:5px" >
<div id="m2">
<div id="m3">
<apex:repeat value="{!Posts}" var="post">
<h2 id="title">
<a class="title" href="/apex/blogedit?id={!post.Id}" target="_top">
{!post.Name}</a></h2>
<br />
<script type="text/javascript">
document.write('{!post.Post__c}');
</script>
<p class="sub">
Posted by {!post.CreatedBy.Name} on {!post.CreatedDate}</p>
</apex:repeat>
<center>
<apex:form>
<apex:commandButton styleclass="groovybutton" value="New Post" action="{!handleNewPostClick}"/>
</apex:form>
</center>
</div></div></div></div></div>
</apex:page>
Custom Controller for /apex/blog page, only used to send user to a new edit page or get a list of all posts
public class blogController {
public PageReference handleNewPostClick() {
PageReference pageRef= new PageReference('/apex/blogedit');
pageRef.setredirect(true);
return pageRef;
}
public Blog_Post__c[] getPosts() {
return [select Name, id, CreatedDate, CreatedBy.Name, Post__c from Blog_Post__c];
}
}
The next page is for creating a new blog entry or editing an existing blog entry, it works for both because the standard controller knows when the query string has an ID provided and when it does not. No extra work is required by the page itself, very clever!!
The interesting things to note are , use of standard controller for this custom object. Let's detail the process around the Save button, this is important to understand.
Why do we need to perform code in the onclick ? well the editing in the YUI widget happens on the client, and we need to store the resulting modified text into the standard controllers' object, before the save or our changes would be lost. The standard controller then takes care of all the (AJAX type) actions around save and we simply navigate away to the view blog page again.
<apex:page showHeader='false' standardcontroller="Blog_Post__c">
<apex:form>
<!-- Skin CSS file -->
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.3.0/build/assets/skins/sam/skin.css" ></link>
<!-- Utility Dependencies -->
<script src="http://yui.yahooapis.com/2.3.0/build/utilities/utilities.js"></script>
<!-- Needed for Menus, Buttons and Overlays used in the Toolbar -->
<script src="http://yui.yahooapis.com/2.3.0/build/container/container-min.js"></script>
<script src="http://yui.yahooapis.com/2.3.0/build/menu/menu-min.js"></script>
<script src="http://yui.yahooapis.com/2.3.0/build/button/button-beta-min.js"></script>
<!-- Source file for Rich Text Editor-->
<script src="http://yui.yahooapis.com/2.3.0/build/editor/editor-beta-min.js"></script>
<div class="yui-skin-sam" style="display:none" id="editor" align="center" ><p />
<h2>Blog Title: </h2>
<apex:inputField value="{!Blog_Post__c.Name}"/>
<apex:commandButton value="Save" action="{!Save}"
onClick="beforeTextSave()"
oncomplete="window.parent.location.href='/apex/blog';"/>
<p />
<apex:inputtextarea id="msgpost" cols="500" rows="50" value="{!Blog_Post__c.Post__c}"/>
</div>
<script type="text/javascript" >
var myEditor;
window.onload = function init() {
myEditor = new YAHOO.widget.Editor('{!$component.msgpost}', {
height: '400px', width: '722px', dompath: true, animate: true });
myEditor.render();
document.getElementById('editor').style.display='';
}
function beforeTextSave() {
document.getElementById('{!$component.msgpost}').value = myEditor.getEditorHTML();
}
</script>
</apex:form></apex:page>
That's it, you have a simple application to View a blog, Create a blog entry and Edit a blog entry.
Note: to refer to a DOM element ID in your javascript there is a special merge field : {!$component.page_component_id} which in this case is seen as {!$component.msgpost}
this may be used in getElementById() rather than requiring you to know the page generated element ID.
Why do we use window.onload ? The page takes time to load the editor from Yahoo, so we gladly wait for this to complete before drawing the editor onto the screen, if we don't then Yahoo will not see a valid DOM element to draw into on the client. Hooking the window.onload event lets this process happen in a deterministic order. We also take the trouble to hide the editor DIV until Yahoo is all done with the editor render() operation, otherwise we get a flash of a text area before Yahoo Editor is done wrapping our blog text.
--Ron Hess 17:26, 24 September 2007 (PDT)