CREATING BLOCKS IN EPISERVER 7 PREVIEW

This post is about blocks in EPiServer CMS 7. Blocks are like a built-in composer support to the cms core. Blocks makes it simpler to reuse functionality for developers and web editors. It’s a lot like building a “composer function” with the great power of using MVC and typed page data in EPiServer 7.

We are not forced to implement a controller/model. For simpler blocks it could maybe seem overkill to implement a controller/model, more on this topic later.

The block type

A block is a lot like a PageData object but is defined as a BlockData object that inherits from ContentData, same base type as PageData inherits from.

Imagine that we are building a web application for a book store then we would maybe need a reusable block that shows the latest books.

We create a block type like creating a typed page data object but the class inherits from BlockData instead of PageData. The block definition contains a heading for the block and a reference to the root of where to get books from.

[ContentType(GUID = "9E4C0D85-1139-48EA-B905-67C08BEE3C69",
            DisplayName = "Latest books",
            Description = "Simple block that displays latest books",
            GroupName = "Book listing",
            Order = 1)]
public class LatestBooksBlock : BlockData  
{
    [CultureSpecific]
    [Display(
        GroupName = SystemTabNames.Content,
        Name = "Heading",
        Description = "The heading of the block.",
        Order = 1)]
    public virtual string Heading { get; set; }

    [CultureSpecific]
    [Display(
        GroupName = SystemTabNames.Content,
        Name = "Book root",
        Description = "The root where to get new books from",
        Order = 2)]
    public virtual PageReference BookRoot { get; set; }
}

The model

For this block we create a view model that is representing data for the presentation (the view). The model contains a list of new books and our complete block type object.

public class LatestBooksViewModel  
{
    public LatestBooksBlock LatestBooksBlock { get; private set; }
    public IEnumerable<PageTypes.BookPage> LatestBooks { get; set; }

    public LatestBooksViewModel(LatestBooksBlock latestBooksBlock, IEnumerable<PageTypes.BookPage> latestBooks) 
    {
        LatestBooksBlock = latestBooksBlock;
        LatestBooks = latestBooks;
    }
}

The controller

The controller will get the data we need for the block, add it to the view model, picking a view and wire it all together. Actually its quit simple. Following code is assuming that some kind of container is wiring up our dependencies. More on this topic here and here. Also the sample mvc templates are shipped with a structure map dependency resolver you can look at.

public class LatestBooksController : ActionControllerBase, IRenderTemplate<LatestBooksBlock>  
{
    private readonly IContentRepository contentRepository;

    public LatestBooksController(IContentRepository contentRepository) 
    {
        this.contentRepository = contentRepository;
    }

    public ActionResult Index(LatestBooksBlock latestBooksBlock) 
    {
        var latestBooks = Enumerable.Empty<BookPage>();
        var bookRoot = latestBooksBlock.BookRoot;

        if (!PageReference.IsNullOrEmpty(bookRoot)) 
        {
            latestBooks = contentRepository.GetChildren<BookPage>(bookRoot, LanguageSelector.AutoDetect(true), 0, 5).OrderByDescending(x => x.StartPublish);
        }

        return View(new LatestBooksViewModel(latestBooksBlock, latestBooks));
    }
}

By inheriting from ActionControllBase we get some useful stuff and by implement IRenderTemplate with T as our block type (LatestBooksBlock) we bind the controller to our block type.

The IContentRepository interface is injected to the constructor which is the one of the (many(!)) interfaces for the EPiServer.Datafactory.

When the block loads the Index method is executed and the instance of our block type is injected as a parameter. The method then gets the five latest books and adding it to a instance of our view model and passing it to the view. Notice the startIndex and maxRows parameters within the GetChildren call, sweet stuff :)

The view

The view is a partial view that is bound to controller by default asp.net mvc folder structure convention. The file is placed in ~/Views/LatestBooks/Index.cshtml.

@using System.Linq;
@using EPiServer.Web.Mvc.Html
@model EPiBooks.Models.Blocks.LatestBooksViewModel

<h1>@Html.PropertyFor(x => x.LatestBooksBlock.Heading)</h1>  
@if (Model.LatestBooks.Any()) 
{
    <ul>
        @foreach (var book in Model.LatestBooks) 
        {
            <li>@Html.ContentLink(book)</li>
        }
    </ul>
}

We are defining that we are binding the LastestBooksViewModel to the view by adding the “@model”-line same model that we passed to the view from our controller. The view then uses simple if-statements and loops to render a list of new books with the help of some built-in EPiServer html helpers

Adding the block to a page

Now we have created our block and we want to use it. A block can be used similar to a PageTypeBuilder property group described here.

[Display(Name = "Latest books",
     Description = "A block that lists latest books.",
     GroupName = SystemTabNames.Content,
     Order = 3)]
 public virtual LatestBooksBlock LatestBooks { get; set; }

The block will then be a part of the page type and can be edited directly on the page in edit mode.

A block can also be added dynamically to ContentArea.

[Display(Name = "Content area",
    Description = "A content for adding any shared block",
    GroupName = SystemTabNames.Content,
    Order = 4)]
public virtual ContentArea ContentArea { get; set; }  

In this way we can create a block separate from a page and reuse the same block at different pages. Now we can create instances of shared blocks and drag-and-drop them to a ContentArea of any page.

Creating a block without controller/model

As I wrote we can create a block without using a controller/model. We create a simple block type, same way as for the LastestBooksBlock. The block is only containing a text editor.

[ContentType(GUID = "6fe1bf14-2aeb-4954-9fcb-eb989f92d46b",
    DisplayName = "Text block",
    Description = "Simple block that displays text from a editor",
    Order = 2)]
public class TextBlock : BlockData  
{
    [CultureSpecific]
    [Display(Name = "Main body",
             Description = "The main body using the XHTML-editor you can insert for example text, images and tables.",
             GroupName = SystemTabNames.Content,
             Order = 1)]
    public virtual XhtmlString MainBody { get; set; }
}

We skip all the controllers and models and just add a view to out shared folder (~/Views/Shared/TextBlock.cshtml)

@model EPiBooks.BlockTypes.TextBlock

@Model.MainBody

Now we can created instances of TextBlock and add them to a ContentArea.

Conclusion

I think blocks are a really powerful and cool feature that I know we can befit from and use a lot in future project. I really like the idea of a built-in composer into the core of the cms and I hope that blocks will be replacing composer after the CMS 7 release.

comments powered by Disqus