In my previous posts — please be sure to read this post followed by this post and then this post before moving forward since you’ll need some context, and some of the code below is dependent on code in these previous posts — I showed two approaches for rendering a custom attribute — I called this attribute Tag in these posts and will continue to do so here — on a link set in a General Link field which is rendered using the Sitecore ORM Glass.Mapper.
In this post, I am going to share an approach on achieving the same but using a custom GlassController coupled with a class that implements the IGlassHtml interface — the code for this class can be found in this post.
First, we need a model to experiment with. I built the following class to serve as an example for this post:
using Sitecore.Data; using Glass.Mapper.Sc.Configuration.Attributes; using Sitecore.Sandbox.Glass.Mapper.Sc.Fields; namespace Sitecore.Sandbox.Models { public class SampleItemModel { [SitecoreId] ID ItemID { get; set; } public string Title { get; set; } public string Text { get; set; } [SitecoreField("Link One")] public TagLink LinkOne { get; set; } [SitecoreField("Link Two")] public TagLink LinkTwo { get; set; } } }
Next, we need a ViewModel. I built the following as well to serve as an example for this post:
using System.Web.Mvc; using Sitecore.Data; using Glass.Mapper.Sc.Configuration.Attributes; namespace Sitecore.Sandbox.Models.ViewModels { public class SampleItemViewModel { public MvcHtmlString Title { get; set; } public MvcHtmlString Text { get; set; } public MvcHtmlString LinkOne { get; set; } public MvcHtmlString LinkTwo { get; set; } } }
I then built the following subclass of Glass.Mapper.Sc.Web.Mvc.GlassController:
using System; using System.Linq.Expressions; using System.Web; using System.Web.Mvc; using Sitecore.Configuration; using Sitecore.Diagnostics; using Glass.Mapper.Sc; using Glass.Mapper.Sc.Web.Mvc; using Glass.Mapper.Sc.Web; using GlassMapperSc = Glass.Mapper.Sc; namespace Sitecore.Sandbox.Glass.Mapper.Sc.Web.Mvc.Controllers { public abstract class SandboxGlassController : GlassController { private static IGlassHtmlFactory GlassHtmlFactory { get; set; } static SandboxGlassController() { GlassHtmlFactory = CreateGlassHtmlFactory(); } public SandboxGlassController() : this(GetContextFromHttp()) { } protected SandboxGlassController(ISitecoreContext sitecoreContext) : this(sitecoreContext, GlassHtmlFactory.CreateGlassHtml(sitecoreContext), new RenderingContextMvcWrapper(), null) { } public SandboxGlassController(ISitecoreContext sitecoreContext, IGlassHtml glassHtml, IRenderingContext renderingContextWrapper, HttpContextBase httpContext) : base(sitecoreContext, glassHtml, renderingContextWrapper, httpContext) { } protected static IGlassHtmlFactory CreateGlassHtmlFactory() { IGlassHtmlFactory factory = Factory.CreateObject("sandbox.Glass.Mvc/glassHtmlFactory", true) as IGlassHtmlFactory; Assert.IsNotNull(factory, "Be sure the configuration is correct in utilities/customAttributesAdder of your Sitecore configuration!"); return factory; } protected static ISitecoreContext GetContextFromHttp() { try { return GlassMapperSc.SitecoreContext.GetFromHttpContext(null); } catch (Exception exception) { Log.Error("Failed to create SitecoreContext", exception, typeof(SandboxGlassController)); } return null; } protected virtual MvcHtmlString Editable<T>(T model, Expression<Func<T, object>> field, object parameters = null) { return ConvertToMvcHtmlString(GlassHtml.Editable(model, field, parameters)); } protected virtual MvcHtmlString RenderLink<T>(T model, Expression<Func<T, object>> link, object attributes = null, bool IsEditable = false) { return ConvertToMvcHtmlString(GlassHtml.RenderLink(model, link, attributes, IsEditable)); } protected virtual MvcHtmlString ConvertToMvcHtmlString(string value) { return new MvcHtmlString(value); } } }
I declared the above class as abstract since I don’t see how it could live on its own — none of its methods return ViewResult or ActionResult instances.
When an instance of the above class is created, an instance of a IGlassHtmlFactory — this is defined in my last post — is used to create an instance of a IGlassHtml which adds a Tag attribute and value to the link attributes when applicable. The Editable and RenderLink methods delegate to methods with the same signature on the IGlassHtml instance, and then transform the returned strings into MvcHtmlString instances via the ConvertToMvcHtmlString method so that the fields work in the Sitecore Experience Editor
I then built the following subclass of the above class for testing:
using System.Web.Mvc; using Sitecore.Sandbox.Glass.Mapper.Sc.Web.Mvc.Controllers; using Sitecore.Sandbox.Models; using Sitecore.Sandbox.Models.ViewModels; namespace Sitecore.Sandbox.Web.Mvc.Controllers { public class SampleItemController : SandboxGlassController { public ViewResult MainContent() { SampleItemModel model = SitecoreContext.GetCurrentItem<SampleItemModel>(); if(model == null) { return View(); } SampleItemViewModel viewModel = new SampleItemViewModel { Title = Editable(model, x => x.Title), Text = Editable(model, x => x.Text), LinkOne = RenderLink(model, x => x.LinkOne, null, true), LinkTwo = RenderLink(model, x => x.LinkTwo, null, true) }; return View(viewModel); } } }
The MainContent method gets an instance of a SampleItemModel from the current Item — in this example these are fields on the home Item — and transforms this into a SampleItemViewModel instance using the protected methods defined on its base class. The SampleItemViewModel instance is then sent to the View.
I then whipped up the following Razor View so we can see some data on the front-end:
@model Sitecore.Sandbox.Models.ViewModels.SampleItemViewModel @if(Model == null) { return; } <div id="Content"> <div id="LeftContent"> </div> <div id="CenterColumn"> <div id="Header"> <img src="/~/media/Default Website/sc_logo.png" id="scLogo" /> </div> <h1 class="contentTitle"> @Model.Title </h1> <div class="contentDescription"> @Model.Text <div> @Model.LinkOne </div> <div> @Model.LinkTwo </div> </div> </div> </div>
There isn’t much going on in the above Razor View — it’s just displaying data from the ViewModel.
I am going to omit how I wired-up the Controller above with a Controller rendering in Sitecore. If you don’t know how to wire-up Controllers with Controller renderings in Sitecore, please watch this video by Martina Welander.
Let’s see if this works (works on my machine 😉 ).
First, I ensured I had Tag atrributes set on my two General Link fields on my home Item in Sitecore:
Once I built and deployed everything, I navigated to my homepage Item, and saw the following rendered HTML:
As you can see, it worked. 😀
If you have any questions/comments/thoughts, please leave a comment.