Boostrap Modal, browse for photo and submit via ajax to controller with [FromForm] IFormFile file - ajax

I want to create a reusable upload feature for images and have created a partialView for this. On click, the partial is loaded via ajax call and the user can browse for an image, the chosen image is displayed and on submit the file should be passed to the controller but the url for submission is always a 404.
Load the Bootstrap Modal
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="ajax-modal" data-target="#add-contact" data-url="#Url.Action("ShowModalUploadForm", new { Id= Model.Id })">
Add contact
</button>
var placeholderElement = $('#modal-placeholder');
$(document).on('click', 'button[data-toggle="ajax-modal"]', function (event) {
var url = $(this).data('url');
$.get(url).done(function (data) {
placeholderElement.html(data);
placeholderElement.find('.modal').modal('show');
});
});
This works...The issue is when I submit, the URL that is fored appears to not match what the controller is expecting
PartialView (omitted unnecessary portions)
<form id="UploadPhoto" asp-action="UploadPhoto" enctype="multipart/form-data" method="post">
<input name="VolunteerId" id="VolunteerId" type="hidden" value="#Model" />
<div class="col-md-12">
<div class="form-group">
<label>Upload Image</label>
<div class="input-group">
<span class="input-group-btn">
<span class="btn btn-default btn-file">
Browse… <input type="file" id="imgInp">
</span>
</span>
<input type="text" class="form-control" readonly>
</div>
<img id='img-upload' name='img-upload' />
</div>
</div>
</form>
$(document).on('click', '#btnSubmitUpload', function (event) {
event.preventDefault();
var form = $(this).parents('.modal').find('form');
var dataToSend = new FormData(form.get(0));
$.ajax({
url: '#Url.Action("UploadPhoto", "Volunteer", new { Area = "Admin" })',
method: 'post',
data: dataToSend,
processData: false,
contentType: false
}).done(function (data) {
//Do stuff here
}
});
});
$(document).on('change', '.btn-file :file', function () {
var input = $(this),
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [label]);
});
$(document).on('fileselect', '.btn-file :file', function (event, label) {
var input = $(this).parents('.input-group').find(':text'),
log = label;
if (input.length) {
input.val(log);
} else {
if (log) alert(log);
}
});
$(document).on('change', '#imgInp', function () {
readURL(this);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#img-upload').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
VolunteerController
[HttpPost]
private async void UploadPhoto([FromForm] IFormFile file)
{
//await _storage.SaveBlobAsync("volunteers", file, BlobType.Image);
}

Open the F12 developer tools, where is your misalignment? Or we can also use the FormData object.
View:
#model ImageFileUpload.Models.Human
#{
ViewBag.Title = "SaveData";
}
<h2>SaveData</h2>
<div>
Create New
<br /><br />
<table class="table table-responsive">
<thead>
<tr style="background-color:#333;color:white;">
<th>Human Name</th>
<th>Human Image</th>
<th>Human Phone</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody id="SetHumanList">
<tr id="LoadingStatus"></tr>
</tbody>
</table>
</div>
#using (Html.BeginForm("SaveData", "Human", FormMethod.Post, new { id = "form", enctype = "multipart/form-data" }))
{
<div class="modal fade" id="MyModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
×
<h4 id="ModalTitle"></h4>
</div>
<div class="modal-body">
<fieldset id="SubmitForm">
#Html.HiddenFor(a => a.HumanId, new { #id = "HumId" })
<div class="form-group">
#Html.TextBoxFor(a => a.HumanName, new { #id = "HumName", #class = "form-control", #placeholder = "Name" })
</div>
<div class="form-group">
<input type="file" id="UploadFile" name="Upload" class="form-control w" />
</div>
<div class="form-group">
#Html.TextBoxFor(a => a.HumanPhone, new { #id = "HumPhone", #class = "form-control", #placeholder = "Phone" })
</div>
<div class="form-group">
<button id="SaveRecord" type="button" class="btn btn-warning">Create</button>
</div>
</fieldset>
</div>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
<script src="~/Scripts/jquery.form.min.js"></script>
#Scripts.Render("~/bundles/jqueryval")
<script>
function AddNewHuman() {
$("#MyModal").modal("show")
}
$("#SaveRecord").click(function () {
var formData = new FormData();
formData.append("Upload", $('#UploadFile')[0].files[0]); //append the image file
var other_data = $('form').serializeArray();
$.each(other_data, function (key, input) { //append other input value
formData.append(input.name, input.value);
});
$.ajax({
type: "POST",
url: "/Human/SaveData",
data: formData,
contentType: false, // Not to set any content header
processData: false, // Not to process data
success: function (result) {
alert("Success");
window.location.href = "/Human/index";
//$("#MyModal").modal("hide"); //this line is unnecessary because the user has been redirect
}
})
});
</script>
}
Result

Related

AJAX Post Request with Multiple Properties and Files ASP.NET Core

I am trying to pass an object with multiple properties including user selected files to ASP.NET Core Controller. The POST request passes all properties except the files. How to fix the request to include the files in object? See the code below.
ViewModel:
public class InquiryViewModel
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Upload Documents")]
public IEnumerable<IFormFile> Files { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(InquiryViewModel model)
{
try
{
//if (model.Files != null)
//{
// foreach(var file in model.Files)
// await _uploadFileService.PostFileAsync(file, "", 1);
//}
return Json(new { success = true });
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message });
}
}
View:
#model InquiryViewModel
#{
ViewData["Title"] = "New Inquiry";
var AllowedExtensions = new string[] { "jpeg", "txt", "jpg", "pdf", "docx", "doc", "csv", "xls", "xlsx", "zip", "png" };
}
<div class="text-center pt-5 bg-logo">
<form id="wizard" autocomplete="false">
#Html.AntiForgeryToken()
<div class="row">
<div class="col-md-12">
<div class="requiredNotice">* Required Field</div>
</div>
</div>
<div class="row">
<div class="form-group col-lg-12 col-md-12 col-sm-12">
<label asp-for="FirstName" class="form-label"></label>
<input asp-for="FirstName" class="form-control" />
</div>
<div class="form-group col-lg-12 col-md-12 col-sm-12">
<label asp-for="LastName" class="form-label"></label>
<input asp-for="LastName" class="form-control" />
</div>
</div>
<div class="row">
<div class="form-group col-lg-12 col-md-12 col-sm-12">
<label asp-for="Files" class="form-label"></label>
<kendo-upload drop-zone=".dropZoneElement" id="files" name="Files">
<async auto-upload="false" chunk-size="12582912" />
<validation allowed-extensions="#AllowedExtensions" max-file-size="12582912" />
</kendo-upload>
<div class="demo-hint">Maximum allowed file size is <strong>12MB</strong>.</div>
</div>
</div>
<input type="button" name="next" class="col-2 submit action-button" value="Submit" />
</form>
</div>
#section Scripts {
<script>
$('input.submit').on('click', function() {
var formData = $('#wizard').serializeArray();
// add files to form data
var upload = $("input[name=Files]").data("kendoUpload");
var files = upload.getFiles();
for (var i = 0; i < files.length; i++) {
formData.push({ name: "Files", value: files[i] });
}
$.ajax({
type: 'POST',
//cache: false,
//contentType: false,
//processData: false,
data: formData,
url: '/Home/Create',
success: function (result) {
if (result.success == true) {
// success
}
else if (result.success == false) {
console.log(result);
}
},
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr);
console.log(ajaxOptions);
console.log(thrownError);
}
});
});
</script>
}
As is the form submits only the first and last names no files. If I remove the comment from the cache = false, contentType = false, processData = false, it will not hit the controller at all.
Here is what I changed to make it work.
In the controller I added the [FromForm]
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([FromForm] InquiryViewModel model)
{
try
{
//if (model.Files != null)
//{
// foreach(var file in model.Files)
// await _uploadFileService.PostFileAsync(file, "", 1);
//}
return Json(new { success = true });
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message });
}
}
In the View I replaced kendo uploader with the out of the box html file input with the attribute multiple. In the AJAX I ended up constructing the FormData from scratch. Lastly, I used the contentType: false and processData: false
View
#model InquiryViewModel
#{
ViewData["Title"] = "New Inquiry";
}
<div class="text-center pt-5 bg-logo">
<form id="wizard" autocomplete="false">
#Html.AntiForgeryToken()
<div class="row">
<div class="col-md-12">
<div class="requiredNotice">* Required Field</div>
</div>
</div>
<div class="row">
<div class="form-group col-lg-12 col-md-12 col-sm-12">
<label asp-for="FirstName" class="form-label"></label>
<input asp-for="FirstName" class="form-control" />
</div>
<div class="form-group col-lg-12 col-md-12 col-sm-12">
<label asp-for="LastName" class="form-label"></label>
<input asp-for="LastName" class="form-control" />
</div>
</div>
<div class="row">
<div class="form-group col-lg-12 col-md-12 col-sm-12">
<label asp-for="Files" class="form-label"></label>
<input type="file" name="Files" id="files" class="form-control" multiple maxlength="12582912" />
</div>
</div>
<input type="button" name="next" class="col-2 submit action-button" value="Submit" />
</form>
</div>
#section Scripts {
<script>
$('input.submit').on('click', function() {
var formdata = new FormData();
var files = $("#files").get(0).files;
for (var i = 0; i < files.length; i++) {
formdata.append("Files", files[i]);
}
var otherData = wizard.serializeArray();
$.each(otherData, function (key, input) {
formdata.append(input.name, input.value);
});
$.ajax({
url: '/Home/Create',
type: "POST",
data: formdata,
contentType: false,
processData: false,
success: function (result) {
if (result.success == true) {
// success
}
else if (result.success == false) {
console.log(result);
}
},
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr);
console.log(ajaxOptions);
console.log(thrownError);
}
});
});
</script>
}

Bootstrap modal is disappearing after AJAX response

I use AJAX to send data inside a bootstrap modal to a controller in ASP.NET MVC. After its operation is completed inside this controller, a success or failure response is sent back to bootstrap modal using following code:
Controller:
[HttpPost]
public JsonResult Action([Bind(Include = "Id,firstName,lastName")] InqueryViewModel model)
{
JsonResult json = new JsonResult();
if (ModelState.IsValid)
{
Inquery inquery = new Inquery();
inquery.firstName = model.firstName;
inquery.lastName = model.lastName;
var result = SaveInqueries(inquery);
json.Data = new { Success = true };
}
else
{
json.Data = new { Success = false, Message = "Unable to perform an action on sending" };
}
return json;
}
As you notice above, code is dividing the logic into 2 types of responses, i.e., success or failure. Following code inside bootstrap model is used to handle this response:
JavaScript
<script>
$("#actionButton").click(function () {
$.ajax({
url: '#Url.Action("Action","Home")',
type: "post",
data: $("#actionForm").serialize()
})
.done(function (response) {
if (response.Success) {
//data is saved.
$(".alert-success").show();
$("#actionForm").hide();
}
else {
$(".errorDiv").html(response.Message);
}
});
});
</script>
Above code is working as expected if response is a success, but its not showing .errorDiv on a failure response. Instead, bootstrap modal is being disappeared. Following is bootstrap modal code:
HTML:
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Get Your Survey</h5>
<button type="button" class="close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="alert alert-success collapse">
<span>
Success
</span>
</div>
<form id="actionForm">
<div class="form-group">
#Html.TextBoxFor(m => m.firstName, new { placeholder = "First Name", #class = "form-control form-control-sm" })
</div>
<div class="form-group">
#Html.TextBoxFor(m => m.lastName, new { placeholder = "Last Name", #class = "form-control form-control-sm" })
</div>
<button id="actionButton" class="data-btn btn btn-outline-success" type="submit">
<i class='fa fa-paper-plane'></i>
Send
</button>
</form>
<div class="errorDiv">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
Can anyone see why this program is behaving like this?
Thank you.

Every row is executed twice

I have found a bug - and I haven't found any solution to this.
I have a code in ASP.NET Core (using VSPro 2019 16.5.0):
public IActionResult CreateSubGroup(MyClass model, string returnUrl = null)
{
if (ModelState.CreateMyClassValidation())
{
if (!db.MyClass.Where(x => x.Title == model.Title).Any())
{
ViewData["ReturnUrl"] = returnUrl;
var code = new MyClass { Title = model.Title, IdGroup = model.IdGroup, GroupCode = model.GroupCode};
db.MyClass.Add(code);
var result = db.SaveChanges();
if (result > 0)//if there was no issue (at least one row was changed)
{
this.AddNotification(MessagesHandler.Success, $"Item\"{model.Title}\" was successfully created.");
}
else
{
this.AddNotification(MessagesHandler.Error, $"Item \"{model.Title}\" cannot be created.");
}
}
else
{
this.AddNotification(MessagesHandler.Error, $"Item \"{model.Title}\" already exists.");
}
}
else
{
this.AddNotification(MessagesHandler.Error, $"ErrorMessage.");
}
return RedirectToLocal(returnUrl);
}
Creating of new Item always crashes with unique code exception from DB - During debuging I have found, that every row is executed twice (and I don't know why??) - so also the row db.SaveChanges() is executed twice and that's why I got this exception.
Second bad thing is, that not even the first attempt to save database is not executed (= new Item is not created in DB).
Have you seen this error?
EDIT:
I have found, that it happens only when data are posted from view with JS/AJAX (from modal window)
Here is the code for sending data:
<div class="modal fade" id="ModalWindow" tabindex="-1" role="dialog" aria-labelledby="ModalForm" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form id="ModalForm" action="" method="post" class="validator">
<div class="modal-body">
<div class="form-group">
<label asp-for="Id"></label>
<input class="form-control" asp-for="Id" value="" readonly data-val="false">
<div class="form-text text-muted small">ID cannot be changed!</div>
</div>
<div class="form-group">
<label asp-for="Title"></label>
<input class="form-control mlfb-create" asp-for="Title" placeholder="Title" value="" autofocus tabindex="#(++tabindex)">
<span class="text-danger small" asp-validation-for="Title"></span>
</div>
<div class="form-group">
<label asp-for="IdGroup"></label>
<select class="selectpicker form-control" asp-for="IdGroup" data-live-search="true" data-style="btn-info" tabindex="#(++tabindex)">
#if (data?.GroupData != null)
{
#foreach (var item in data?.GroupData)
{
<option value="#(item.Id)">#item.Title</option>
}
}
</select>
</div>
<div class="form-group">
<label asp-for="GroupCode"></label>
<input class="form-control mlfb-create" asp-for="GroupCode" placeholder="Title" value="" autofocus tabindex="#(++tabindex)">
<span class="text-danger small" asp-validation-for="GroupCode"></span>
</div>
</div>
<div class="text-center modal-footer">
<button type="submit" class="btn btn-success _modal-buttton-save" tabindex="#(++tabindex)"><i class="fas fa-check mr-2"></i><span>Save</span></button>
<button type="reset" class="btn btn-secondary" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
#section scripts {
<script>
$(function () {
"use strict";
$(document).on('click', '._createSubFormButton', function () {
$('#ModalWindow').modal('show');
$('.modal-title').text('Creating of subgroup');
$('.modal-buttton-save span').text('Create');
$('#ModalForm').attr('action', '/MyCode/CreateSubGroup/?returnurl=' + window.location.href);
});
// Edit form
$(document).on('click', 'tr ._editSubFormButton', function () {
$('#ModalWindow').modal('show');
var $tr = $(this).closest('tr');
var Id = $tr.find('._Id').text();
var Title = $tr.find('._Title').text();
var IdGroup = $tr.find('._IdGroup').text();
var GroupCode = $tr.find('._GroupCode').text();
$('.modal-title').text('Editing of subgroup');
$('#ModalForm').attr('action', '/MyCode/EditSubGroup/' + Id + '?returnurl=' + window.location.href);
$('#Id').val(Id);
$('#Title').val(Title);
$('#GroupCode').val(GroupCode);
});
// form validation reset during closing modal form
$('#ModalWindow').on('hidden.bs.modal', function () {
$(this).find('form').trigger('reset');
$('#IdGroup').load();
$('.form-group .is-invalid').each(function () { $(this).removeClass('is-invalid'); });
$('.form-group .is-valid').each(function () { $(this).removeClass('is-valid'); });
$('.form-text.text-danger').each(function () { $(this).removeClass('text-danger'); });
$('.form-text.text-success').each(function () { $(this).removeClass('text-success'); });
$('.invalid-feedback').each(function () { $(this).remove(); });
});
$(document).on('submit', '#ModalForm', function (e) {
var form = $('#ModalForm');
if (form.valid()) {
console.log(form.serializeArray());
$.ajax({
url: form.attr("action"),
type: form.attr("method"),
data: form.serializeArray()
}).done(function () {
console.log('done');
$tr.find('._Number').text();
var $tr = $(this).closest('tr');
})
.fail(function () {
console.log('fail');
});
$('#ModalWindow').modal('hide');
}
});
error I got:
Have you tried debugging this code? Debugging with setting breakpoints and stepping through the code would help you find what is wrong with this code.

Ajax call with .net MVC

I am trying to use Ajax to Submit a form. But when I do submit the page is reloaded and the url changes. I think that the url changes because of the #Html.AntiForgeryToken();
See my code bellow :
This is how my form looks like :
#model PersonModel
....
<form action="SubmitLead" class="new-lead">
#Html.AntiForgeryToken();
<div class="col-md-12">
<p>
<input type="hidden" value="#Model.TrackingCode" id="hdnTrackingCode" />
My name is #Html.TextBoxFor(model => model.FirstName,
new { #placeholder =
Html.DisplayNameFor(model => model.FirstName) })
#Html.TextBoxFor(model => model.Surname, new { #placeholder = Html.DisplayNameFor(model => model.Surname) })
</p>
</div>
<div class="clearfix"></div>
<div class="col-md-12 text-center">
<button type="submit" id="btnSubmit" class="orange-button">Get Quotes Now</button>
</div>
</form>
#if (Model.Results != null &&
Model.Results.IsSuccessful)
{
<div class="col-md-12 text-center">
<img src="~/Content/Images/Products/new-success.png" height="24px" />
<p id="result"></p>
</div>
}
Please see my script here :
#section Scripts{
<script type="text/javascript">
$(document).ready(function () {
$('.new-lead').submit(function (event) {
$.ajax({
url: '#Url.Action("Lead/SubmitLead")',
type: 'POST',
data: $(this).serialize(),
dataType: 'json',
success: function (result) {
var resultMessage = "success";
$('result').html(resultMessage);
}
})
})
})
</script>
Do this way
<form onsubmit="return submit(thi)" class="new-lead">
....
</form>
<script>
function submit(e){
$.ajax({
url: '#Url.Action("Lead/SubmitLead")',
type: 'POST',
data: $(e).serialize(),
dataType: 'json',
success: function (result) {
var resultMessage = "success";
$('result').html(resultMessage);
}
})
return false;
}
</script>

laravel 5.1 formData - AJAX - PHP- Uploading multiple files

I can not get the formData from the file upload
form.blade.php
<form method="POST" class="form-horizontal" enctype="multipart/form-data" id="modal-file-upload">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="folder" value="" id="modal-file-upload-folder">
<div class="modal-header">
<h4 class="modal-title">Upload New File</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="file" class="col-sm-3 control-label">
File
</label>
<div class="col-sm-8">
<input multiple="true" accept="image/*" required="true" id="fileToUpload" type="file" name="file[]">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="close_upload_view()">Cancel</button>
<button class="btn btn-primary" id="btn-upload">Upload File</button>
</div>
</form>
Controller
public function uploadMultiFile(Request $request) {
// getting all of the post data
$data = $request->input('formData');
var_dump($data); current return null
}
<script>
// Confirm file delete
function close_upload_view() {
$("#modal-file-upload").modal("hide");
return false;
}
$.ajaxSetup({
headers: { 'X-CSRF-Token' : $('meta[name=_token]').attr('content') }
});
$("#modal-file-upload").submit(function(e){
e.preventDefault();
//var formData = new FormData(document.getElementById('modal-file-upload')[0]);
var fd = new FormData();
var ins=document.getElementById('fileToUpload').files.length;
for(var x=0;x<ins;x++)
{
fd.append("fileToUpload[]", document.getElementById('fileToUpload').files[x]);
}
var folder = $("input#modal-file-upload-folder").val();
var token = $("input[name=_token]").val();
var dataString = 'formData='+fd+'&folder='+folder+'&token='+token;
$.ajax({
type: "POST",
url: '{!! url() !!}/admin/upload/multifile',
cache: false,
data : dataString,
processData: false, // Don't process the files
contentType: false, // Set content type to false as jQuery will tell the server it
dataType: 'JSON',
success : function(data){
console.log(data);
}
},"json");
});
</script>

Resources