Home > Back-end >  Serializing Form Subgroups for Ajax Submission to ASP.NET MVC
Serializing Form Subgroups for Ajax Submission to ASP.NET MVC

Time:11-17

I am creating an application where a note can be created and one to many parts can be added to the note (the application is for a tractor salvage yard where customers call for tractor parts).

I am using Jquery AJAX to load a popup modal with the create view. I am creating a note with its parts in one create view. My problem is I don't see a way to serialize the form data from the create view so that the controller post action can update both classes. Everything previously worked when I had a separate page and url for the create view. I included a screenshot of the create view.

Create View

Here are my model classes:

public class Note
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
    public string CustomerPhone { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateUpdated { get; set; }
    public string CreatedBy { get; set; }
    public string AssignedTo { get; set; }
    public virtual ICollection<Part> Parts { get; set; }
}

public class Part
{
     public int PartID { get; set; }
     public string PartNumber { get; set; }
     public string Description { get; set; }
     public int NoteID { get; set; }
     public virtual Note Note { get; set; }
}

My post method:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Note note)
{
        if (ModelState.IsValid)
        {
            db.Notes.Add(note);
            db.SaveChanges();
            return Json(new { success = true });
        }           

        return PartialView("CreateDialog", note);
}

I need the note parameter to contain the data from the view. When I had a separate page for the create view, it was correctly passing all of the form data including the Parts to the note parameter when submitted.

This first one shows how the Parts property should be populated with however many parts added in the create view:

Correct Data

But when I try to use jQuery to serialize and submit this via ajax, I only get this. Even though the Parts input field was populated:

Missing Part Data

And finally here is my view and javascript. Please explain to me how I can either modify the html so serialize will also get the parts or maybe an alternative to serialize that I can manually attach the data to each property.

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

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

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

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

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

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

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


@*I am using js so the user can add part editors to this list. I have a part editor template (below) that this uses.*@ 
        <div class="parts-container create">         
             @Html.EditorFor(model => Model.Parts[p])            
        </div>

        <div id="btnWrapper">
            <input id="btnAddPart" type="button" value="Add Part" />
        </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>

Part Editor Template

@model CallNote.Models.Part
<div id="part-template" class="part-info">
    @Html.EditorFor(model => model.PartNumber, new { htmlAttributes = new { @class = "part-number" } })
    @Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "description" } })
    <input type="button" value="Delete" class="btn btn-default delete-button" />
    <br />
</div>
$.ajaxSetup({ cache: false });
    $(".create-button").click(function () {
        $(".ajax-content").load("/note/create", function (e) {
            $(".modal").modal({
                keyboard: true
            }, "show");
            bindForm(this);
        });
    });

    function bindForm(dialog) {
        $("form", dialog).submit(function () {
            $.ajax({
                url: this.action,
                type: this.method,
                data: $(this).serialize(),
                complete: function (xhr, status) {
                    if (status == "success") {
                        $(".modal").modal("hide");
                        location.reload();
                    } else {
                        alert("Failed: "   xhr.status   " "   xhr.statusText);
                    }
                }
            });
            return false;
        });
    }

CodePudding user response:

Problem in your scenario is that when you use MVC rendering for input object in loop it will render like this (if i remember it correctly)

<input type="text" name="Parts_PartNumber" value="part abc" />

its not going to serialize in a way that MVC controller will understand, for that you need

<input type="text" name="Parts[0].PartNumber" value="part abc" />
<input type="text" name="Parts[1].PartNumber" value="part xyz" />

what you can do when you dynamically create new input control for part number you also set its name attribute like name='Parts[0].PartNumber' Here is working example

public class CustomerViewModel
{
    public string CustomerName { get; set; }
    public IList<Part> Parts { get; set; }
}

public class Part
{
    public string PartName { get; set; }
}

Controller

[HttpPost]
public ActionResult Customer(CustomerViewModel customer)
{

    return Json(customer, JsonRequestBehavior.AllowGet);
}

View

<form action="/Home/Customer" method="POST">

    <input name="CustomerName" value="abc" /> <br />
    <input name="Parts[0].PartName" value="xyz" />

    <button id="submitForm" type="submit">Submit</button>

</form>



@section scripts {

    <script type="text/javascript">

    $('form').submit(function () {

        console.log('click', this);


        var options = {
            url: '/Home/Customer',
            type: 'POST',
            data: $(this).serialize()
        }


        $.ajax(options)
        .done(function (response) {
            console.log('response: ', response);
        });

        return false;
    });


    </script>

Or an other approach could be js object as we discussed in comments in that case it should look like this

data: { customerName: 'name', parts: [ {partName: 'part 1'},  {partName: 'part 2'}]}

you can also loop through input objects in html and create json or convert serialized form into a valid json.

Please note that it could be not an ideal approach but this is how I can help you solving the issue.

  • Related