0

I've a user profile form having basic Name, Contacts fields with an option of Attachments (Images/ Files etc.). This form is bind with a custome viewModel that is working perfectly for all fields except input type=file

Here is my html code;

<div class="form-group">
                    <label>First Name*</label>

                    <input asp-for="systemUser.FirstName" class="form-control" />
                </div>

                <div class="form-group">
                    <label>Middle Name*</label>
                    <input asp-for="systemUser.MiddleName" class="form-control" />
                </div>

                <div class="form-group">
                    <label>Last Name*</label>
                    <input asp-for="systemUser.LastName" class="form-control" />
                </div>

   <div class="panel panel-default">
            <div class="panel-heading">
                <i class="fa fa-edit"></i>
                <b class="panel-title"> Contact Info. </b>
            </div>
            <div class="panel-body">
                <div class="form-group">
                    <label>Description</label>
                    <input asp-for="userContactDetail.ContactDescription" class="form-control" />
                </div>

                <div class="form-group">
                    <label>Type</label>
                    <select class="form-control" asp-for="userContactDetail.ContactTypeId" asp-items="@(new SelectList(Model.contactTypes, "Id", "Name"))"><option value="-1"></option></select>
                </div>
                <div class="form-group">
                    <label>Contact Number</label>
                    <input asp-for="userContactDetail.ContactNumber" class="form-control" />
                </div>
                <div class="form-group">
                    <label>Extension</label>
                    <input asp-for="userContactDetail.ContactExtension" class="form-control" />
                </div>
                <div class="row">
                    <label class="col-sm-6 form-group">
                        <input type="checkbox" asp-for="userContactDetail.PrimaryContact" />
                        Primary
                    </label>
                    <label class="col-sm-6 form-group">
                        <input type="checkbox" asp-for="userContactDetail.PrivateContact" />
                        Private
                    </label>
                </div>
                <div class="form-group">

                </div>
                <div class="form-group pull-right">
                    <button id="btnAddContact" class="btn btn-sm btn-primary"><i class="fa fa-dot-circle-o"></i> Add</button>
                    <button id="btnCancelContact" class="btn btn-sm btn-danger"><i class="fa fa-ban"></i> Cancel</button>
                </div>
            </div>
            <div class="form-group panel-body">
                <div class="table-responsive">
                    <table class="table table-striped table-bordered table-hover" id="tblContacts" name="tblContacts">
                        <thead class="bg-primary">
                            <tr>
                                <th>Description</th>
                                <th>Type</th>
                                <th>Contact Number</th>
                                <th>Extension</th>
                                <th>Primary</th>
                                <th>Private</th>
                                <th></th>
                                <th hidden></th>
                            </tr>
                        </thead>
                        <tbody>


                            @for (int i = 0; i < Model.lstContacts.Count; i++)
                            {
                                <tr>
                                    <td hidden>@Html.HiddenFor(con => Model.lstContacts[i].UserContactDetailId)</td>
                                    <td hidden>@Html.HiddenFor(con => Model.lstContacts[i].ContactDescription)</td>
                                    <td>@Html.DisplayFor(con => Model.lstContacts[i].ContactDescription)</td>
                                    <td hidden> @Html.HiddenFor(con => Model.lstContacts[i].ContactTypeId) </td>
                                    <td> @Html.DisplayFor(con => Model.lstContacts[i].ContactTypeName) </td>
                                    <td hidden> @Html.HiddenFor(con => Model.lstContacts[i].ContactNumber)</td>
                                    <td> @Html.DisplayFor(con => Model.lstContacts[i].ContactNumber)</td>

                                    <td hidden> @Html.HiddenFor(con => Model.lstContacts[i].ContactExtension) </td>
                                    <td> @Html.DisplayFor(con => Model.lstContacts[i].ContactExtension) </td>
                                    <td hidden> @Html.HiddenFor(con => Model.lstContacts[i].PrimaryContact)</td>
                                    <td> @Html.DisplayFor(con => Model.lstContacts[i].PrimaryContact)</td>
                                    <td hidden> @Html.HiddenFor(con => Model.lstContacts[i].PrivateContact)</td>
                                    <td> @Html.DisplayFor(con => Model.lstContacts[i].PrivateContact)</td>

                                    <td>
                                        <a onclick="EditUserContact(this)" class="btn-sm btn-primary" style="margin:2px;">
                                            <i class="fa fa-folder-open"></i> Edit
                                        </a>
                                        <a onclick="DeleteUserContact(this)" class="btn-sm btn-danger" style="margin:2px;">
                                            <i class="fa fa-ban"></i> Delete
                                        </a>
                                    </td>
                                    <td hidden>@Html.HiddenFor(con => Model.lstContacts[i].IsDeleted)</td>
                                </tr>

                            }
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

 <div class="panel panel-default">
                <div class="panel-heading">
                    <i class="fa fa-edit"></i>
                    <b class="panel-title"> Attachment </b>
                </div>
                <div class="panel-body">
                        <input type="file" name="file" asp-for="lstAttachments" accept="application/pdf,application" multiple/>

                </div>
            </div>

 <button type="submit" id="btnUpdateProfile" class="btn btn-success">Update Profile</button>

this is how I'm submitting my form

$('#btnUpdateProfile').click(function (e) {
    e.preventDefault();
var formData = $("#formUpdateProfile").serialize();

$.ajax({
        url: "/Admin/UpdateProfile",
        type: "POST",
        data: formData,
        processData : false,
        contentType: false,//'application/x-www-form-urlencoded; charset=utf-8',
        dataType: "json",
        success: function (data) {
            toastr.clear();

            if (data.status == "Success")
            {
                toastr.success("User updated sucessfully.");
                return;

            }
            else
            {
                toastr.error(data.message);
                return;

            }

        },
        error: function () {
            toastr.clear();
            toastr.error("Something went wrong. Please try again.");
            return;
        }
    });

When I submit my form, I always get lstAttachments empty in my controller while other data is perfectly correct. I've tried many solutions like

Added a for loop and changed .serialize() to .serializeArray() for object reference in a .each() to append to the FormData. but it didn't work for me. Then i tried this way;

var fd = new FormData();

var file_data = object.get(0).files[i]; //get file data
var other_data = $('form').serialize();  // get form data

fd.append("file", file_data);

but again, no help to resolve my issue... Can someone explain what I'm missing? Any Kind of help will be appreciated.

Update

This is my form tag

<form asp-action="UpdateProfile" method="post" id="formUpdateProfile" enctype="multipart/form-data">
Community
  • 1
  • 1
Naila Akbar
  • 3,033
  • 4
  • 34
  • 76
  • 1
    You need to use `FormData` - refer [how to append whole set of model to formdata and obtain it in MVC](http://stackoverflow.com/questions/29293637/how-to-append-whole-set-of-model-to-formdata-and-obtain-it-in-mvc/29293681#29293681) - `var formdata = new FormData($('#formUpdateProfile').get(0));` –  Apr 20 '17 at 18:20
  • I've also tried with FormData but it does not pass any single value on submit – Naila Akbar Apr 20 '17 at 18:24
  • Read the dupe ! –  Apr 20 '17 at 18:26
  • My bad. I was missing `#` sign with form name. but problem is still there. I'm getting all data except `lstAttachments`. Its always null. – Naila Akbar Apr 20 '17 at 18:31
  • 1
    Then what is property `lstAttachments` and why have you given it a different name attribute? (and why in the world are you generating all those hidden inputs for properties of `lstContacts`) –  Apr 20 '17 at 18:36
  • GOD. you got it.. issue was different attribute name and it took me almost a day to find it -_- Thanks a bunch man – Naila Akbar Apr 20 '17 at 18:41
  • and all hidden fields in `lstContacts` are values (updated or not) to send back to controller. if I dont use `hidden-for`, I couldn't get them in controller. – Naila Akbar Apr 20 '17 at 18:44

1 Answers1

1

You need to use FormData, but each individual name/value needs to be added to FormData. You can simply use

var formdata = new FormData($('#formUpdateProfile').get(0));

which will serialize all your form controls, including the file input, and then you ajax call is

$.ajax({
    url: "/Admin/UpdateProfile", // recommend you use '@Url.Action("UpdateProfile", "Admin")'
    type: "POST",
    data: formData,
    processData : false,
    contentType: false,
    dataType: "json",
    success: function (data) {

However, your giving your file input a name attribute which does not relate to your model property. Change the html to remove the name="file" attribute so that the correct attribute (name="lstAttachments") is generated for model binding

<input type="file" asp-for="lstAttachments" accept="application/pdf,application" multiple/>