I have an array inside array every object has file object
Eg
[
{
file:fileObject,
Description:string
},
{
file:fileObject,
Description: string
}
]
How can i pass this array in formdata and also
the controller net core catch data from formdata
If you post data by ajax,you need firstly know the two things below:
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:
Model:
public class FileModels
{
public string Description { get; set; }
public IFormFile FormFile { get; set; }
}
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];
form.append('model['+i+'].FormFile', file);
}
savePhysicalFile(form);
}
}
function savePhysicalFile(formData) {
if (formData != null) {
$.ajax({
url: "/Home/Save",
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 void Save(List<FileModels> model)
{
//...
}
Result:
If you post data by form submit,here is a whole working demo:
#model FileModels
<form asp-action="Save" method="post" enctype="multipart/form-data">
#for (int i = 0; i < 3; i++)
{
<div>
<input type="text" asp-for="Description" name="[#i].Description" />
</div>
<div>
<input type="file" asp-for="FormFile" name="[#i].FormFile" />
</div>
}
<div class="form-group">
<input type="submit" value="Submit" id="submit" class="btn btn-primary" />
</div>
</form>
Controller:
[HttpPost]
public void Save(List<FileModels> model)
{
//...
}
Result2:
Related
When I submit Form in a Razor Page by click on Submit button, IFormFile send to OnPost action method properly. But if I submit the Form by JavaScript code, IFormFile is null in OnPost action method, other model members are not null.
Model
public string Applicant { get; set; }
public List<IFormFile> FormFiles { get; set; }
Form
<form method="post" name="ApplicantForm" enctype="multipart/form-data">
<input asp-for="#Model.ApplicantModel.Applicant" />
<input type="file" asp-for="#Model.ApplicantModel.FormFiles" multiple />
<button type="submit" id="btnSubmit">Submit</button>
<button type="button" onclick="doSomethingBeforeSubmit()">Do Something Before Submit</button>
</form>
JavaScript
function doSomethingBeforeSubmit() {
// Do something before submitting the form ...
.
.
document.forms["ApplicantForm"].submit();
// Or
document.getElementById("btnSubmit").click();
}
OnPost action method
[BindProperty]
public ApplicantModel ApplicantModel { get; set; }
public async Task<IActionResult> OnPostAsync()
{
ApplicantModel.Applicant // OK, Always ..
ApplicantModel.FormFiles // OK, if form submitted by click on Submit button.
ApplicantModel.FormFiles // Null, if form submitted by JavaScript code.
}
What is the right way to submit a form by JavaScript code in Razor Page with IFormFile in model?
.NET 6,
Visual Studio 2022,
ASP.NET Core Web App (Razor Pages)
If you want to pass files with js,you need to use FormData,here is a demo:
function doSomethingBeforeSubmit() {
var Form_Data = new FormData();
Form_Data.append('ApplicantModel.Applicant', $("#ApplicantModel_Applicant").val());
var files = $("#ApplicantModel_FormFiles").get(0).files;
for (var i = 0; i < files.length; i++) {
Form_Data.append('ApplicantModel.FormFiles', files[i]);
}
$.ajax({
type: "POST",
url: '',
headers: { "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val() },
data: Form_Data,
contentType: false,
processData: false,
success: function (data) {
},
error: function (result) {
}
})
}
Which model should I use to receive a js data, included a File object and a string, from client? Or which data type should I use for File object in asp.net Core server?
Use ViewModel to receive the file and string , and submit form directly to post these data . Note : add enctype="multipart/form-data" in Form when you want to send files
public class TestViewModel
{
public string Name { get; set; }
public IFormFile file { get; set; }
}
<div class="row">
<div class="col-md-4">
<form asp-action="UploadFile" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input asp-for="file" class="form-control" />
</div>
<div class="form-group">
<input type="submit" id="btnSubmit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
<span id="result"></span>
</div>
If you want to send form data by js , you could add id="fileUploadForm" and try the javascript like below
#section Scripts
{
<script type="text/javascript">
$(document).ready(function () {
$("#btnSubmit").click(function (event) {
//stop submit the form, we will post it manually.
event.preventDefault();
// Get form
var form = $('#fileUploadForm')[0];
// Create an FormData object
var data = new FormData(form);
// If you want to add an extra field for the FormData
data.append("CustomField", "This is some extra data, testing");
// disabled the submit button
$("#btnSubmit").prop("disabled", true);
$.ajax({
type: "POST",
//enctype: 'multipart/form-data',
url: "/Home/UploadFile",
data: data,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
success: function (data) {
$("#result").text(data);
console.log("SUCCESS : ", data);
$("#btnSubmit").prop("disabled", false);
},
error: function (e) {
$("#result").text(e.responseText);
console.log("ERROR : ", e);
$("#btnSubmit").prop("disabled", false);
}
});
});
});
</script>
}
Controller
[HttpPost]
public async Task<ActionResult> UploadFile(TestViewModel data)
{ }
Which model should I use to receive js data, including a File object and a string
ASP Net Core has the IFormFile container for this purpose. The data is held internally as a stream.
Your action signature for a string and a file would look like,
public IActionResult Action(IFormFile file, string name) { }
Note, the parameter names must match the form data names you use in javascript. I.e,
const fd = new FormData()
fd.append('file' , file)
fd.append('name', 'some string')
This documentation provides full usage examples.
I have a view where i dynamically render checkbox items with razor. User can make adjustments and then i need to send form back to controller. If i send only one item i.e. row, it works fine, but if i try to send all of them i always get null. How can i collect form data properly. This is what i have at the moment.
public void SendData(List<SomeClass> myData)
{
var data = myData; //always null
}
public class SomeClass
{
public int Id { get; set; }
public int RoleId { get; set; }
public bool R { get; set; }
public bool W { get; set; }
public bool E { get; set; }
public bool D { get; set; }
}
And view:
<script type="text/javascript">
$(document).ready(function () {
var test = $("#myForm").serialize();
console.log(test);
test = JSON.stringify({ 'myData': test });
console.log(test);
$.ajax({
contentType: 'application/json; charset=utf-8',
type: 'POST',
url: '/Home/SendData',
data: test,
success: function () {
},
failure: function (response) {
}
});
});
</script>
<form id="myForm">
<div class="row control_group">
#Html.Hidden("Id", 1)
#Html.Hidden("RoleId", 1)
#Html.CheckBox("R", false)
#Html.CheckBox("W", false)
#Html.CheckBox("E", false)
#Html.CheckBox("D", false)
</div>
<div class="row control_group">
#Html.Hidden("Id", 2)
#Html.Hidden("RoleId", 2)
#Html.CheckBox("R", true)
#Html.CheckBox("W", true)
#Html.CheckBox("E", true)
#Html.CheckBox("D", true)
</div>
</form>
EDIT:
This is how i render items with razor
foreach (SomeObject x in items)
{
var menuName = someStringFromDb;
var permissions = x.MainItem.FirstOrDefault();
<div class="row control_group">
<div class="col-sm-4">#menuName</div>
<input name="Id" type="hidden" value="#permissions.Id"/>
<input name="RoleId" type="hidden" value=#permissions.RoleId />
<div class="col-sm-2">
#Html.CheckBox("R", #permissions.Read)
</div>
<div class="col-sm-2">
#Html.CheckBox("W", #permissions.Write)
</div>
<div class="col-sm-2">
#Html.CheckBox("E", #permissions.Edit)
</div>
<div class="col-sm-2">
#Html.CheckBox("D", #permissions.Delete)
</div>
</div>
}
EDIT 2
Thank you for your answer #Avi Fatal it got me this far. Problem i am facing now is this. Checkbox elements rendered by razor have two inputs, one is hidden and other one is shown. When i collect form data i am always getting last input value (hidden one, that's always false) How can i get true value?
Current data sent to controller (everything is false):
{"ajaxData":[{"Id":"1","RoleId":"1","R":"false","W":"false","E":"false","D":"false"},{"Id":"2","RoleId":"2","R":"false","W":"false","E":"false","D":"false"}]}
Collecting data like this (found similar problem here on SO):
var ajaxData = $('.control_group').map(function (i, group) {
var data = {};
$(group).find(':input').each(function () {
data[this.name] = this.value;
});
return data;
}).get();
ajaxData = JSON.stringify({ 'ajaxData': ajaxData });
console.log(ajaxData);
EDIT 3
With only .serialize() i get null as input parameter on controller, with JSON.stringify i get Count = 0, also empty. What am i missing?
HTML:
#model List<WebApplication3.Controllers.HomeController.SomeClass>
<form id="myForm">
#for (int i = 0; i < Model.Count; i++)
{
<div>Element</div>
#Html.HiddenFor(m => m[i].Id)
#Html.HiddenFor(m => m[i].RoleId)
#Html.CheckBoxFor(m => m[i].R)
#Html.CheckBoxFor(m => m[i].W)
#Html.CheckBoxFor(m => m[i].E)
#Html.CheckBoxFor(m => m[i].D)
}
</form>
<button id="send">SEND</button>
<script type="text/javascript">
$('#send').on('click', function () {
var data = $("#myForm").serialize();
console.log(data);
//data = JSON.stringify({ 'ajaxData': data });
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: '/Home/SendData',
data: data,
success: function () {
},
failure: function (response) {
}
});
});
</script>
Controller
public void SendData(IEnumerable<SomeClass> ajaxData)
{
var data = ajaxData;
}
It took me some time to understand the problem (-:
Anyway, I have created a small test, and it is working.
public class User
{
public string UserName { get; set; }
}
public void TestPost(List<User> users)
{
}
you need to serialize your data to json array of objects,
[{ 'UserName': "user 1" }, { 'UserName': "user 2" }] a little jquery manipulation... (see here: Convert form data to JavaScript object with jQuery)
$.ajax({
contentType: 'application/json; charset=utf-8',
type: 'POST',
url: '/Home/TestPost',
data: JSON.stringify([{ 'UserName': "user 1" }, { 'UserName': "user 2" }]),
dataType: "json",
});
I have an ASP.NET MVC 5 application. And I'm trying to send a POST request with the Model data, and also include user selected files.
Here is my ViewModel (simplified for clarity):
public class Model
{
public string Text { get; set; }
public long Id { get; set; }
}
Here is the controller Action:
[HttpPost]
public ActionResult UploadFile(long userId, Model model)
{
foreach (string file in Request.Files)
{
// process files
}
return View("Index");
}
Html input element:
<div>
<input type="file" name="UploadFile" id="txtUploadFile" />
</div>
And the JavaScript code:
$('#txtUploadFile').on('change', function (e) {
var data = new FormData();
for (var x = 0; x < files.length; x++) {
data.append("file" + x, files[x]);
}
data.append("userId", 1);
data.append("model", JSON.stringify({ Text: 'test text', Id: 3 }));
$.ajax({
type: "POST",
url: '/Home/UploadFile',
contentType: false,
processData: false,
data: data,
success: function (result) { },
error: function (xhr, status, p3, p4) { }
});
});
The problem is that when the Request reaches controller action, I have files and 'userId' populated, but 'model' parameter is always null. Am I doing something wrong when populating FormData object?
Here is what I test using with MVC5 and IE11 / chrome
View
<script>
$(function () {
$("#form1").submit(function () {
/*You can also inject values to suitable named hidden fields*/
/*You can also inject the whole hidden filed to form dynamically*/
$('#name2').val(Date);
var formData = new FormData($(this)[0]);
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
data: formData,
async: false,
success: function (data) {
alert(data)
},
error: function(){
alert('error');
},
cache: false,
contentType: false,
processData: false
});
return false;
});
});
</script>
<form id="form1" action="/Home/Index" method="post" enctype="multipart/form-data">
<input type="text" id="name1" name="name1" value="value1" />
<input type="hidden" id ="name2" name="name2" value="" />
<input name="file1" type="file" />
<input type="submit" value="Sublit" />
</form>
Controller
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase file1, string name1, string name2)
{
var result = new List<string>();
if (file1 != null)
result.Add(string.Format("{0}: {1} bytes", file1.FileName, file1.ContentLength));
else
result.Add("No file");
result.Add(string.Format("name1: {0}", name1));
result.Add(string.Format("name2: {0}", name2));
return Content(string.Join(" - ", result.ToArray()));
}
}
Thanks to Silver89 for his answer
Ok so I have this form in my view:
<form id="MyForm">
<input type="text" name="myinput" />
<button type="submit" />
</form>
I have the following Javascript at the top of my view, which runs when the page loads:
<script type="text/javascript">
$(document).ready(
function () {
$("#MyForm").submit(
function () {
var url = "Home/TestAjax";
var dataToSend = $("#MyForm").serialize();
alert(dataToSend);
$.ajax
(
{
type: "POST",
url: url,
data: dataToSend,
success: function (data) {
alert(data);
}
}
);
return false;
}
);
}
);
</script>
The form is being serialised to ajax correctly, as verified by the alert box. Here is my TestAjax controller method:
[HttpPost]
public string TestAjax(string data)
{
return !string.IsNullOrEmpty(data) ? "Success" : "Failure";
}
The value being returned is Failure, because the AJAX isn't being posted back. What am I doing wrong here?
Thanks
The name of your input field is myinput not data. So make sure you have consistently named the argument of your action as well:
[HttpPost]
public ActionResult TestAjax(string myinput)
{
return !string.IsNullOrEmpty(myinput) ? Content("Success") : Content("Failure");
}
When you use $("#MyForm").serialize() this will return myinput=some_value where some_value is obviously the value that the user has entered in this input field.
And if you had 2 input fields in your form:
<form id="MyForm">
<input type="text" name="foo" />
<input type="text" name="bar" />
<button type="submit" />
</form>
you would of course write a view model:
public class MyViewModel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
that your controller action will take as parameter:
[HttpPost]
public ActionResult TestAjax(MyViewModel model)
{
return !string.IsNullOrEmpty(model.Foo) ? Content("Success") : Content("Failure");
}
Also please notice that in ASP.NET MVC controller actions should return ActionResults, not strings or whatever.