Inspired by a great post by Adam Weber about Sitecore Defensive Coding Practices, I’ve decided to throw down some of my favorite patterns and practices with Sitecore Development. If you have any of your own, share them in the comments!
Define a BaseSublayout Class
BaseSublayout class to expose base-level functionality to any UserControl/Sublayout that you will have on your site, e.g. accessing a Data Source item. For example:
protected class PromoBox : BaseSublayout
protected void Page_Load(object sender, EventArgs e)
// use any code exposed in the inherited class
My experience with this: I started doing this when I realized the power of assigning data source items to a sublayout defined on an item. The code to get a datasource takes a few lines, so I abstracted it into a base class for reusability on any project. It turns out John West has had something even better on Shared Source for a while now, the Sublayout Parameter Helper.
Gracefully Degrade the Front-End
This is by far my favorite pattern that I use all the time. As developers, our job is to make the site render correctly per the content in Sitecore. That includes handling content editor errors that may occur, such as not filling out data or missing something. The concept is simple:
Set controls (ASP.NET controls, FieldRenderers, etc.) to be
visible="false" by default, then prove the data to render exists and render the data. There are many sub-patterns you can go with here, depending on how you write your HTML and how the component works. For example, any sublayout I have that requires a DataSource is wrapped in a PlaceHolder with
visible="false". My job as a developer is to ensure a data source is set and its of the right template type. Once confirmed, I turn the placeholder’s visibility on. This pattern ensures no widow HTML exists that is rendered without any real data.
Here’s a simple example:
<asp:PlaceHolder ID="TitleWithLinkPlaceholder" runat="server" Visible="false">
<asp:Hyperlink ID="TitleWithLinkHyperlink" runat="server">
<sc:Text ID="TitleWithLinkText" runat="server" Field="Title" />
bool linkSet = !String.IsNullOrEmpty(genericCallout.Link.Url);
if (linkSet) // With link
TitleWithLinkHyperlink.NavigateUrl = genericCallout.Link.Url;
TitleWithLinkText.Item = genericCallout.InnerItem;
TitleWithLinkPlaceholder.Visible = true;
As you can see above, I assume the data is not in Sitecore by wrapping my HTML in a placeholder that is
visible="false" then in C#, I see if the data exists and turn the controls on. In this simple example, it prevents a random
<h4> tag from appears with nothing in it. Is it a lot of code for a little check like this? Yes. Is it worth it when not all fields in Sitecore are required and they affect the front-end? YES!
Use FieldRenders or at Least Trigger the
Many Sitecore solutions need to support Page Edit mode. In order for code to support this, the
FieldRenderer controls on the front-end when possible, or if not possible, at least use the
FieldRenderer via C#. Luckily, the Custom Item Generator (CIG) exposes easy access to the
Rendered value of a field, so at least use this if you need to use regular .NET controls. (Note: the CIG is another useful pattern Adam covers in his post).
Use a Page Load Pattern with Try-Catch Blocks
Every sublayout I write follows the same pattern:
Page_Loadmethod body contains a try-catch block.
- The try block calls a helper method to do the main work.
- If the sublayout takes a
DataSource, a check occurs to ensure the item is set and is of the right template.
- The catch block logs the exception to the Sitecore log with the name of the specific sublayout.
Here’s an example where we use several of the patterns in this blog post (BaseSublayout to expose the DataSource, CIG items,
protected void Page_Load(object sender, EventArgs e)
if(DataSource != null && DataSource.TemplateId.Equals(PromoItem.TemplateId.ToString()))
Sitecore.Diagnostics.Log.Error("Promo Sublayout", ex, this);
In the above code, the method
LoadPromo(Item item) would actually bind the data to controls, etc.
My experience with this: I started doing this when I found it hard to determine where some
null errors were occurring on sublayouts that didn’t have appropriate
null checks. I started to follow the same pattern over and over and eventually tacked on the try-catch with Sitecore logging to make it easier to know when issues came up.
Avoid Hard-coded Paths and Instead Expose Global Item Links
To explain this approach, let me pose a scenario:
Say you’re building a search results page on your site to host the results sublayout for your global full text site search (using Lucene.NET, or Coveo, or Google Search Appliance, etc.). You have a textbox in your header that handles the search query and you push the query to the results page to actually search the site. How does your code for the textbox know which page to go to for the results?
- Option 1: hard-code it (BLAH!)
- Option 2: expose a global field in Sitecore to set the results page (FTW!)
In code, your search box query method would do something like this:
protected void btnSearch_Click(object sender, EventArgs e)
// assumes Globals.GlobalItem is a static helper class to get the globals item
ReferenceField searchPage = Globals.GlobalItem.Fields["Search Page"];
var url = LinkManager.GetItemUrl(searchPage.TargetItem) + "?q=" + txtSearch;
Why is Option 2 better? If someone renames the search results page, say from
search-results to just
results then the global field in Sitecore would handle the change.
For Any Standard User Control in Use, Consider Statically Binding it as a Sublayout
If your code in layouts and sublayouts references other modular controls such as standard users controls, e.g.
<uc:Promo runat="server"></uc:Promo> you should consider loading the user controls the Sitecore way. By changing a standard user control reference to a statically bound sublayout, you can leverage Sitecore’s HTML output cache to cache the rendered HTML per the business rules of the control. E.g.
<sc:Sublayout Path="~/path/to/your/control.ascx" Cacheable="true" runat="server" />, then in C# you could perhaps set the
VaryByParm property to alter the cache instances by your uniqueness of the control.
Leverage Utility Classes, Do Not Reinvent
Sitecore comes with a plethora of utility classes in many namespaces. Learn them and use them. They’re very handy and you can avoid writing code that may already exist (e.g. sanitizing an item name).