Is it possible to automatically bind ASP.NET controllers model with an ajax request that submits data as FormData.
in my provided example I'm required to use HttpContext.Current.Request.Form["property_name"]
to receive data because if I provide a model that is identical to the submitted form data, all values are equal to null;
or does ASP.NET model binding only work on JSON requests ?
Simple code bellow:
View:
#using (Html.BeginForm("Post", "Test", FormMethod.Post, new { #class="test-form"}))
{
<input type="text" name="firstName"/>
<input type="text" name="lastName"/>
<button type="submit">Submit</button>
}
Scripts:
<script>
$('.test-form').on('submit', function (e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: "#Url.Action("TestPost", "Test")",
method: "POST",
data: formData,
processData: false,
success: function(e){
}
});
});
</script>
Controller:
[HttpPost]
public ActionResult TestPost()
{
var firstname = HttpContext.Current.Request.Form["firstName"];
var lastName = HttpContext.Current.Request.Form["lastName"];
return PartialView("TestPost");
}
Does Not Work Controller:
public class User
{
public string firstName { get; set; }
public string lastName { get; set; }
}
[HttpPost]
public ActionResult TestPost(User model) //model values are null
{
return PartialView("TestPost");
}
When you use a FormData object with ajax the data is sent as multipart/form-data and the content type header is set automatically for you with the correct boundary.
You can override the content type and set tit to whatever you want, which is what happens here.
You might be thinking well I didn't do it, well you good friend jQuery did it for you. It set the default content type for $.ajax for you (application/x-www-form-urlencoded) which pretty much craps up the request.
To stop this action i.e. to stop jQuery from setting a content type header you have to set the contentType parameter to false.
Related
I'm trying to send a list of values from View to Controller. I tried a lot of solutions from the Internet but haven't succeeded.
My View HTML Table list of Data send to controller via JSON but the list is empty even when I use JSON.stringify
Heres my code
JavaScript:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script type="text/javascript">
$("body").on("click", "#btnSave", function () {
//Loop through the Table rows and build a JSON array.
var customers = new Array();
$("#tblCustomers TBODY TR").each(function () {
var row = $(this);
var customer = {};
//skill.skill_name = row.find("TD").eq(0).html();
customer.CNIC = row.find("TD").eq(1).html();
customers.push(customer);
});
console.log(customers);
console.log(JSON.stringify(customers));
//Send the JSON array to Controller using AJAX.
$.ajax({
type: "POST",
//traditional: true,
url: "/Admin/Reconcile/Customers",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(customers),
dataType: "json",
success: function (r) {
alert(r + " record(s) inserted.");
location.reload();
}
});
});
</script>
Controller Action:
public JsonResult Customers(List<String> customers)
{
}
Firstly,you need to create a model like this:
public class Customer {
public string CNIC { get; set; }
}
Then since you pass json type data in ajax,you need to use [FromBody] in action:
public JsonResult Customers([FromBody]List<Customer> customers)
{
}
You are sending an array of objects, so for the model binder to work you should accept a List of SomeModel, where SomeModel should have a property CNIC. Check the documentation here https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api on how objects are binded.
So you are sending this:
[
{
"CNIC":"somevalue",
"CNIC":"somevalue"
}
]
And you are trying to bind it to a List of string and it will not happen. Try binding it with List of SomeModel:
public class SomeModel
{
public string CNIC { get; set; }
}
Also try this attribute [IgnoreAntiforgeryToken] above your action. If it works then, you should be sending the antiforgery token with the post request.
My project consists on making an OCR read on an image from coordinates drawn by the user.So i have made 2 external javascripts one manipulates the rectangles and the second manipulates the file(delete, preview..)
FileUpload.JS
function saveImage() {
var file = $("#imageBrowser").get(0).files;
var data = new FormData;
data.append("ImageFile", file[0]);
$.ajax({
async: true,
type: "POST",
dataType: "JSON",
url: "/OcrImage/OcrOnImage",
data: data,
processData: false,
contentType: false,
})
}
RectangleDrawing.js
$(function () {
$('#btnSend').click(function (e) {
$.ajax({
type: "POST",
contentType: "application/json;charset=utf-8",
data: JSON.stringify(boundingBoxes),
url: "/OcrImage/OcrOnImage",
});
});
});
the Controller
[HttpPost]
public ActionResult OcrOnImage(ImageUploadModel objeImageViewModel, Coordinate[] Coordinates)
{//code}
My question is how can i pass data from each javascripts to a single controller through a single submit.Keep in mind that i have tried to upload the file using razorview and i could pass only one data which is either Coordinates or the file to upload.
If there's a better method to do this please let know with an example.Thank you in advance.
I can provide more info if not clear.
If you want to file upload with view models (in your case ImageUploadModel) then you need to consider two things.
Your view model should contains a property of type HttpPostedFileBase like below:
HttpPostedFileBase ImageUpload { get; set; }
You should have strongly typed view with model bindings. something like below:
#model ImageUploadModel
<form action="" method="post" enctype="multipart/form-data">
<div>
#Html.LabelFor(m => m.ImageUpload)
#Html.EditorFor(m => m.ImageUpload)
</div>
<button type="submit">Create</button>
You could use #using (Html.BeginForm()) { ... } helper alternative to using form if you want to but for fileupload you must have this enctype="multipart/form-data" attribute.
Controller code would be like below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult OcrOnImage(ImageUploadModel model)
{
var validImageTypes = new string[]
{
"image/gif",
"image/jpeg",
"image/pjpeg",
"image/png"
}
if (model.ImageUpload == null || model.ImageUpload.ContentLength == 0)
{
ModelState.AddModelError("ImageUpload", "This field is required");
}
else if (!imageTypes.Contains(model.ImageUpload.ContentType))
{
ModelState.AddModelError("ImageUpload", "Please choose either a GIF, JPG or PNG image.");
}
if (ModelState.IsValid)
{
var image = new Image
{
Title = model.Title,
AltText = model.AltText,
Caption = model.Caption
}
if (model.ImageUpload != null && model.ImageUpload.ContentLength > 0)
{
var uploadDir = "~/uploads"
var imagePath = Path.Combine(Server.MapPath(uploadDir), model.ImageUpload.FileName);
var imageUrl = Path.Combine(uploadDir, model.ImageUpload.FileName);
model.ImageUpload.SaveAs(imagePath);
image.ImageUrl = imageUrl;
}
db.Create(image);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
I have never faced like this situation where file upload and another action is going with along , it is some violation of SRP (single responsibility principle). By the way you can try coordination with js as you described above on putting Coordinate list/array in controller action like below and let me is that works or not.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult OcrOnImage(ImageUploadModel model,Coordinate[] Coordinates) {//like above}
I have a create,edit,delete application On my Index view i have Button for Edit. by clicking this button it should open pop up in which all data should be displayed for editing.
To achieve this i passed ID of that row which is getting Edited. see code below :
<td>
<button type="button" onclick="EditPopup(#item.Id)">Edit</button>
</td>
here i am passing ID to my EditPopup javascript method. see the method below :
<script type="text/javascript">
$(document).ready(function () {
$("#EditDialog").dialog({
autoOpen: false,
title: 'Title',
width: 'auto',
height: 'auto',
modal: true
});
});
function EditPopup(Get_Id) {
alert(Get_Id) // I am getting correct ID here.
$.ajax({
method: "POST",
url: '#Url.Action("Edit","Home")',
dataType: 'json',
cache: false,
data:{Get_Id}, // tried : {id:Get_Id} , {id:"Get_Id"} not working
success: function (data) {
$('#EditDialog').html(data);
}
});
$("#EditDialog").dialog("open");
}</script>
I am sending value of ID to my Controller method Edit thats why i am using Post method in ajax call. Edit is name of method and Home is name of controller.
HomeController Edit methods
[HttpPost]
public JsonResult Edit(int? id)
{
FloorFactor floorFactor = db.FloorFactors.Find(id);
return Json(floorFactor, JsonRequestBehavior.AllowGet);
}
// POST:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
FloorFactor floorFactor = db.FloorFactors.Find(id);
return View(floorFactor);
}
in few examples i saw that in ajax call they usually use json result method. so that is the reason i also used json result method.
finally Code which is in my index view where i will show pop up result.
<div id="EditDialog" style="display:none;">
<label> Floor Factor </label>
<br />
<label> Effective From :</label>
So the Whole scenario is :
I send id value on button click event to javascript.
on javascript i make a call to my controller method to get data.
those should pass in EditDialog box of div.
on div block it should display in pop up.
Current output :
I also want to understand how url field works in ajax call.
if i am getting multiple column results as output of that url how can i collect all output in Data part of ajax call.
please also explain on success what parameters i can pass in function.
Thank you for explanation and help.
Edit : It shows no error on console tab.
as shown in this script tab i think it is sending a request as it generates request Id.
Try the below changes
Action Code :
[HttpPost]
public JsonResult Edit(int? id)
{
FloorFactor floorFactor = db.FloorFactors.Find(id);
return Json(floorFactor, JsonRequestBehavior.AllowGet);
}
View Changes
<div id="EditDialog" style="display:none;">
<label> Floor Factor </label> <span id="floorFactor"></span>
<br />
<label> Effective From :</label> <span id="effectiveFrom"></span>
Success method changes
if(data)
{
// GET VALUES
var floorFactor = data.Property_Having_FloorFactor;
var effectiveFrom = data.Property_Having_EffectiveFrom;
// ASSIGN VALUES
$('#floorFactor').text(floorFactor);
$('#effectiveFrom ').text(effectiveFrom );
// OPEN DIALOG
$("#EditDialog").dialog("open");
}
Hope it will work for you.
Change the Controller as
[HttpGet]
public JsonResult Edit(int? id)
{
FloorFactor floorFactor = db.FloorFactors.Find(id);
return Json(floorFactor, JsonRequestBehavior.AllowGet);
}
// POST:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
FloorFactor floorFactor = db.FloorFactors.Find(id);
return View(floorFactor);
}
Change the 'ajax' as
$.ajax({
method: "GET",
url: '#Url.Action("Edit","Home")',
dataType: 'json',
cache: false,
data: {id:Get_Id},
success: function (data) {
$('#EditDialog').html(data);
}
});
Note: Code is untested. It should have work for you.
I have a simple create form in MVC 4 and would like two submit functions: (1) Create and (2) Create & Print. Create is a normal Create action and works perfectly. Create & Print should save the object and then launch a popup browser window with data from the newly saved object. The original window needs to refresh to a blank Create form ready for another record.
What is the best way to approach this?
Below is an example that works in practice however I have the ID hardcoded in. Ideally, this ID will dynamically inherit from the object that was just saved and link there. Is JavaScript the best idea here or should (can) I launch the popup from the Controller?
<input type="submit" value="Create" />
<input type="submit"
value="Create & Print"
onclick="window.open('Print/f1ad6330-2978-4ea9-9116-65f861412260'
, 'PRINT'
, 'height=200,width=200');" />
Best option is to create another action which returns string (last-insert-id), post data to it through ajax and get last-insert-id back in javascript then you can use it to open new window.
Now suppose this is new controller action:
[HttpPost]
public string CreateAndPrint(Object obj)
{
// Save data here / insert record here
if (Request.IsAjaxRequest())
{
// Now get last insert id
string lastInsertId = db.GetLastInsertId; // get last insert id from database
return lastInsertId;
}
}
Have a javascript function to post the data:
<script type="text/javascript">
function creteAndPrint() {
$.ajax(
{
url : "CreateAndPrint",
type: "POST",
data : $("#from1").serialize(),
success:function(data)
{
var lastInsId = data; // you will get last insert id here.
var secWin = window.open('Print/'+lastInsId
, 'PRINT'
, 'height=200,width=200');
secWin.focus();
}
});
}
</script>
And call this function only on create & print button:
<input type="submit" value="Create & Print" onclick="creteAndPrint();" />
Hope it works for you. Thank you.
Here I am editing my answer after your comment :)
Yes! you can call the same Create action for achieving the same which I explained above. But for that you have to make some changes in the your Create action:
public string Create(Object obj)
{
// Save data here / insert record here
if (Request.IsAjaxRequest())
{
// Now get last insert id
string lastInsertId = db.GetLastInsertId; // get last insert id from database
return PartialView("_Create", lastInsertId);
}
return View();
}
Notice that when you call this action through AJAX it will return a partial view, which return just LAST_INSERT_ID as string. You just have create one simple partial view _Create to print last-insert-id.
Partial view will have only two lines:
#model string
#Model
This will print the last-inst-id which we have passed from controller action.
I ended up bypassing the form's default submit call to the Create method and just created two new methods. It's not ideal, but it works.
SOLUTION
Form:
#using (Html.BeginForm("Dummy", "Count", FormMethod.Post, new { id = "form1" }))
{
// My Form
// Note the Dummy controller which will just fall through and do nothing
}
Form Submit:
<input type="submit" value="Create & Print" onclick="createAndPrint();" />
<input type="submit" value="Create" onclick="createWithoutPrinting();" />
JavaScript:
<script type="text/javascript">
function createAndPrint() {
$.ajax(
{
url: "CreateAndPrint",
type: "POST",
data: $("#form1").serialize(),
success: function (data) {
var lastInsId = data; // you will get last insert id here.
var secWin = window.open('Print/' + lastInsId
, 'PRINT'
, 'height=450,width=230');
secWin.focus();
}
});
}
</script>
<script type="text/javascript">
function createWithoutPrinting() {
$.ajax(
{
url: "Create",
type: "POST",
data: $("#form1").serialize()
});
}
</script>
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Dummy(Count count)
{
return RedirectToAction("Create");
}
[HttpPost]
public string CreateAndPrint(Count count)
{
SaveCount(count);
if (Request.IsAjaxRequest())
{
// Now get last insert id
string lastInsertId = count.Id.ToString();
return lastInsertId;
}
return "";
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Count count)
{
SaveCount(count);
if (Request.IsAjaxRequest())
{
// Now get last insert id
string lastInsertId = count.Id.ToString();
return PartialView("_Create", lastInsertId);
}
return RedirectToAction("Create");
}
I am trying to auto post the inputs in my beginform, but their values do not reach the model or controller. They remain null (break points never hit.) What could be causing this?
#model project.Models.data
JAVASCRIPT
function send()
{
$.ajax({
type: 'POST',
url: this.action,
data: {'data1': $('#data1').val(),
'data2': $('#data2').val() },
success: function (done) {
$('#box').html(done.output);
});
}
FORM
#using (Html.BeginForm())
{
<input id="data1" value="0" name="data1" onclick="send();" >
<input id="data2" value="0" name="data2" onclick="send();" >
}
MODEL
namespace projects.Models
{
public class data
{
public int data1{ get; set; }
public int data2 { get; set; }
}
}
You don't seem to be canceling the default event of the form. I would recommend you subscribing to the .submit event of the form and stop mixing markup and javascript:
#using (Html.BeginForm())
{
<input id="data1" value="0" name="data1" />
<input id="data2" value="0" name="data2" />
}
and then in a separate javascript file you could subscribe to the .submit event of this form:
$(function() {
$('form').submit(function() {
$.ajax({
type: this.method,
url: this.action,
data: {
data1: $('#data1').val(),
data2: $('#data2').val()
},
success: function (done) {
$('#box').html(done.output);
}
});
// It is very important to return false in order to prevent the default action
return false;
});
});
For starters, I would recommend not using the onclick attribute. Using jQuery's click or on is much better.
Secondly, the Html.BeginForm() does not work the way in which you are using it. If you want to hit the controller method you need to have a submit button with the value of the controller method you are trying to hit.
If you are trying to hit a method asynchronously with jQuery (using AJAX that is), then I recommend looking at this post here:
Send data with jquery to an MVC controller
To POST to the server you don't need the Html.BeginForm(), if you are using AJAX. However, if you want to POST and Redirect to another action, I would use the Html.BeginForm().
Now if you are trying to POST to the server without using AJAX, then you need that submit button in your Html.BeginForm(). This article below details how a Register Form works and could apply to your situation.
How does the MVC3 Controller retrieve HTTPPOST params?