How do I submit MVC razor beginform with dynamically created, readonly elements

504 Views Asked by At

I am trying to build a form that will dynamically create readonly inputs using javascript and pass those values along with model values into the controller, but my FormCollection in my controller method only contains the model values, not the input values. Any help on what I am doing wrong would be awesome. Here is my code:

Model.Courses contains int CourseId and string CourseName. I am contructing readonly inputs with the value of the CourseName to pass into the FormCollection to establish the order of the courses contained within each bootcamp.

View:

    <div class="container">
<div class="row">
    <div class="col-xs-12">
        <h2>Create Bootcamp</h2>

        @using (Html.BeginForm("Create", "LMSBootcamp", FormMethod.Post))
        {
            @Html.AntiForgeryToken()

            <div class="form-horizontal">
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })

                <div class="form-group">
                    @Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="row">
                    <div class="col-xs-6 w-50">
                        <h5>Course Order</h5>
                        <ol id="orderContainer">

                        </ol>
                    </div>
                    <div class="col-xs-6 w-50">
                        <h5>Available Courses</h5>
                        <ul>
                            @foreach (var course in Model.Courses.OrderBy(a=>a.CourseName))
                            {
                                <li id="@course.CourseId" onclick="Add_Course()" data-arg="@course.CourseId">@course.CourseName</li>
                            }
                        </ul>
                    </div>
                </div>
                


                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Create" class="btn btn-default" />
                    </div>
                </div>
            </div>
        }

        <div>
            @Html.ActionLink("Back to List", "Index")
        </div>
    </div>
</div>
<script type="text/javascript">
function Add_Course() {
    var courseName = event.target.innerHTML;
    var courseId = event.target.getAttribute('data-arg');

    //create the readonly input for the course desired
    var order = document.createElement("input");
    order.readOnly = true;
    order.setAttribute("type", "text");
    order.setAttribute("value", courseName);
    order.classList.add("form-control", "text-box", "single-line");

    //create li element
    var listElement = document.createElement("li");
    listElement.onclick = function Remove_Course() {
        var element = event.target
        element.remove();
    }
    //add input to list element
    listElement.appendChild(order);

    //add list element to container
    var orderContainer = document.getElementById("orderContainer");
    orderContainer.appendChild(listElement);
}

Controller:

 [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Create(FormCollection courseOrder)
    {
        
        return RedirectToAction("Index");
    }

Thank you!

2

There are 2 best solutions below

0
On BEST ANSWER

Here is a demo:

Models:

public class FormModel {
        public string Title { get; set; }
        public List<Course> Courses { get; set; }
    }
    public class Course {
        public int CourseId { get; set; }
        public string CourseName { get; set; }

    }

View(before form submit add name to each input added):

@model FormModel


        <div class="row">
            <div class="col-xs-12">
                <h2>Create Bootcamp</h2>

                @using (Html.BeginForm("Create", "LMSBootcamp", FormMethod.Post, new { id = "myForm" }))
                {
                    @Html.AntiForgeryToken()

                    <div class="form-horizontal">
                        <hr />
                        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

                        <div class="form-group">
                            @Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
                            <div class="col-md-10">
                                @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
                                @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
                            </div>
                        </div>

                        <div class="row">
                            <div class="col-xs-6 w-50">
                                <h5>Course Order</h5>
                                <ol id="orderContainer">
                                </ol>
                            </div>
                            <div class="col-xs-6 w-50">
                                <h5>Available Courses</h5>
                                <ul>
                                    @foreach (var course in Model.Courses.OrderBy(a => a.CourseName))
                                    {
                                        <li id="@course.CourseId" onclick="Add_Course()" data-arg="@course.CourseId">@course.CourseName</li>
                                    }
                                </ul>
                            </div>
                        </div>



                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <input type="submit" value="Create" class="btn btn-default" />
                            </div>
                        </div>
                    </div>
                }

                <div>
                    @Html.ActionLink("Back to List", "Index")
                </div>
            </div>
        </div>
        @section scripts{
            <script type="text/javascript">
                function Add_Course() {
                    var courseName = event.target.innerHTML;
                    var courseId = event.target.getAttribute('data-arg');

                    //create the readonly input for the course desired
                    var order = document.createElement("input");
                    order.readOnly = true;
                    order.setAttribute("type", "text");
                    order.setAttribute("value", courseName);
                    order.classList.add("form-control", "text-box", "single-line");

                    //create li element
                    var listElement = document.createElement("li");
                    listElement.onclick = function Remove_Course() {
                        var element = event.target
                        element.remove();
                    }
                    //add input to list element
                    listElement.appendChild(order);

                    //add list element to container
                    var orderContainer = document.getElementById("orderContainer");
                    orderContainer.appendChild(listElement);
                }
                //add name before form submit
                $('#myForm').submit(function () {
                    var i = 0;
                    $("#orderContainer li").each(function () {
                        $(this).find("input").attr("name", "courseName[" + i+"]");
                        i++;
                    })
                    // DO STUFF...
                    return true; // return false to cancel form action
                });
    </script>
}

Controller:

[HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create(FormCollection courseOrder)
        {

            return Ok();
        }

result: enter image description here

0
On

Before you submit make sure those dynamically added inputs have correct model names and you should be fine. So in you example it would be something similar to this:

<input type="text" name="courseName[0]" value="courseName 1"/>
<input type="text" name="courseName[1]" value="courseName 2"/>
<input type="text" name="courseName[3]" value="courseName 3"/>

And the model binder will automatically create a List of string with those 3 strings ("courseName 1","courseName 2","courseName 3") in them and assign it to the corresponding property, in this case value.