Fortis & Sitecore Tutorial 2 – Renderings

Let’s continue…
Today will demonstrate a simple and convenient way of implementing Sitecore renderings which Fortis provides out of the box.

The latest Fortis version is built to support Sitecore MVC rendering types.

  • View Renderings
  • Controller Renderings

You are more than welcome to organize your razor views the usual way, but in the demo solution I have tried to use asp.net MVC areas in order to isolate website features and keep them “moduled” way. Sitecore seems to support the areas (as long as that works in demo solution :)).
Each area contains its own controllers, models and views. You can read more about areas for example here http://www.itorian.com/2013/10/area-in-mvc-5-with-example-step-by-step.html.

So there are two areas in the solution which define 2 logical modules:

  • ContentPage
  • Markup

website-areas

The “Markup” area is designed to contain all renderings that may appear on “master page” – general ‘every-page’ functionality like Header, Footer, Navigation, Grids, etc..
ContentPage – should contain simple content renderings for the content page.

Fortis.Model.RenderingModel class

Remember that Fortis in general is just a wrapper around Sitecore API. Here is not an exception and to build a rendering using Fortis we have to use a model class which is of type (or inherited from) Fortis.Model.RenderingModel class.

There are two types or RenderingModel class provided by Fortis:

  • Fortis.Model.RenderingModel<TPageItem, TRenderingItem>
  • Fortis.Model.RenderingModel<TPageItem, TRenderingItem, TRenderingParametersItem>

And here are main properties of IRenderingModel:

  • PageItem – exposed item from Sitecore.Mvc.Presentation.PageContext.Current.Item (which returns the Sitecore.Context.Item really)
  • RenderingItem – exposed item from Sitecore.Mvc.Presentation.RenderingContext.Current.Rendering.RenderingItem.InnerItem – returns the DataSource item or if its null then returns PageItem. RenderingItem property also supports queries in the Data Source.
  • RenderingParametersItem – exposed item for current rendering parameters if the rendering has it.
  • Factory – instance of the ItemFactory

This way all our renderings have the same API. If we need to extend the model class – we can simply inherit our model from the Fortis.Model.RenderingModel and add required functionality.
Remember that the RenderingModel class is meant to implement the Adapter pattern (or ViewModel if you used to it in ). It should be created and filled in inside the Controller and the rendering should just read data inside the model. But it should not fill data itself or do any requests to services etc…

In order to retrieve the current RenderingModel there are methods inside the ItemFactory:

  • ItemFactory.GetRenderingContextItems<TPageItem, TRenderingItem>()
  • ItemFactory.GetRenderingContextItems<TPageItem, TRenderingItem, TRenderingParametersItem>()

Controller Renderings

Let’s see how to create controller renderings with Fortis.

I have made a Header rendering (/sitecore/layout/Renderings/Fortis Demo/Markup/Header) and mapped it next way:
header-rendering-item
Please note the Area is filled in as described above.

In order to keep the solution de-coupled enough, controllers have to be very simple and they should not contain any business logic. Website controllers are just one of possible presentation layers for the solution so basically the controller action code should be looking like this:

  1. retrieve required IRenderingModel with expected PageItem and RenderingItem types
  2. create instance of extended model from retrieved rendering model
  3. call business logic services and fill extended model properties
  4. pass the model to the view.

The model class for Header rendering needs to have additional properties so here is my extended model class:

Model:

public class HeaderRenderingModel : NavigationRenderingModel
{
	public HeaderRenderingModel(IRenderingModel<IContentPageItem, IContentPageItem> model)
		: base(model)
	{
		this.NavigationItems = Enumerable.Empty<INavigationItem>();
	}

	public ISiteRootItem SiteRoot { get; set; }
}

HeaderRenderingModel is inherited from NavigationRenderingModel in order to call the partial rendering for the Navigation which is re-used in header and footer.

 public class NavigationRenderingModel : RenderingModel<IContentPageItem, IContentPageItem>
{
	public NavigationRenderingModel(IRenderingModel<IContentPageItem, IContentPageItem> renderingModel)
		: base(renderingModel.PageItem, renderingModel.RenderingItem, renderingModel.Factory)
	{
	}

	public NavigationRenderingModel(IRenderingModel<IContentPageItem, IContentPageItem> renderingModel, IEnumerable<INavigationItem> navigationItems)
		:this(renderingModel)
	{
		this.NavigationItems = navigationItems;
	}

	public IEnumerable<INavigationItem> NavigationItems { get; set; }
}

As long as it is a controller rendering – we need to create a controller class.

Controller:

public class MarkupController : WebsiteController
{
	protected readonly INavigationService NavigationService;

	public MarkupController(IItemFactory itemFactory, INavigationService navigationService) 
		: base(itemFactory)
	{
		this.NavigationService = navigationService;
	}

	public ActionResult Header()
	{
		var renderingModel = this.ItemFactory.GetRenderingContextItems<IContentPageItem, IContentPageItem>();
		var siteRoot = this.ItemFactory.GetSiteRoot<ISiteRootItem>();

		var headerModel = new HeaderRenderingModel(renderingModel)
		{
			SiteRoot = siteRoot,
			NavigationItems = this.NavigationService.GetNavigation<INavigationItem>(siteRoot, page => !page.HideFromNavigation.Value)
		};

		return View(headerModel);
	}
}

Rendering:

@model FortisDemo.Website.Areas.Markup.Models.HeaderRenderingModel
<div class="container">
    <div class="logo">
        <a href="/">
            @Model.SiteRoot.SiteLogo.Render()
        </a>
    </div>
    <div class="head-nav">
        <span class="menu"> </span>
        @Html.Partial("~/Areas/Markup/Views/Markup/Partials/Navigation.cshtml", Model)
    </div>
</div>

View Renderings

When your rendering does not need any additional data, you can create a regular ‘View Rendering’ without writing dedicated controller class. Fortis installs a config file /App_Config/Include/Fortis/Fortis.Mvc.config where defines a resolver for the view rendering model.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
		<pipelines>
			<mvc.getModel>
				<processor patch:before="processor[@type='Sitecore.Mvc.Pipelines.Response.GetModel.GetFromItem, Sitecore.Mvc']" type="Fortis.Mvc.Pipelines.GetModel.GetFromView, Fortis.Mvc"/>
			</mvc.getModel>
		</pipelines>
	</sitecore>
</configuration>

This adds a possibility to define a RenderingModel which the view expects. For example have a look at ‘~/Areas/ContentPage/Views/PageTitle.cshtml’ which is mapped to sitecore rendering item ‘/sitecore/layout/Renderings/Fortis Demo/Content Page/Page Title’:

@using Fortis.Model
@using FortisDemo.Model.Templates.UserDefined
@model IRenderingModel<IContentTitleItem, IContentTitleItem>

<div class="banner">
	<div class="container">
		<div class="main">
			<h2>@Model.RenderingItem.ContentTitle</h2>
		</div>
	</div>
</div>

This is a view rendering and it expects the model instance of IRenderingModel<IContentTitleItem, IContentTitleItem>. The configured handler will call ItemFactory.GetRenderingContextItems<IContentTitleItem, IContentTitleItem>() for you and pass the retrieved model to your view.

Render() method

As we talked in previous post, each generated field in Fortis is an instance of field-specific FieldWrapper class.
Each FieldWrapper implements IHtmlString interface and calling @Model.RenderingItem.ContentTitle will execute IHtmlString.ToHtmlString() method where the this.Render() method will be called. So in result this statement @Model.RenderingItem.ContentTitle will be equal to @Model.RenderingItem.ContentTitle.Render().

There is a list of variants how we can execute the .Render() method and for each field type it can have some extra functionality. Here are just a few examples of field render calls:

<!--Render an image with attributes -->
@Model.RenderingItem.ContentImage.Render(new { @class = "img-responsive", mw = 350 })

<!-- Render link field -->
@Model.RenderingItem.ContentLink.Render(new { target = "_blank" })

<!-- Or render link field with inner html which will be displayed even when the link is not set -->
@Model.RenderingItem.ContentLink.RenderBeginField(new { @class = "link", target = "_blank" })
<div>Inner html here</div>
@Model.RenderingItem.ContentLink.RenderEndField()

<!-- Render field and forbid its editing in edit mode -->
@Model.RenderingItem.ContentTitle.Render(editing: false)

<!-- Reder field with Enclosing Tag. Tag won't be displayed if the field is empty. -->
@Model.RenderingItem.ContentTitle.Render(new { EnclosingTag = "span" })

<!-- Render field with Enclosing html. The 'before' and 'after' html won't appear if the field is empty -->
@Model.RenderingItem.ContentTitle.Render(new { Before = "<div class=\"my-class\">", After = "</div>" })

As you can see there is a quite decent and rich API for creating Sitecore rendering using Fortis.
As mentioned in previous post, all source code could be found here: https://github.com/vhil/fortis-demo

Enjoy!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s