How can I send a viewmodel to controller through Ajax - javascript

The user is able to write text and upload a file.
Using Javascript, I create an object with the file and text as properties, then send it to the controller using Ajax.
Using debugging, it seems that the problem is in the Ajax part. Everything before that(file upload, object creation) seems to be fine.
Here is the Javascript/JQuery:
function AddPost() {
var fileInput = document.getElementById('file').files;
var textInput = document.getElementById('addPostText').value;
if (fileInput != null || textInput != null) {
file = null;
if (fileInput.length > 0) {
file = fileInput[length-1];
}
var AddPostViewModel =
{
"Text": textInput,
"File": file
};
$.ajax(
{
url: "#Url.Action('AddPost')",
type: "POST",
dataType: "json",
data: JSON.stringify(AddPostViewModel),
contentType: 'application/json; charset=utf-8'
}
);
}
}
Here is the method in the controller:
[HttpPost]
public void AddPost(AddPostViewModel viewModel)
{
}
And here is the object:
public class AddPostViewModel
{
[DataType(DataType.MultilineText)]
public string Text { get; set; }
public IFormFile File { get; set; }
}
The HTML:
<textarea id="addPostText"></textarea>
<div class="upload-button">
<div class="label">Add image</div>
<input id="file" type="file" size="1" />
</div>

You can't send the File inside a json. Also, you've a bug in line file = fileInput[length-1]; (length is wrong, should be 0 or fileInput.length-1.
Here is a working function. This function should be inside a Razor Page because it needs to build the form action url #Url.Action("AddPost").
function AddPost() {
var fileInput = document.getElementById('file').files;
var textInput = document.getElementById('addPostText').value;
if (fileInput != null || textInput != null) {
var file = null;
if (fileInput.length > 0) {
file = fileInput[0];
}
var form = new FormData();
form.append("Text", textInput);
form.append("File", file);
$.ajax(
{
url: '#Url.Action("AddPost")',
type: "POST",
data: form,
processData: false,
contentType: false
}
);
}
}
EDIT Here is a full demo using the tag helpers asp-action, asp-for and a small JS/jQuery code to intercept the form submission and use Ajax.
1: Controller.
public class FileController : Controller
{
public IActionResult AddPost()
{
return View();
}
[HttpPost]
public void AddPost(AddPostViewModel viewModel)
{
//process the viewModel here
}
}
2: View
#model AddPostViewModel
<form asp-action="AddPost" id="theForm">
<textarea asp-for="Text"></textarea>
<input asp-for="File" size="1" />
<button type="submit">Send</button>
</form>
#section Scripts{
<script>
$("#theForm").submit(function(event) {
event.preventDefault();
$.ajax(
{
url: $(this).prop("action"),
type: "POST",
data: new FormData(this),
processData: false,
contentType: false,
success: function() {
console.log("form sent!");
}
}
);
});
</script>
}

You cant stringify a file data and you cant send file as JSON. You should use FormData to send file with ajax request.
Try to send request as follow
function AddPost() {
var fileInput = document.getElementById('file').files;
var textInput = document.getElementById('addPostText').value;
if (fileInput != null || textInput != null) {
file = null;
if (fileInput.length > 0) {
file = fileInput[length-1];
}
var AddPostViewModel =
{
"Text": textInput,
"File": file
};
var formData = new FormData();
Object.getOwnPropertyNames(AddPostViewModel).forEach(function ( attr, index ) {
var value = AddPostViewModel[attr];
formData.append(attr, value)
});
$.ajax(
{
url: "#Url.Action('AddPost')",
type: "POST",
contentType: false,
processData: false,
data: formData,
}
);
}
}

Related

Trouble sending JavaScript mp3 file to asp.net generic handler via Ajax?

I have an Input FileUpload and a button as html elements
<input id='track' type='file' style='display: none' accept='.mp3' />
<input type="button" id="btnUpload" value="Upload" />
and has a javascript event handler which saves the uploaded file as a javascript variable.
document.getElementById("track").addEventListener("change",upload_track_audio_eventhandler, false);
var currentTrackAudioFile;
function upload_track_audio_eventhandler() {
var files = event.target.files;
currentTrackAudioFile = files[0];
}
and then there is another function which is suppose to upload the audio file (currentTrackAudioFile) to asp.net Generic Handler.
document.getElementById("btnUpload").addEventListener("click", butttonclick)
function butttonclick() {
$.ajax({
url: 'Handler1.ashx',
type: 'POST',
data: currentTrackAudioFile,
cache: false,
contentType: false,
processData: false,
success: function (file) {
alert("uploaded");
}
});
}
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.Files.Count > 0)
{
HttpPostedFile postedFile = context.Request.Files[0];
string folderPath = context.Server.MapPath("~/Uploads/");
string fileName = Path.GetFileName(postedFile.FileName);
postedFile.SaveAs(folderPath + fileName);
string json = new JavaScriptSerializer().Serialize(
new
{
name = fileName
});
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.ContentType = "text/json";
context.Response.Write(json);
context.Response.End();
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
But its not receiving the file. context.Request.Files.Count is 0.
What am i doing wrong ?
p.s. I don't want to send directly from the DOM ( i.e. data: new FormData($('form')[0]))

POST file list to controller in ajax post

I tried to upload documents but I couldnt pass to list my controllers parameter. My scenario is:
user click to "choose file" button and pick the files and press done
then my some functions get file list and pass to controller for save locally via POST merhod like below:
view side: (get file list)
function saveDocuments(documentList) {
if (documentList.length > 0)
{
var formList = new Array;
for (var i = 0; i < documentList.length; i++) {
var form = new FormData();
var file = documentList[i];
form.append('FormFile', file);
formList.push(form);
}
savePhysicalFile(formList);
}
}
view side: (post file list)
function savePhysicalFile(formData)
{
if (formData != null)
{
$.ajax({
url: "Installation/SavePhysicalPath",
type: 'POST',
dataType: "json",
contentType: "multipart/form-data",
data:formData,
processData: false,
contentType: false,
success: function (result) {
console.log("Success", result);
},
error: function (data) {
console.log(data);
}
});
}
}
In my controller side; the parameter "model" is always null. I couldnt pass view side list here. How can I figure out ?
controller side
public JsonResult SavePhysicalPath([FromForm] List<FileModel> model)
{
var savingRootPath = #"C:\MyDocuments";
//I'm doing save locally
return Json(savingRootPath);
}
model side
public class FileModel
{
public string Files { get; set; }
public IFormFile FormFile { get; set; }
}
From your code,you may pay attention to two things here:
1.For each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.For model you receive in backend is a List,you need give the name like:[index].FormFile or model[index].FormFile.
2.Your model has a IFormFile and your action receives a list model,if you only pass the IFormFile you need remove FromForm attribute and be sure do not have [ApiController].It is a known github issue and this has been moved to Next sprint planning milestone.
Here is a whole working demo:
View:
<input type="file" multiple onchange="saveDocuments(this.files)"/>
<div class="form-group">
<input type="button" value="Submit" id="submit" class="btn btn-primary" />
</div>
#section Scripts
{
<script>
function saveDocuments(documentList) {
if (documentList.length > 0) {
var form = new FormData();
for (var i = 0; i < documentList.length; i++) {
var file = documentList[i];
//change here
form.append('model['+i+'].FormFile', file);
}
savePhysicalFile(form);
}
}
function savePhysicalFile(formData) {
if (formData != null) {
$.ajax({
url: "/Installation/SavePhysicalPath",
type: 'POST',
dataType: "json",
contentType: "multipart/form-data",
data: formData,
processData: false,
contentType: false,
success: function (result) {
console.log("Success", result);
},
error: function (data) {
console.log(data);
}
});
}
}
</script>
}
Controller:
[HttpPost]
public JsonResult SavePhysicalPath(List<FileModel> model)
{
var savingRootPath = #"C:\MyDocuments";
//I'm doing save locally
return Json(savingRootPath);
}
Result:

FormData Values in Razor HTML page, getting in C# Method

I have following input element in razor view page
<input type="file" id="uploadFile" name="FileUpload" multiple="multiple" />
using following script I'm binding data to this ForData
$('#uploadFile').on('change', function()
{
var fd = new FormData();
var files = $('#uploadFile')[0].files;
for (var i = 0; i < files.length; i++)
{
if (files[i].size < 5242880)
{
fd.append("myFiles", files[i])
}
}
});
I'm trying to get these files in C# method like following
[HttpPost]
public ActionResult SomeAction(ModelClass model)
{
var attchaedfiles = System.Web.HttpContext.Current.Request.Files["myFiles"];
for (int i = 0; i < attchaedfiles.Count; i++)
{
if (!string.IsNullOrEmpty(attchaedfiles[i].FileName))
{
....
}
}
}
but here I'm getting folllowing errors
Operator '<' cannot be applied to operands of type 'int' and 'method
group'
Cannot apply indexing with [] to an expression of type
'System.Web.HttpPostedFile'
In your ajax script, make sure you include these:
$.ajax({
url: yourUrl,
type: 'POST',
// Needed for FormData submit
processData: false,
// Needed for FormData submit
contentType: false,
data: fd,
dataType: 'json',
success: function () {
// Success things
},
error: function () {
// Error things
},
complete: function () {
// Complete things
}
});
And that your form code includes enctype = "multipart/form-data":
#using (Html.BeginForm(null, null, FormMethod.Post, new { #action = YourAction, id = "your_id", enctype = "multipart/form-data" }))
Try the following approach that checks possible conditions regarding to file uploads:
Model:
public class ExperimentViewModel
{
public int Id { get; set; }
[DataType(DataType.Upload)]
public IEnumerable<HttpPostedFileBase> FileUpload { get; set; }
//other properties
}
Controller:
if (model.FileUpload != null)
{
if (model.FileUpload.Count() > 0)
{
foreach (var upload in model.FileUpload)
{
if (upload != null && upload.ContentLength > 0)
{
//your stuff
}
}
}
}
You can also look at my answer on How to display images in MVC.

Upload file using MVC Razor from Ajax - Beginner logic issue

I am trying to upload a file using AJAX using C#-Razor. When I submit by clicking on the button the controller method is not being executed. How can I solve this ?
My code is as follows:
View
<div class="form-group">
#Html.TextBoxFor(model => model.IMG, new { #class = "control-label col-md-12", type = "file", placeholder = "Industry", name = "files[]", id="FileUpload" })
#Html.LabelFor(model => model.IMG, new { #class = "col-md-12 " })
#Html.ValidationMessageFor(model => model.IMG)
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" value="Create" class="btn btn-default" id="UseShipAddr" />
</div>
</div>
AJAX
$('#UseShipAddr').click(function () {
var formData = new FormData();
var totalFiles = document.getElementById("FileUpload").files.length;
for (var i = 0; i < totalFiles; i++) {
var file = document.getElementById("FileUpload").files[i];
formData.append("IMG", file);
alert("h" + file);
}
formData.append("name", "James");
formData.append("age", "1");
$.ajax({
url: "/Post/New",
type: "POST",
data: formData,
cache: false,
async: true,
success: function (data) {
alert(data);
}
});
});
Controller
[HttpPost]
//[ValidateAntiForgeryToken]
public async Task<ActionResult> New([Bind(Include="age","name","IMG")] POST rec)
{
if (ModelState.IsValid)
{
db.REC.Add(rec);
await db.SaveChangesAsync();
return RedirectToAction("My", "Post");
}
return View(rec);
}
Send the extra parameters in a querystring. Here is the AJAX code:
$.ajax({
url: "/Post/New?name=James&age=1",
type: "POST",
data: formData,
cache: false,
async: true,
contentType: false, // Not to set any content header
processData: false, // Not to process data
success: function (data) {
alert(data);
}
});
And your controller should be similar to below:
public async Task<ActionResult> New(string name, int age)
{
try
{
foreach (string file in Request.Files)
{
var fileContent = Request.Files[file];
if (fileContent != null && fileContent.ContentLength > 0)
{
// get a stream
var stream = fileContent.InputStream;
// and optionally write the file to disk
var fileName = Path.GetFileName(file);
using (var fileStream = new MemoryStream())
{
stream.CopyTo(fileStream);
}
// File is in the memory stream, do whatever you need to do
}
}
}
catch (Exception)
{
// whatever you want to do
}
}
You are not specifying any form while creating the form in object in your form submit #UseShipAddr click event. please specify your form while creating object as:
var formData = new FormData($(#formID)[0]);
or create form constructor
var dataString = new FormData();
append file to the form
dataString.append("UploadedFile", selectedFile);
var form = $('#formID')[0];
var dataString = new FormData(form);
now send that string to the action in controller.
There are lots of problem in your request. First remove this then you'll be able to call that action of the controller.

Submitting multipart/form-data via Ajax breaks HttpPostedFileBase submission [duplicate]

How do I pass a whole set model object through formdata and convert it to model type in the controller?
Below is what I've tried!
JavaScript part:
model = {
EventFromDate: fromDate,
EventToDate: toDate,
ImageUrl: imgUrl,
HotNewsDesc: $("#txthtDescription").val().trim(),
};
formdata.append("model",model);
then pass it through AJAX, it will be a string, and if I check the value of Request.Form["model"] the result will be same, that is it will be received as string and value will be "[object object]"
Is there any way to pass model through formdata and receive it in the controller?
If your view is based on a model and you have generated the controls inside <form> tags, then you can serialize the model to FormData using
var formdata = new FormData($('form').get(0));
This will also include any files generated with <input type="file" name="myImage" .../>
and post it back using
$.ajax({
url: '#Url.Action("YourActionName", "YourControllerName")',
type: 'POST',
data: formdata,
processData: false,
contentType: false,
});
and in your controller
[HttpPost]
public ActionResult YourActionName(YourModelType model)
{
}
or (if your model does not include a property for HttpPostedFileBase)
[HttpPost]
public ActionResult YourActionName(YourModelType model, HttpPostedFileBase myImage)
{
}
If you want to add additional information that is not in the form, then you can append it using
formdata.append('someProperty', 'SomeValue');
If you want to send Form data using Ajax.This is the way to send
var formData = new FormData();
//File Upload
var totalFiles = document.getElementById("Iupload").files.length;
for (var i = 0; i < totalFiles; i++) {
var file = document.getElementById("Iupload").files[i];
formData.append("Document", file);
}
formData.append("NameCode", $('#SelecterID').val());
formData.append("AirLineCode", $('#SelecterID').val());
$.ajax({
url: "/Controller/ActionName",
type: "POST",
dataType: "JSON",
data: formData,
contentType: false,
processData: false,
success: function (result) {
}
})
Using Pure Javascript, considering you have
<form id="FileUploadForm">
<input id="textInput" type="text" />
<input id="fileInput" type="file" name="fileInput" multiple>
<input type="submit" value="Upload file" />
</form>
JS
document.getElementById('FileUploadForm').onsubmit = function () {
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('fileInput');
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//text value
formdata.append("textvalue",document.getElementById("textInput").value);
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Home/UploadFiles');
xhr.send(formdata); // se
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
//on success alert response
alert(xhr.responseText);
}
}
return false;
}
in your C# controller you can get values it as below
[HttpPost]
public ActionResult UploadFiles(YourModelType model, HttpPostedFileBase fileInput)
{
//save data in db
}
Reference : File Uploading using jQuery Ajax or Javascript in MVC
In view side ,if you are using ajax then,
$('#button_Id').on('click', function(){
var Datas=JSON.stringify($('form').serialize());
$.ajax({
type: "POST",
contentType: "application/x-www-form-urlencoded; charset=utf-8",
url: '#Url.Action("ActionName","ControllerName")',
data:Datas,
cache: false,
dataType: 'JSON',
async: true,
success: function (data) {
},
});
});
In Controller side,
[HttpPost]
public ActionResult ActionName(ModelName modelObj)
{
//Some code here
}

Categories

Resources