Search code examples
javascriptasp.net-coremodel-view-controllerrazorhtml.beginform

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


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!


Solution

  • 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