Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get sequence/array index in Editor Template?

Case: I have a list of items of Class X displayed using Editor Template for Class X.

Problem: How can I get index of an item being processed on the inside of the Editor Template?

like image 297
Andrei Avatar asked Jun 19 '13 17:06

Andrei


4 Answers

I've been using this HtmlExtension that returns only the needed id of an iteration. It's basically a regex on ViewData.TemplateInfo.HtmlFieldPrefix that's capturing the last number.

public static class HtmlExtensions
    public static MvcHtmlString Index(this HtmlHelper html)
    {
        var prefix = html.ViewData.TemplateInfo.HtmlFieldPrefix;
        var m = Regex.Match(prefix, @".+\[(\d+)\]");
        if (m.Success && m.Groups.Count == 2) 
            return MvcHtmlString.Create(m.Groups[1].Value);
        return null;
    }
}

Can be used in an EditorFor-template like this:

@Html.Index()
like image 73
Jona Avatar answered Sep 20 '22 02:09

Jona


Use a for loop instead of for each and pass the indexer into the EditorFor extension; razor should handle the rest.

@for(var i = 0; i < Model.count(); i++)
{
    @Html.EditorFor(m => Model.ToArray()[i], new { index = i })
}

Update:

pass in the the index of the item using view data as show above.

In your editor template access the item via the ViewBag

<span> Item Index: @ViewBag.index </span>
like image 37
Jay Avatar answered Sep 23 '22 02:09

Jay


Using the EditorTemplate is the best solution when viewing models that contain a list of something.

In order to find the index for the sub-model being rendered you can use the property that Razor sets by default:

ViewData.TemplateInfo.HtmlFieldPrefix

Say, for example, you have the following view models:

public class ParagraphVM
{
    public int ParagraphId { get; set; }
    public List<LineVM> Lines { get; set; }
}

and

public class LineVM
{
    public int Id { get; set; }

    public string Text {get; set;}
}

and you want to be able to edit all the "LineVM" within a "ParagraphVM". Then you would use an Editor Template so you would create a view at the following folder (if it doesn't exist) with the same name as the sub-model Views/Shared/EditorTemplates/LineVM.cshtml:

@model MyProject.Web.MVC.ViewModels.Paragraphs.LineVM
@{
     //this will give you the List's element like Lines[index_number]
     var field = ViewData.TemplateInfo.HtmlFieldPrefix;
}
<div id="@field">
    @Html.EditorFor(l => l.Text)
</div>

Assuming you have a Controller's ActionResult that is returning a View and passing a ParagrapghVM viewmodel to a view, for example Views/Paragraph/_Paragraph.cshtml:

@model MyProject.Web.MVC.ViewModels.Paragraphs.ParagraphVM

@using (Html.BeginForm("Details", "Paragraphs", FormMethod.Post))
{
    @Html.EditorFor(p => p.Lines)
}

This view would render as many editors for the list Lines as items contains that list. So if, for example, the property list ParagraphVM.Lines contains 3 items it would render something like:

<div id="#Lines[0]">
   <input id="Lines_0__Text name="Lines[0].Text"/>
</div>
<div id="#Lines[1]">
   <input id="Lines_1__Text name="Lines[1].Text"/>
</div>
<div id="#Lines[2]">
   <input id="Lines_2__Text name="Lines[2].Text"/>
</div>

With that you can know exactly what position each items is within the list and for example use some javascript to create a carousel or whatever you want to do with it. But remember that to edit that list you don't really need to know the position as Razor takes care of it for you. If you post back the model ParagraphVM, the list Lines will have the values bound (if any) without any additional work.

like image 34
diegosasw Avatar answered Sep 22 '22 02:09

diegosasw


How about:

@using System
@using System.Text.RegularExpressions

var i = Convert.ToInt32(Regex.Matches(
             ViewData.TemplateInfo.HtmlFieldPrefix,
             @"\[([0-9]+)?\]")[0].Groups[1].ToString());
like image 26
Ryan Penfold Avatar answered Sep 24 '22 02:09

Ryan Penfold