Small helper from my side for those who search for simmilar solution.
It's a helper that allows to render content area blocks and at the same time send them a "message" are they first or last in rendering.
Helper clas:
public static class RazorHelper { public static MvcHtmlString If(this string text, bool condition) { return new MvcHtmlString(condition ? text : string.Empty); } public static MvcHtmlString IfValue(this string name, string value) { bool condition = !(string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value)); return new MvcHtmlString(condition ? string.Format(" {0}=\"{1}\"", name, value) : string.Empty); } public static MvcHtmlString IfValue(this string name, int value) { bool condition = (value > 0); return new MvcHtmlString(condition ? string.Format(" {0}=\"{1}\"", name, value) : string.Empty); } public static HelperResult RenderBlocks<TModel>(this ContentArea content, WebViewPage<TModel> wvp) { return new HelperResult(writer => { if(null != content) { var blocks = content.FilteredContents.ToArray(); for(int i = 0, len = blocks.Length; i < len; i++) { var block = blocks[i]; var firstOrLast = ((i == 0) ? "first " : "") + ((i == len - 1) ? "last" : ""); WebPageExecutingBase.WriteTo(writer, wvp.Html.PropertyFor(x => block, new { @class = firstOrLast })); } } }); } }
Top razor component rendering part (use inside *.cshtml):
@Model.Column1.RenderBlocks(this)
Use inside block (*.cshtml):
@{ var classTag = (String)ViewData["class"]; var isLast = (null != classTag && classTag.Contains("last")); var isFirst = (null != classTag && classTag.Contains("first")); } @if(!isLast) { <hr class="horizontal" /> }
Hm, this looks like a pretty nice addition to my Bootstrap aware content area render :) Can I borrow, refactor (I would prefer just something like Html.PropertyFor(m => m.CurrentPage.ContentArea)), cleanup and add to the lib?
Just a question, I use this code to add a index to the blocks in a contentarea that displays it like step 1, step 2 and so on.
I use 7.5 so it might not work for 7.0
public class ApplicationStepBlockController : BlockController<ApplicationStepBlock> { public override ActionResult Index(ApplicationStepBlock currentBlock) { var currentContentArea = ControllerContext.ParentActionViewContext.ViewData.Model as ContentArea; int index = 1; // we'll need to check if it is actually rendered in a ContentArea. if (currentContentArea != null) { // the index of the current selected block index = currentContentArea.Contents.IndexOf(currentBlock as IContent) + 1; } ViewBag.Index = index; return PartialView(currentBlock); } }
Would it not be possible to just do like this:
ViewBag.IsFirst = index == 1; ViewBag.IsLast = index == currentContentArea.Contents.Count();
Yes, it will work too... Only if you have a controller for your blocks. In my case I prefer to make all job on Razor level, no controllers, no code changes... quick changes in formatting and you got a preview in a second.
I actually prefer to add it to my content area render, so that means that block will receive this info even without controller.
Btw, what was reason to get this info on back-end? wasn't requirements solvable using CSS only?
Oleksandr and Valdis, that is true, great point!
Valdis, the only reason is propably that I am not so great at more advanced css.
Henrik a thing that might break your example is IF the editor adds the same content twice even if this might not be the case in your step-by-step solution.
Also I'm getting a bit scared if a partial content (block as property, block in contentarea, page in contentarea etc..) tries to reach things outside their zone.
How about that when you render your content area, for each iteration you push your current index as information to your Controller (using ViewData/ViewBag).
True Alf, thanks for the feedback.
The controller does not need to know of the index, it is only for the display, do you know a better way to get currentindex of contentarea without doing like I do?
What I've seen so far, the ContentArea rendering doesn't tell each Content Area Item rendering about its index.
I would propose that you create a custom renderer for ContentAreas, either use a Tag or replace the renderer using an IConfigurableModule (see DependencyResolverInitialization and AlloyContentAreaRenderer in the Alloy templates)
I'm almost finished adding this stuff to Bootstrap rendere (it will give block info even if that's rendered without controller - in case of "dumb" blocks). You can sneak that code fragment from me then :)
just FYI also will be interesting to provide to the blocks how deep they are in rendering hierarchy.
Use case:
- my blocks support "preview" mode, in which they shows additional elements on UI for making possible easy editing of properties
- such preview works only if "block" is top rendering level, not inside other containers.
Quick pattern (with reference on helper class that I publish before):
var stack = this.Context.Items["Epi:ContentAreaStack"] as ICollection; var nested = (null == stack) ? 0 : stack.Count;
<div class="@("back".If(!PageEditing.PageIsInEditMode || nested > 0))"> @Html.PropertyFor(x => x.Content) </div>
This has been a long period of silence, but anyway. Thanks to Erik Henningson to inspire and finally push me :)
Now, if you happen to use Bootstrap Content Area and you need to get index of the block - you are able to write:
<div> Index: @Html.BlockIndex() </div>
Hi,
is any good way of detecting that block is first or last in a list during the rendering?