Question

How can I add an index, to input names and ids in forms that are used multiple times in one view?

I have created a form for photo rotator that provides the ability to edit a context-specific caption for each photo (billboard). I need to be able to include multiple instances of the form fields for this so the admins can edit all of the captions for a rotator's set of photos in one view, and so I need a way to keep ids and field names unique.

Editor templates automatically add a prefix, but when I loop over the photos like this:

<% foreach (var billboard in Model.Billboards ) {  %>
    <%: Html.EditorFor(x => billboard, "BillboardForm")%>
<% } %>

It simply adds "billboard_" as the prefix, which doesn't solve my problem.

I'd like to append the rotator id billboard id to the end of each input name and id:

<form action="/Rotators/Edit/5" method="post">                  
    <input id="billboard_21_RotatorId" name="billboard_21_RotatorId" type="hidden" value="5" /> 
    <input id="billboard_21_ImageId" name="billboard_21_ImageId" type="hidden" value="19" />
    <label for="billboard_21_Title">Title</label>
    <textarea cols="20" id="billboard_21_Title" name="billboard_21_Title" rows="2">Title</textarea>
    <label for="billboard_21_Caption">Caption</label>
    <textarea cols="20" id="billboard_21_Caption" name="billboard_21_Caption" rows="2">This is the caption</textarea>
    <select id="billboard_21_TopicId" name="billboard_21_TopicId">
        <option value="1">1st option</option>                   
    </select>
</form>

Any easy way to do this??

Was it helpful?

Solution

plz download this sampel code from steve sanderson's blog post. it does not directly relate to your question. But in demo project you will find BeginCollectionItem html helper that is changing the prefix scope for input or series of inputs. This can give u a starting point
Edit:
in ur editor template u can use following method from steve's code like

using(Html.BeginHtmlFieldPrefixScope("BillBoard" + Model.ID.ToString())){
<label>Image<label>
@Html.TextBoxFor(x=>x.Image)
<label>Caption</label>
@Html.TextBoxFor(x=>x.Caption)
}

if ID is property of your model and has value e.g 4 then u will have html like

<label>Image</label>
<input name = "BillBoard4.Image" .../>
<label>Caption</label>
<input name = "BillBoard4.Caption" .../>

OTHER TIPS

Note: The features used below may not have existed 4 years ago... Firstly, you don't have to use beestings any more, the @ syntax used in the Razor examples is far cleaner.

The method you're calling is in System.Web.Mvc.Html.EditorExtensions:

public static MvcHtmlString EditorFor<TModel, TValue>(
    this HtmlHelper<TModel> html,
    Expression<Func<TModel, TValue>> expression
)
...

Your approach:

@foreach (var billboard in Model.Billboards ) {
   @Html.EditorFor(x => billboard, "BillboardForm")
}

The body of expression x => billboard is a ConstantExpression.

This approach results in the appropriate scope applying in the EditorTemplate:

@for (var i = 0; i < Model.BillBoards.Count(); i++)
{
    @Html.EditorFor(x => Model.BillBoards[i], "BillboardForm")
}

If Model.BillBoards is an array, the expression x => Model.BillBoards[i] can be described as

SimpleBinaryExpression{NodeType:ArrayIndex}(
    Left: ConstantExpression,
    Right: ConstantExpression
)

If Model.BillBoards is an IList<T>, the expression x => Model.BillBoards[i] can be described as

InstanceMethodCallExpressionN(
    Method:RuntimeMethodInfo(IList<T>.get_Item (Int32 index)),
    Object:PropertyExpression(ConstantExpression),
    Arguments:[ConstantExpression]
)

The overloads of EditorFor() that accept Expressions check the expression body's Type and NodeType and construct the scope accordingly.

This code should be equivalent if you don't have anything else inside the loop:

@Html.EditorFor(x => Model.BillBoards, "BillboardForm")

If you only have a read-only view and an editing view, you can rename your templates and remove the second parameter. Assuming BillBoard is your Model class, rename BillboardForm.cshtml to EditorTemplates/BillBoard.cshtml, then change the code to

@Html.EditorFor(x => Model.BillBoards)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top