Is block first or last?

Vote:
 

Hi,

is any good way of detecting that block is first or last in a list during the rendering?

#114327
Dec 10, 2014 10:32
Vote:
 

Solution

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" />
}

#114331
Edited, Dec 10, 2014 11:15
Vote:
 

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?

#114341
Dec 10, 2014 13:57
Vote:
 

You are welcome. Use without any restrictions.

#114374
Dec 11, 2014 9:18
Vote:
 

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();



#114385
Dec 11, 2014 9:26
Vote:
 

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.

#114396
Dec 11, 2014 9:31
Vote:
 

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?

#114397
Dec 11, 2014 9:38
Vote:
 

Oleksandr and Valdis, that is true, great point!

Valdis, the only reason is propably that I am not so great at more advanced css.

#114398
Dec 11, 2014 9:51
Vote:
 

Henrik are you using Bootstrap render or default built-in one?

#114400
Dec 11, 2014 10:43
Vote:
 

The default built-in one

#114402
Dec 11, 2014 11:30
Vote:
 

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).

#114522
Dec 15, 2014 8:45
Vote:
 

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?

#114524
Dec 15, 2014 9:00
Vote:
 

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)

#114525
Dec 15, 2014 9:13
Vote:
 

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 :)

#114526
Dec 15, 2014 9:19
Vote:
 

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>
#114537
Dec 15, 2014 11:08
Vote:
 

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>
#179491
Jun 13, 2017 23:56
This thread is locked and should be used for reference only. Please use the Episerver CMS 7 and earlier versions forum to open new discussions.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.