How to get sequence/array index in Editor Template

2019-04-19 01:09发布

问题:

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?

回答1:

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


回答2:

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>


回答3:

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.



回答4:

How about:

@using System
@using System.Text.RegularExpressions

var i = Convert.ToInt32(Regex.Matches(
             ViewData.TemplateInfo.HtmlFieldPrefix,
             @"\[([0-9]+)?\]")[0].Groups[1].ToString());


回答5:

You can use @Html.NameFor(m => m.AnyField). That expression will output the full name property including the index. You could extract the index there...



回答6:

I think the easiest way is:

@Regex.Match(ViewData.TemplateInfo.HtmlFieldPrefix, @"(?!\[)\d+(?=\])")

Or as helper:

    public static string Index(this HtmlHelper html)
    {
        Match m = Regex.Match(html.ViewData.TemplateInfo.HtmlFieldPrefix, @"(?!\[)\d+(?=\])");
        return m.Success ? m.Value : null;
    }

Inspired by @Jona and @Ryan Penfold