EPiServer  /  CMS August 14, 2012

Working programmatically with Local Blocks in EPiServer 7

Blocks used as properties may not be as sexy as shared blocks with their drag-n-dropiness. Still, they can be just as useful and, as we'll see, they have some interesting characteristics.

Blocks is a new, exciting and much needed concept in EPiServer 7. As I’ve explained in my post about the new data model in EPiServer 7 there are two kinds of blocks, shared blocks and local blocks. A single block type and renderers for it can be used both for local and shared blocks. Both Anton Kallenberg and Alexander Haneng have written great introductions and how-tos for both shared and local blocks so I’ll direct you to either of them for an introduction. Anton’s post is available here and Alexander’s here.

In this post we’ll look at how to work programmatically with local blocks. We’ll also see some interesting aspects of local block properties.

Example block type and local block property

Local blocks are created by first creating a block type and thereafter adding a property of that type to a page type, or some other content type. In order to work with a local block programmatically we’ll of course need a local block, so here’s an example of simple block type that I will use throughout this post:

[ContentType(
    DisplayName = "Fact box",
    GroupName = "AlloyTech",
    Description = "For displaying fact boxes related to "
                    + "the main content in articles.")]
public class FactBoxBlock : BlockData
{
    public virtual string Title { get; set; }
    public virtual XhtmlString Content { get; set; }
}

And here’s a page type with a local block property of the above block type.

[ContentType]
public class ArticlePage : PageData
{
    public virtual XhtmlString MainBody { get; set; }
    public virtual FactBoxBlock FactBox { get; set; }
}

Creating local block objects

Local blocks always live on a page or other form of IContent and they are automatically created when the host content, articles in this example, is created. Therefore there isn’t really a concept of creating local block objects, only assigning their values. For instance, to programmatically create an ArticlePage and set the values on its fact box block we could write code such as this:

//Retrieve an IContentRepository using the ServiceLocator.
//In an MVC scenario use constructor injection instead.
//We could also have used DataFactory.Instance, but this
//is "the new way".
var contentRepository = 
    ServiceLocator.Current
        .GetInstance<IContentRepository>();

//Create an article page under the start page and
//set the required PageName property. This will also
//implicitly set the required IContent.Name property
//as PageData's Name property maps to the PageName property.
var newArticle = contentRepository
    .GetDefault<ArticlePage>(PageReference.StartPage);
newArticle.PageName = tbArticleName.Text;
            
//Set the Title and Content of the local FactBox block
newArticle.FactBox.Title = tbFactBoxTitle.Text;
newArticle.FactBox.Content = 
    new XhtmlString(tbFactBoxContent.Text);

//Save the page. This will save all of the values we've
//set including those on the local block.            
contentRepository.Save(
    newArticle, 
    SaveAction.Publish, 
    AccessLevel.Publish);

Local Blocks cannot be null

There’s not much of interest to the above code except for one detail, we assume that the FactBox property has a value. That is, we don’t assign it a new FactBox instance and we don’t bother with null checking it. We can count on it being there as, as stated earlier, local blocks are created with their host content. In fact, we couldn’t even make it null even if we wanted to.

newArticle.FactBox = null;
//Will be false
var factBoxIsNull = newArticle.FactBox == null)

var contentLink = contentRepository.Save(
    newArticle, 
    SaveAction.Publish, 
    AccessLevel.Publish);
newArticle = contentRepository.Get<ArticlePage>(contentLink);
            
//Still false
factBoxIsNull = newArticle.FactBox == null;

In the above code we explicitly set the FactBox property to null, but doing so has no effect for the page that we’re saving. Unsurprisingly it doesn’t effect the saved page, retrieved from the database, either.

Similarly, just as we can’t force a local block property to be null by setting it to null we can’t assign a new, non-null, value to it. Or rather, while we can assign it a new object, the values of the new object won’t be persisted.

//Create a new FactBox block and assign it
//to the local block property
var factBox = new FactBoxBlock();
factBox.Title = tbFactBoxTitle.Text;
factBox.Content = 
    new XhtmlString(tbFactBoxContent.Text);
newArticle.FactBox = factBox;

//Will be true
newArticle.FactBox == null;

var contentLink = contentRepository.Save(
    newArticle, 
    SaveAction.Publish, 
    AccessLevel.Publish);

newArticle = contentRepository.Get<ArticlePage>(contentLink);

//Will be false
newArticle.FactBox == null;

//Will be true
newArticle.FactBox.Title == null;

Somewhat surprisingly setting a local block property to a new object actually makes the property’s value null. This is only temporary though as order is restored after loading the host object from the database after saving.

Updating Local Blocks

Updating a local block programmatically involves updating the host content as local blocks are persisted on other content. The steps to update a local block is identical to updating any other property on the host content. We need to retrieve the host content, clone it, change one or more values and finally save it.

//Clone the host content, in this case retrieved
//by using the CurrentPage property in a template/renderer.
var clone = (ArticlePage) 
    CurrentPage.CreateWritableClone();

//Update values on the local block
clone.FactBox.Title = tbTitle.Text;
clone.FactBox.Content = 
    new XhtmlString(tbContent.Text);

//Save the host content
contentRepository.Save(
    clone, 
    SaveAction.Publish, 
    AccessLevel.Publish);

Clearing the values of a Local Block

What if we want to programmatically clear all of a local block’s values? We’ve seen that we can’t force a local block property to be null by assigning it null as value. Assigning it a new value of the same type (FactBoxBlock in our case) doesn’t have any significant effect either. There is a SetDefaultValues method on BlockData, but that doesn’t have any effect for local blocks.

So, what to do? We could of course assign a null value to each individual property of the block explicitly. That’s not a big problem for blocks with few properties, such as the fact box block we’ve been using as an example. But for blocks with a lot of properties or perhaps of an unknown type that’s not a good option. What we can do then is utilize that BlockData implement IContentData and therefor must have a Properties property through which all properties are exposed. We can iterate over each of the block’s properties and clear their values. In the case of BlockData the Properties property from IContentData is explicit so we’ll first have to cast the local block to IContentData.

var clone = (ArticlePage) CurrentPage.CreateWritableClone();

var contentData = (IContentData)clone.FactBox;
foreach (var property in contentData.Properties)
{
    property.Clear();
}

contentRepository.Save(
    clone,
    SaveAction.Publish,
    AccessLevel.Publish);

Checking if a Local Block is empty

We’ve seen that there’s no point in null-checking a local block property. It will always have a value given that the host content object has been created or loaded using an IContentRepository. What if we wanted to check if the block should be rendered or not depending on if it has any values? Hadn’t it always been non-null we could simply have checked if it wasn’t null and then assumed that it should be rendered, but we can’t. What we can do is take again iterate over each of its properties to see if any of them has any values.

//using System.Linq;

var contentData = (IContentData) CurrentPage.FactBox;
var factBoxIsEmpty = contentData.Properties
    .Any(x => x.IsNull);

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

My book

Want a structured way to learn EPiServer 7 development? Check out my book on Leanpub!

More about EPiServer CMS