Perplex Content Blocks
This is the fifth article in the series about the various block editors for Umbraco.
Perplex Content Blocks is something we all might have got a silent demo of in the many talks and demos that the team from Perplex Digital has done over the years. And the feature that might have caught the eyes of the audience could be that really nice preview in the backoffice. Perplex Content Blocks is a block editor developed by the award-winning team at Perplex Digital.
In this article am going to build a page that looks as shown below and I will be explaining the configuration and code to build the Hero component. Code for the entire page is available in my repo.
To get started with Perplex Content Blocks the package must be installed.
Install-Package Perplex.ContentBlocks
Perplex Content Blocks are based on Element Types. The first step is to create an element type. My Hero element type is shown below. It has an alias elementHero
.
Perplex Content Blocks require a Nested Content data type each for each of the element types. So I have created a data type for this element type. The min and max items must be set to 1 on the Nested Content data type. I am also going to note down the id of this data type from the Info Content App. The integer id or the Guid can be used. The id should be used to specify the ContentBlockDefinitions
With Perplex Content Blocks, each of the Nested Content data type must be added as a ContentBlockDefinition
to the IContentBlockDefinitionRepository
. For this, I create a Component
and register the Component using a Composer
. This is a Umbraco V8 concept. More information about composing can be found here.
Below is the code for my Component.
public class PerplexContentBlocksComponent : IComponent
{
private readonly IContentBlockDefinitionRepository _definitions;
public PerplexContentBlocksComponent(IContentBlockDefinitionRepository definitions)
{
_definitions = definitions;
}
public void Initialize()
{
var heroBlock = new ContentBlockDefinition
{
Name = "Hero Content Block", //name of the block
Id = Guid.Parse("b1f44c8a-8f66-475e-bdd3-21b3a48c157e"),//unique id, create your own guid
DataTypeKey = Guid.Parse("b9f44c8a-8f66-475e-bdd3-21b3a48c157e"), //datatype key for the nested content data type that was created
PreviewImage = "/assets/img/hero.png", // Image that shows in the UI as a preview of this block. Relative path from the root of your site to an image.
Description = "Hero Content Block", //Description of this Content Block
CategoryIds = new[] //List of ids of the categories this Content Block should appear in. This references the id of a IContentBlockCategory
{
Perplex.ContentBlocks.Constants.Categories.Headers,
Perplex.ContentBlocks.Constants.Categories.Content,
_demoContentBlockCategory.Id
},
Layouts = new IContentBlockLayout[] //List of all layouts of this Content Block
{
new ContentBlockLayout
{
Id =Guid.Parse("b2f44c8a-8f66-475e-bdd3-21b3a48c157e"), //unique id, create your own guid
Name = "Layout 1",//name for layout
Description = "Centre Aligned Text", //description
PreviewImage = "/assets/img/hero.png", //preview image for backoffice
ViewPath = "~/Views/Partials/PerplexContentBlocks/Hero/Layout1.cshtml" //view for website rendering
},
new ContentBlockLayout
{
Id = Guid.Parse("b3f44c8a-8f66-475e-bdd3-21b3a48c157e"),
Name = "Layout 2",
Description = "",
PreviewImage = "/assets/img/layout2.png",
ViewPath = "~/Views/Partials/PerplexContentBlocks/Hero/Layout2.cshtml"
},
}
};
_definitions.Add(heroBlock);
}
public void Terminate()
{
}
}
}
To add my content block definitions to the collection, I inject IContentBlockDefinitionRepository
into my Component. I can then define my definition and add it to the definition repository. Each definition consists of a
- Id - A unique identifier for the definition. You must create your Guid.
- Name - Name of the block
- Description - Description of the block
- Preview Image - Image that shows up as the preview image for the block
- DataTypeKey - The guid key of the Nested Content data type for the content block
- Category Ids - With Perplex Content Blocks, you can categorise your blocks. Categorising blocks makes them appear in a specific category in the block picker dialog. This can be particularly useful when you have a large collection of content blocks and you don't wish to overload your editor. There are two categories available - Header and Footer. You can define your own categories. An example of it can be found in my repo.
- Layouts - Perplex Content Blocks lets you define multiple layouts or website rendering for your content block. Each layout item has a create your own guid id, name, description, preview image and the view path for website rendering. Content editors can specify which layout to choose at the time of content editing. More information about Layouts can be found here.
There are few more options to limit a content block definition to doctypes and cultures which can be found here.
I also need to register my Component.
[RuntimeLevel(MinLevel = Umbraco.Core.RuntimeLevel.Run)]
public class ContentBlockComposer : ComponentComposer<PerplexContentBlocksComponent> { }
Now I need to create a data type based on Perplex.ContentBlocks and add it to my document type. Its a single data type that is needed as the blocks available to a particular document type is actually controlled by code.
Its pretty self-explantory. But the structure defines the structure of the page. I have set it to blocks. You also have Header and Header+Blocks. Header introduces a header area inside the CMS and can be a good way to give a logical idea to the page structure. I have also selected the ability to add blocks without header, otherwise it expects a header block before content can be added.
Rendering Perplex Content Blocks
Content Blocks can be rendered using the extension method RenderContentBlocks()
. I can call this extension method passing in the property to render the content blocks. The view paths are set in the content block definition and the extension method will pick up the correct view for rendering based on the layout specified by the editor.
@inherits Umbraco.Web.Mvc.UmbracoViewPage<ContentModels.PerplexContentBlocks>
@using ContentModels = Umbraco.Web.PublishedModels;
@using Perplex.ContentBlocks.Rendering
@{
Layout = "_Layout.cshtml";
}
@Html.RenderContentBlocks(Model.ContentBlocks)
The partial view gets a IContentBlockViewModel<TContent>
where TContent
is the Models Builder model for the element type. And here is the code for my partial view. You must remember to create partial views for each layout specified.
@using Perplex.ContentBlocks.Rendering;
@model IContentBlockViewModel<ElementHero>
<header class="masthead" style="background-image:url('@Model.Content.Image.Url')">
<div class="container">
<div class="masthead-subheading">@Model.Content.PreHeading</div>
<div class="masthead-heading text-uppercase">@Model.Content.Heading</div>
<a class="btn btn-primary btn-xl text-uppercase js-scroll-trigger" target="@Model.Content.CallToActionLink.Target" href="@Model.Content.CallToActionLink.Url">@Model.Content.CallToActionLink.Name</a>
</div>
</header>
You can also pass in custom view models if needed. More information can be found here.
Preview in backoffice
Perplex Content Blocks makes use of the native preview feature in Umbraco to provide a preview of content to editors. The preview is quite advanced with both desktop and mobile views. The preview rolls automatically to the point based on the block being edited. No customisations are needed.
Use Cases
Perplex Content Blocks can be a good choice for content-heavy, brochureware sites.
Points to Remember
- Supported on Umbraco 8.1+
- Fantastic editor interface with an inbuilt preview feature
- Ability to copy single/multiple items
- Ability to hide an item from rendering without developer effort
- Settings take the shape of layouts, but can get very complex quickly if there are too many settings
- Ships with a fantastic block picker dialog well complimented by a layout picker
- There is a learning curve involved with setting up extra classes for the content block definitions.