If you’re a seasoned Sitecore developer, feel free to skip this post. If your company just bought SCORE and you’re looking for information on how to build things “the SCORE way,” you found it. I want to get into some theory, some nomenclature and the mindset of a SCORE developer.
What it means to Componentize
So let’s talk about components. When you look at a modern website, you can typically break it down into conceptual chunks of functionality. These chunks are so prevalent throughout the web that entire frameworks are built around them. Just take a look at Zurb, Bootstrap, MDL or any others. See anything familiar? See anything that’s really the same thing across those frameworks?
Those chunks are referred to as Components. A component could be as simple as a button that links to another page, or as complex as a form that executes custom logic. It’s up to you as the Sitecore developer to define the granularity of your application’s components, and to embrace the SCORE way of delivering them. Really, when you’re building applications in Sitecore, there are two rules you should live by. First: if it gives you flexibility, then you should componentize (but remember to NOT overdo it!). Second: don’t fight the framework.
componentize
verb
(transitive) To split into separate components, especially interchangeable pieces.
At Brainjocks, now Perficient, when we talk about components, we throw around two terms: Atomic and Molecular. An Atomic component is a single rendering, which may or may not have rendering parameters or a data source. It can work on its own, and doesn’t depend on any other components on the page (although two atomic components could interact with each other through events). An example may be a Two Column component, a Hero component, a Button component that raises an event when clicked, another component that executes an action when an event is raised, etc.
On the other hand, a Molecular component is composed of multiple renderings and datasources. Molecular components empower your content authors to build things such as Carousels. Really, when you think about a carousel, it would be created as an outer “Carousel Wrapper” component, which exposes a placeholder. Within that placeholder, you should be able to place any number of “Carousel Panel” components, and each panel should let you place an image and some text within it (or whatever component you want to support). To the end user, this is a single component, but really it’s composed of multiple mini-components that work together.
In Sitecore, when we build components, there are a few major pieces: Templates, Renderings, Placeholders and Models.
Where SCORE fits in
So where does SCORE fit into all of this? With SCORE, you gain the power of Dynamic Placeholders; Area support (although Sitecore 8.1 finally added this, yay!); an extended rules engine for things like placeholders, datasources and queries; validation annotations tied back to datasource fields; some automatic SEO help; a set of extremely useful base templates; a (very extensive) library of general use components; rendering transformations; a scoping, eventing and communication framework for JavaScript; and more.
At the end of the day, the end goal is to enable developers to create a very rich “component assembling” experience for your content authors. If you have ever had the pleasure of building a large-scale Sitecore application without a framework like SCORE, then it’s very easy to see the power it brings to the table.
But why should I care?
Now, I have something to admit. The first Sitecore build I worked on was done by a team of 4 developers. My first Sitecore implementation didn’t use datasources. My first Sitecore implementation had around 100 page types. Each page type consisted of a Layout and single Sublayout (Web Forms version of a Rendering) that represented the page contents between the header and footer. The only content was shared across the entire instance, but none of it was used as a data source. Instead, we stored everything in an “extras” folder that every multi-list and drop-link in the system pointed to.
At best, a single broken link would cause a giant blank space between the header and footer. At worst, a single broken link would trigger yellow screens in the craziest places. We didn’t know what rendering parameters were (and didn’t care, who needs those anyways?). We didn’t support page editor (pff, looks cool but don’t care! I have tickets that NEED to get done!) and the site had around 2000 pages.
It wasn’t that I was stupid, or my team was stupid, because we were far from it. In fact, that team was one of the best I’ve ever worked on. The problem was that we just didn’t know. No one had seen the light, no one had made mistakes with Sitecore before and we fought the framework at every corner. I was ready to throw Sitecore away. I went to Sitecore North America 2014 in Las Vegas (really, I just wanted an excuse to go to Vegas), and there I saw SCORE for the first time. SCORE blew my mind.
If you want to make the same mistakes I did, feel free to stop reading this series now. In fact, it’s a good learning experience. Remember when you used to tie the same model to your repository and views? If not, go ahead and scaffold up a new site using SCORE 2.0, and let’s build some components.
So let’s start by creating a basic component
Here’s our user story:
Story:
As a content author, I would like the ability to insert a biography component onto a content page.
Acceptance Criteria:
- A biography component can be placed into any nested content area.
- When creating a biography, the content author should be able to specify:
- Portrait – Image
- Name – Single Line Text
- Description – Rich Text
Also, our designer is ahead of the game and already has given us a component thumbnail. Sweet! Let’s upload this to /sitecore/media library/system/playground/thumbnails.
The Datasource Template
This should be easy enough to create. Start with a template in Sitecore, and add your fields. Don’t forget your standard values! I like to follow the convention of a Root folder for your tenant application. My tenant is named “playground,” so I will stick all of my templates under /sitecore/templates/playground.
Mine looks like this:
The Datasource Model
This part is not as immediately apparent. After all, you can build an entire Sitecore application without creating a single C# model. Here’s what your model would look like:
public class BiographyDatasource : ScoreUIItem { public static class Fields { public const string Portrait = "Portrait"; public const string Name = "Person Name"; public const string Description = "Description"; } // replace with your template's ID public static readonly ID TemplateId = ID.Parse("{A3AA61C2-B969-408B-9428-F000DBF89427}"); public BiographyDatasource(Item item) : base(item) { } public ImageField Portrait { get { return this.InnerItem.Fields[Fields.Portrait]; } } public string PersonName { get { return this.InnerItem.Fields[Fields.Name].Value; } } public string Description { get { return this.InnerItem.Fields[Fields.Description].Value; } } public static bool TryParse(Item item, out BiographyDatasource parsedItem) { parsedItem = item == null || item.IsDerived(TemplateId) == false ? null : new BiographyDatasource(item); return parsedItem != null; } public static implicit operator BiographyDatasource(Item innerItem) { return innerItem != null innerItem.IsDerived(TemplateId) ? new BiographyDatasource(innerItem) : null; } public static implicit operator Item(BiographyDatasource customItem) { return customItem != null ? customItem.InnerItem : null; } }
Things to note:
- Yes, we inherit from ScoreUIItem. It wraps Sitecore’s CustomItem, and exposes a nifty utility method for determining if this item is a local, site shared or multi site shared content item.
- Our constructor needs to accept a Sitecore item, and needs to call our base class’ constructor with that item.
- Exposing our template ID and field names as constants grants us the power of strong typing, and magic strings are hidden. I prefer to create a static sub-class called Fields. This allows me to write things like Biography.Fields.Description throughout my application.
- Exposing a TryParse and implicit casting operators allow us to cast to and from Sitecore.Data.Items.Item. This bit of code is some unfortunate boilerplate that all of your Datasource models will have, but in the end it’s worth it.
- I like to stick these models in a Base or Core project. SCORE scaffolds a Data project, which is also a good place.
The Rendering Model
For all of you MVC folks, you can think of Rendering Models as your View Models. When working with SCORE, your rendering Model will inherit one of three things:
public class YourRenderingModel : Score.UI.Web.Areas.ScoreUI.Models.RenderingModelBase public class YourRenderingModel : Score.UI.Web.Areas.ScoreUI.Models.RenderingModelBase<YourRenderingParameters> public class YourRenderingModel : Score.UI.Web.Areas.ScoreUI.Models.RenderingModelBase<YourRenderingParameters, YourDatasourceModel>
There’s no real rules on which one you should and shouldn’t use. Each has a purpose, and I’ll try to elaborate:
- Use the RenderingModelBase signature if you’re building something like a basic structural component (no datasource, no rendering parameters).
- Use RenderingModelBase<YourRenderingParameters> if you’re building a basic presentation component (such as a wrapper… think SCORE’s ‘style box’). Rendering Parameters are really helpful for things like injecting custom CSS into presentation components. Even if you don’t need your own rendering parameters, you should probably use SCORE UI’s Score.UI.Data.RenderingParameters.BaseComponentParameters (corresponding Sitecore template located at /sitecore/templates/ScoreUI/Base/Rendering Parameters/Base Component).
- Use RenderingModelBase<YourRenderingParameters, YourDatasourceModel> when you need to allow content authors to enter actual content for your components. Again, if you don’t have a use case for custom rendering parameters, use SCORE UI’s BaseComponentParameters.
SCORE UI’s BaseComponentParameters enables the ability for your component to use SCORE’s Show/Hide mechanism. If you didn’t know, Show/Hide allows you inject CSS classes which media queries target to show and hide things on different viewports. This means that if you have something like a massive hero image that should be hidden on phones, you can allow content authors to configure that capability.
For our Biography rendering, let’s re-use SCORE UI’s Score.UI.Data.RenderingParameters.HighlightParameters (corresponding Sitecore template located at /sitecore/templates/ScoreUI/Base/Rendering Parameters/Highlight). This will allow us to define custom Biography variations under /sitecore/content/Playground/Selections/Highlight Styles.
Here is my rendering model:
public class BiographyRenderingModel : RenderingModelBase&amp;lt;HighlightParameters, BiographyDatasource&amp;gt; { public BiographyRenderingModel() : base("biography-wrapper") { } protected override BiographyDatasource InitializeDatasource(Item item) { BiographyDatasource ds; return BiographyDatasource.TryParse(item, out ds) ? ds : null; } }
The important pieces here are that:
- Whenever you inherit from RenderingModelBase, you should call the base constructor and pass in a unique class name for your component. This class name will surface in @Model.Classes along with other classes added by various rendering parameters.
- Your class isn’t fully initialized by just calling the constructor. Sitecore will additionally call InitializeDatasource and pass in the Datasource item defined by your rendering. If you ever need to construct one of your rendering models, you should handle calling InitializeDatasource as well.
To go along with this rendering model, we need to create a Model definition item in Sitecore (/sitecore/layout/models/your-site). Mine looks like this:
The View
Let’s start by creating a View Rendering under /sitecore/layouts/renderings. I am going to create mine at /sitecore/layouts/renderings/playground/features, which looks like this (note: I’ve edited the image to conserve space, and you can get to the thumbnail field if you enable standard fields at View > Standard Fields):
Things to note:
- I created a folder called Features underneath my tenant’s renderings folder to store this rendering. SCORE will pick up on the Feature’s folder and add that as a tab to the “Select a Rendering” dialog. I chose the word Features because SCORE Bootstrap UI stores Highlight under a tab called Features, and I want to add Biography as a selectable component within the same tab.
- This is a View Rendering, which means that we do not need a corresponding controller. We will use a Controller Rendering in a future post.
- We are using SCORE’s “content-all” token to automatically select our tenant datasource locations. If you’re not using SCORE, you can define a path or query at this point.
Here’s our view:
@using Playground.Data @model Playground.Web.Areas.Playground.Models.BiographyRenderingModel &amp;lt;div class="@Model.Classes"&amp;gt; @if (Model.HasDatasource) { &amp;lt;div class="portrait @Model.RenderingParameters.ImageClass"&amp;gt; @Html.Sitecore().Field(BiographyDatasource.Fields.Portrait) &amp;lt;/div&amp;gt; &amp;lt;div class="name"&amp;gt; @Html.Sitecore().Field(BiographyDatasource.Fields.Name) &amp;lt;/div&amp;gt; &amp;lt;div class="description"&amp;gt; @Html.Sitecore().Field(BiographyDatasource.Fields.Description) &amp;lt;/div&amp;gt; } else if (Sitecore.Context.PageMode.IsExperienceEditor) { &amp;lt;div class="error"&amp;gt; WARNING: Biography component has no datasource! &amp;lt;/div&amp;gt; } &amp;lt;/div&amp;gt;
Things to Note:
- Using @Html.Sitecore().Field(“field name”) allows Page Editor to work correctly with MVC.
- Wrapping your component in a DIV with a custom class is always a best practice. This provides your designers with a high level of flexibility.
- We check if the component has a datasource. If the datasource happens to be null (e.g., a content author deletes it and chooses to remove links), then the component will hide (aside from the outer wrapping div). When in page editor without a datasource, our content author will get a useful error message.
We’re almost done!
Placeholder Settings
Now this is where I’ve cheated. To figure out which placeholders this rendering should work in, I’ve gone to Bootstrap UI’s Highlight rendering (/sitecore/layout/Renderings/BootstrapUI/Content/Features/HighLight), and checked its links (Navigate > Links > Items that refer to the selected item). I found these:
- Snippet – [/sitecore/layout/Placeholder Settings/Playground/Snippet]
- Nested Content Area – [/sitecore/layout/Placeholder Settings/Playground/Playground Containers/Nested Content Area]
So this means that we should edit these placeholders (only the ones for our site!) and add our Biography rendering to the “Allowed Controls.” It’s that simple!
Testing it out
Now, when we open a page up in Page Editor, we should see our rendering available in Nested Content Areas under the Features tab:
If you add this to a page and check out its rendering parameters, you’ll notice that you have a few options:
- Highlight Style comes from /sitecore/content/Playground/Selections/Highlight Styles, and this uses SCORE’s standard CSS injection features. Whatever gets placed here will get translated into Model.Classes.
- Image Style comes from /sitecore/content/Playground/Selections/Image Styles, and this also uses SCORE’s standard CSS injection features. Whatever you put in here will appear at Model.RenderingParameters.ImageClass.
- The Show/Hide functionality is present. These also drive CSS classes and they will appear at Model.Classes.
You can confirm by inspecting the resulting HTML.
So what did we learn?
Building components in Sitecore can be a daunting task, especially if you’re new to the platform. While SCORE will help accelerate you in the right direction, there are still fundamental building blocks that you need to wrap your head around in order to be an effective developer. Some portions are similar to ASP.NET MVC, while others require a new mindset.
We just walked through building a very basic component, and I hope that this helps in some way. In the next post, I want to walk through modifying this component to pull additional profile data from an external system. To do that, we’ll use a Controller Rendering and some domain objects.
Until next time!
P.S. If you want to check out the source, you can find it here: https://github.com/jdylanmc/score-2.x-playground
Excellent post! I taught our new developer just yesterday how to do that. Now I have this link. 🙂
This post helped me figure out what I made wrong among my tasks. Thanks a lot Dylan, respect you!