-1

I have a view model like:

public class OrganisationViewModel
{
    public string OrganisationName { get; set; }
    public List<BranchViewModel> Branches { get; set; }
    // more properties...
}

public class BranchViewModel
{
    public string BranchName { get; set; }
    // more properties...
}

This is how the Organisation page looks like:

enter image description here

What I want to achieve is to allow user to update a single BranchViewModel, so I have created a Modal for each Branch and when user click on the Edit branch link, the modal will open:

@for (int i = 0; i < Model.BranchViewModels.Count(); i++)
{
    var branchModalId = OrganisationHelper.GetBranchModalId(Model.BranchViewModels[i].BranchId);

    <div class="modal fade" id="@branchModalId" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <form action="/organisation/updateBranch" method="post" role="form">
                    <div class="modal-body">
                        @Html.AntiForgeryToken()
                        <div class="form">
                            <div class="form-group">
                                @Html.LabelFor(m => Model.BranchViewModels[i].BranchName, htmlAttributes: new { @class = "", @maxlength = GlobalConstants.MaxLengthForLongName })
                                @Html.TextBoxFor(m => Model.BranchViewModels[i].BranchName, new { @class = "form-control input-text" })
                                @Html.ValidationMessageFor(m => Model.BranchViewModels[i].BranchName, "", new { @class = "text-danger" })
                            </div>

                            @*more properties...*@
                        </div>

                    </div>
                    <div class="modal-footer">
                        <input type="button" value="Cancel" class="btn btn-secondary-grey" data-dismiss="modal" />
                        <input type="submit" class="btn btn-primary-action" value="Save" />
                    </div>
                </form>
            </div>
        </div>
    </div>
}

enter image description here

Now the problem is, since branches belong to an array the inputs on the page are generated as an array, so something like this:

<input class="form-control input-text" data-val="true" id="BranchViewModels_0__BranchName" name="BranchViewModels[0].BranchName" readonly="readonly" type="text" value="35671900246">

So when I submit the changes to the branch, the values are passed to the controller as List, so this controller will accept a List of one Branch:

public ActionResult UpdateBranch(List<BranchViewModel> branchViewModel)
{
}

What I want to achieve is to be able to pass the single branch to the controller, so I want the signature of the controller to be like:

public ActionResult UpdateBranch(BranchViewModel branchViewModel)
{
}

But I need the branch to be rendered as an array in the HTML, otherwise I will get duplicated input Ids... what is the best way to achieve this?

2 Answers 2

0

Can you try instead of using the static @HTML.LabelFor and @HTML.TextBoxFor, you could write the html manually with <input> tags and specify the name of the input field. This way, it won't have an index.

<input name="BranchViewModel.BranchId" type="hidden" value="Model.BranchViewModels[i].BranchId" />
<input name="BranchViewModel.BranchName" max="@GlobalConstants.MaxLenghForName" value="Model.BranchViewModels[i].BranchName" />

Full modal code;

@for (int i = 0; i < Model.BranchViewModels.Count(); i++)
{
    var branchModalId = OrganisationHelper.GetBranchModalId(Model.BranchViewModels[i].BranchId);

    <div class="modal fade" id="@branchModalId" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <form action="/organisation/updateBranch" method="post" role="form">
                    <div class="modal-body">
                        @Html.AntiForgeryToken()
                        <div class="form">
                            <div class="form-group">
                                <label>Model.BranchViewModels[i].BranchName</label>
                                <input name="BranchViewModel.BranchId" type="hidden"value="Model.BranchViewModels[i].BranchId" />
                                <input name="BranchViewModel.BranchName" max="@GlobalConstants.MaxLenghForName" value="Model.BranchViewModels[i].BranchName" />
                            </div>

                            @*more properties...*@
                        </div>

                    </div>
                    <div class="modal-footer">
                        <input type="button" value="Cancel" class="btn btn-secondary-grey" data-dismiss="modal" />
                        <input type="submit" class="btn btn-primary-action" value="Save" />
                    </div>
                </form>
            </div>
        </div>
    </div>
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that's a good suggestion... The problem is I have quite a few properties with their validation defined in the ViewModel and using Html helpers is really useful, so I would prefer to keep them...
0

ASP.NET MVC serialization works on html element names. Indexers will have html element names with _i__ (where i is index of branch in that collection) when you create them via for loop. So, instead of using indexers (accessing Model.BranchViewModels[i] via for loop), you can try using foreach.

@{
    var idx = 0;
}
@foreach (var branch in Model.BranchViewModels)
{
    var branchModalId = OrganisationHelper.GetBranchModalId(branch.BranchId);

    <div class="modal fade" id="@branchModalId" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <form action="/organisation/updateBranch" method="post" role="form">
                    <div class="modal-body">
                        @Html.AntiForgeryToken()
                        <div class="form">
                            <div class="form-group">
                                @Html.LabelFor(m => branch.BranchName, htmlAttributes: new { @class = "", @maxlength = GlobalConstants.MaxLengthForLongName })
                                @Html.TextBoxFor(m => branch.BranchName, new { @class = "form-control input-text", @id= Model.BranchViewModels[idx].BranchName})
                                @Html.ValidationMessageFor(m => branch.BranchName, "", new { @class = "text-danger" })
                            </div>

                            @*more properties...*@
                        </div>

                    </div>
                    <div class="modal-footer">
                        <input type="button" value="Cancel" class="btn btn-secondary-grey" data-dismiss="modal" />
                        <input type="submit" class="btn btn-primary-action" value="Save" />
                    </div>
                </form>
            </div>
        </div>
    </div>
    idx++;
}

4 Comments

Thanks for this, but this would generate invalid HTML, because I would have duplicate ids.
did you mean duplicate html element ids? if so, you can set html attribute id explicitly the way how you are setting class.
Yes, the input elements ids, would be duplicated. I have stated this in the last paragraph of the question.
Sorry, I missed that part. But you can set the id like this: define a counter outside the for loop @{ var idx = 0; } and then in the foreach loop, use it like @Html.TextBoxFor(m => branch.BranchName, new { @class = "form-control input-text", @id= Model.BranchViewModels[idx].BranchName}) . Also don't forget to increment idx in the foreach loop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.