jQuery allways Post null to Action in MVC 4 - c#

I have a problem and I don't know what is the issue.
I am constructing a Json object and I want to post it back with $.ajax. The problem is I always get null in my Action.
here is Ajax Part :
$("input[type=button]#ajax-editor-save").click(function() {
var hotelPropertyAssignModel = new Object();
hotelPropertyAssignModel.Hotel_Id = 1;
hotelPropertyAssignModel.HotelProperties = new Array();
$("input.ajax-editor[data-edited=true]").each(function() {
var hotelPropertyValue = new Object();
hotelPropertyValue.HotelProperty_Id = $(this).attr("data-hotelPropertyId");
hotelPropertyValue.Language = $(this).attr("data-lang");
hotelPropertyValue.Value = $(this).attr("value");
hotelPropertyAssignModel.HotelProperties.push(hotelPropertyValue);
});
$.ajax({
url: '#Url.Action( "SetProperties" )',
type: 'POST',
dataType: 'json',
data: JSON.stringify(hotelPropertyAssignModel)
});
});
and here is Action:
[AcceptVerbs( HttpVerbs.Post )]
[HttpPost]
public void SetProperties ( string hotelPropertyAssignModel )
{
}
I changed the parameter to string to validate how json is coming. I get null when I replace it with correct model too!
anybody can help?

Make sure you set the proper contentType:
$.ajax({
url: '#Url.Action( "SetProperties" )',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(hotelPropertyAssignModel)
});
The dataType parameter that you were using indicates the response type, not the request type. You don't need it if your controller action properly sets the Content-Type response header which it normally does if you are returning for example a JsonResult.
But from what I can see your controller action is declared as void which obviously is wrong. Controller actions must return action results. If you don't care about the content, simply use an EmptyResult:
[AcceptVerbs( HttpVerbs.Post )]
[HttpPost]
public ActionResult SetProperties ( string hotelPropertyAssignModel )
{
...
return new EmptyResult();
}
Also there's another very serious problem with your controller action. It is taking a string argument instead of a view model!!! I don't know how you were possibly expecting to bind a JSON request to some string.
So, immediately define a view model that will match the JSON structure you are willing to send:
public class HotelAssignmentViewModel
{
public int Hotel_Id { get; set; }
public HotelPropertyViewModel[] HotelProperties { get; set; }
}
public class HotelPropertyViewModel
{
public int HotelProperty_Id { get; set; }
public string Language { get; set; }
public string Value { get; set; }
}
and then have your controller action take this view model as parameter:
[AcceptVerbs( HttpVerbs.Post )]
[HttpPost]
public ActionResult SetProperties ( HotelAssignmentViewModel model )
{
...
return new EmptyResult();
}
I also notice another problem with your code. You seem to have subscribed to the click event of some DOM element to trigger the AJAX request but you never cancel the default action by returning false from this event. So for example if this is a submit button or an anchor, it will simply redirect the browser away from the page leaving no time for your AJAX request to execute. So make sure you cancel this default action by returning false from your click handler:
$("input[type=button]#ajax-editor-save").click(function() {
...
return false;
});

Related

Call Method in Controller from View(cshtml)

Hi im trying to call a method from controller from view just to display the "staff_name"
Controller
public JsonResult getStaffName(int staff_id)
{
var staffname = (from a in db.master_staff where b.staff_id == staff_id
select a.staff_name).SingleOrDefault();
return Json(staffname,JsonRequestBehavior.AllowGet);
}
View
int[] staff_id = { 24,25,26 };
#foreach (var n in staff_id){
//call method getStaffName from Controller to get "staff_name"
}
it suppose to get the "staff_name" according to the "staff_id"
is there any possible method for this situation?
To call method from controller to view you have to use ajax call from view.
here is ajax call syntax in asp.net mvc:
$.ajax({
type: "GET",
url: '#Url.Action("controller method name", "controller name")',
data: { searchText: value },
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (result) {
},
error: {
}
});
type can be GET or POST depending on your controller method type.
In URL attribute two parameters are passed the first one is controller method name and second one is controller name.
In data attribute you have to pass the values which are required to pass in controller from view such as parameters in controller method.
In success and error attribute you have to write a block of code which should be executed in both cases. such as display data on UI upon success and error message on failure.
Do not do this. It is the way to the Dark side :) And each JSON request is taking some time.
Create model to store your staff
Fill it in controller (or even better in some business logic class)
Display in your view
public ActionResult Staff()
{
// This would be nice to have as separate Data Access Layery class
var staffs = GetStaffs();
return View(staffs);
}
private static StaffDto[] GetStaffs()
{
// One call to database is better that several
var staffs = db.master_staff
.Where(x => x.staff_id > 0) // Any other condition, if needed
.Select(x => new StaffDto(x.staff_id, x.staff_name))
.ToArray();
return staffs;
}
// Data Transfer Object class to separate database from presentation view
public class StaffDto
{
public int StaffId { get; set; }
public string Name { get; set; }
public StaffDto(int staffId, string name)
{
StaffId = staffId;
Name = name;
}
}
Your view file (Staff.cshtml in my case)
#model IEnumerable<TestMvc.Controllers.StaffDto>
<div>
#foreach (var staff in Model)
{
#staff.Name<br />
}
</div>
And, as bonus, you could unit test it easily
private readonly HomeController _homeController = new HomeController();
[TestMethod]
public void Staff_CheckCount()
{
var result = _homeController.Staff();
Assert.IsInstanceOfType(result, typeof(ViewResult));
var actual = ((ViewResult)result).Model as StaffDto[];
Assert.IsNotNull(actual);
Assert.AreEqual(3, actual.Length);
}
You can do it like this:
#Html.Action("getStaffName", "YourController", staff_id)
For more info, look at child actions: https://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx/
However, I do not know what you are trying to achieve with this.

ASP.NET Core MVC posting a model to controller using ajax

I am trying to post a javascript object, representative of a Model, back to a controller using an ajax post. However, the model is always showing as null.
The model in question is as follows
public class Product
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Name is required and must not be empty.")]
[StringLength(200, ErrorMessage = "Name should not exceed 200 characters.")]
public string Name { get; set; }
public DateTime Created { get; set; }
[Required(ErrorMessage = "Price is required and must not be empty.")]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
}
With the ajax call looking like so
$('#btnSaveNewProduct').click(function (e) {
e.preventDefault();
var form = $('#frmNewProduct');
if (form.valid()) {
var data = { // to be replaced with form values
Name: 'Bob',
Price: 34
};
//ajax call to save product
$.ajax({
type: "POST",
url: "#Url.Action("AddProduct", "Admin")",
contentType: "application/json",
dataType: "json",
data: data,
success: function (response) {
alert('done');
},
error: function (response) {
alert(response);
}
});
}
});
The controller method looks like the following
[HttpPost]
public JsonResult AddProduct([FromBody] Product product)
{
bool success = false;
// save product
success = true;
return new JsonResult(success);
}
Any insight would be most appreciated.
To get your code to work as desired, make the following three modifications to your code.
From the Ajax call, remove this: dataType: "json"
From the Ajax call, remove this: data:data
In the Ajax call, add this: data:JSON.stringify(data)
IMPORTANT POINTS TO NOTE:
When you are using the [FromBody] attribute, the Content-Type value determines the formatter for the ASP.NET Core MVC Framework to use for parameter binding.
Since your Content-Type is application/json, a raw JSON string and not a JSON object, should be passed as data. Hence, apply JSON.stringify.
See this reference: Parameter Binding Using [FromBody] Attribute
I found below working:
$.ajax({
method: "POST",
data: { "Name": "bob", "Price": 10},
url: "#Url.Action("AddProduct", "Admin")",
success: function (data) {
//success login
},
error: function (data) {
alert('error' + data.status);
}
});
no need to mention dataType and contentType in ajax call. And your controller will like below:
[HttpPost]
public ActionResult AddProduct(Product product)
{
//You logic will be here
}

Can't post Model via ajax

I'm using Ajax to make model and send it to the controller.
Here is Model:
public class PersonDto{
public Guid Id { get; set; }
public int PersonAge { get; set; }
public string PersonName { get; set; }
public DateTime? DateCreated { get; set; }
}
Here is Controller:
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson(PersonDto offer) {
//Save to Entity FW
}
Here is Ajax:
var data = {
Id: $('#personModal #personModalTitle').text(),
PersonAge: $('#personModal #personId').val(),
PersonName: $('#personModal #personName').val()
};
var dataJson = JSON.stringify(data);
console.log(dataJson);
$.ajax({
type: 'POST',
url: 'Persons/EditPerson',
data: dataJson,
contentType: "application/json",
success: function (result) {
},
error: function (error) {
Alert("Error Saving offer changes!");
}
});
Here is Console Log (Json), everything looks great:
{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
When I Debug PersonsController I see that my model is never populated, is not null, just no data in it.
I tried Adding DateCreated to Model in Ajax function, I tried parsing Age to int.
I tried adding [FromBody] annotation to PersonDto in Action, then I getting NULL to my model.
I don't know where I can make error here.
For JSON encoded body , just add a [FromBody] :
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] PersonDto offer)
{
return new JsonResult(offer);
}
Update :
Another way to do that is use a [ApiController] to decorate the controller class .
If there's no [ApiController] , you should add a [FromBody] to enforce the Model Binder to read from body .
Whether a camelCased or pascalCased payload or not important . It makes no difference .
Camel Case :
POST https://localhost:44372/api/values/EditPerson HTTP/1.1
Content-Type : application/json
{"id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","personAge":"5","personName":"John"}
Pascal Case :
POST https://localhost:44372/api/values/EditPerson HTTP/1.1
Content-Type : application/json
{"Id":"96f2ae80-45cc-4a6c-abe0-230c2cbd3043","PersonAge":"5","PersonName":"John"}
Can you try to get your data as a JObject:
[FromBody] JObject offer
[Route("EditPerson")]
[HttpPost]
public async Task<IActionResult> EditPerson([FromBody] JObject offer) {
string Id = (string)form.SelectToken("Id")
string PersonAge= (string)form.SelectToken("PersonAge")
string PersonName= (string)form.SelectToken("PersonName")
}
As I can see from your code you are not sending variable, the one you expect, "offer".
When doing AJAX call and sending data, data parameters should be named as those you are expecting.
You should send data in this format:
{
offer: { PersonAge: 10, PersonName: 'Senad' }
}
Your call should look like this
var data = {
Id: $('#personModal #personModalTitle').text(),
PersonAge: $('#personModal #personId').val(),
PersonName: $('#personModal #personName').val()
};
$.ajax({
type: 'POST',
url: 'Persons/EditPerson',
data: { offer: data }, //no need to serialize it
dataType: 'json',
success: function (result) {
},
error: function (error) {
Alert("Error Saving offer changes!");
}
});
I hope this is helpful.

How do I create a model using AJAX in ASP.NET MVC 5

Currently I can to create models regularly (by going SettingProfile/Create)
But when I try to use AJAX to send data wrapped in settingProfile JS object it returns HTTP 500 Internal Server Error error, I believe problem is in the data type. What would be correct way of calling Create method in AJAX?
My code:
Model:
public class SettingProfile
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public long? UserId { get; set; }
public string Url { get; set; }
}
View (JS):
function saveSettingProfile() {
var name = prompt("Please enter profile name", "");
var url = $("form").serialize(); // Not AJAX url, its a variable in model
var settingProfile = {
Name: name,
Url: url
};
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
contentType: "application/json; charset=utf-8",
dataType: "json",
method: "POST",
data: settingProfile
}).done(function (response) {
alert("Profile saved successfully");
}).fail(function () {
alert("Could not save profile");
});
}
Controller:
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Name,Url")] SettingProfile settingProfile)
{
settingProfile.UserId = 8; // TODO: get user id from session
if (ModelState.IsValid)
{
db.SettingProfiles.Add(settingProfile);
db.SaveChanges();
return Json(new { success = true });
}
return Json(new { success = false });
}
500 error means, your server code is crashing. It could be of many reasons. One thing i noticed (which might cause a 500 error) is the way you are sending data.
You specified the contentType as "application/json" .But you are sending the javascript object as it is. So your request payload will be sent as something like
Name=SomeNameValue&Url=SomeUrlValue And with your current server code, the model binder cannot map it to an object of SettingProfile.
The solution is to send the stringified version of your javascript object. You may use the JSON.stringify method to do so.
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
contentType: "application/json; charset=utf-8",
method: "POST",
data: JSON.stringify(settingProfile)
}).done(function (response) {
alert("Profile saved successfully");
};
Now your client code will send a request payload like
{"Name":"SomeName","Url":"SomeUrl"}
and model binder will be able to map it properly.
If it is simple data, you can simply not mention a custom contentType and send the js object as it is. jQuery will use the default contentType value which is "application/x-www-form-urlencoded" and model binding will work.
So the below code will work fine.
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
method: "POST",
data:settingProfile
})
You may also check the browser's network tab and see the response coming back from the server for that ajax call. If an exception happens, you can see the details there.

Why dosen't this javascript object convert to a C# class object?

I'm working on a little test bed app, where you give the app a note name, sign, and octave, and it spits out the frequency the note should sound at.
I'm having a problem with a JavaScript object that is sent to a server via AJAX. However, it does not map at all to the class on the action.
The sender code is a piece of JavaScript:
$someContainer.on('click', '.raise-pitch', function (e) {
e.preventDefault();
var $line = $(this).parents('.item-line'),
// Cache GUI elements for this line. Not pertinent.
var data = {
'model': {
'NoteName': $noteName.val(),
'Sign': $noteSign.val(),
'Octave': $noteOctave.val()
},
'steps': 1
};
var dataString = JSON.stringify(data);
$.ajax({
type: 'POST',
url: '#Url.Action("AlterPitch", "Home")',
data: dataString,
async: true,
dataType: 'json',
success: function (result) {
// Unimportant things.
}
});
});
In the data object, you can see that I have two things: the model and the steps. The steps determines how far we alter the musical note. I have verified that the values for note name, sign, and octave are making it into the data object. The steps are pre-determined.
The data object, however, maps to a ViewModel on the C# side, which looks like this:
public class NoteViewModel
{
public enum NoteSign
{
Natural,
Sharp,
Flat
}
public string NoteName { get; set; }
public NoteSign Sign { get; set; }
public int Octave { get; set; }
public float Frequency { get; set; }
private static readonly Dictionary<string, float> FrequencyLookup = new Dictionary<string, float>
{
{"C", 16.35f},
{"C#", 17.32f},
{"D", 18.35f},
{"D#", 19.45f},
{"E", 20.60f},
{"F", 21.83f},
{"F#", 23.12f},
{"G", 24.50f},
{"G#", 25.96f},
{"A", 27.50f},
{"A#", 29.14f},
{"B", 30.87f}
};
// Methods...not important for reasons below.
}
...which itself is encapsulated in a unique view model:
public class AlterPitchViewModel
{
public NoteViewModel model;
public int steps;
public NoteViewModel AlterPitch()
{
model.ChangeStep(steps);
return model;
}
}
...or at least it should. I set a break point in my controller method:
[HttpPost]
public ActionResult AlterPitch(AlterPitchViewModel model)
{
return Json(model.AlterPitch(), JsonRequestBehavior.AllowGet);
}
...But the model is null, which leads to a NullReference exception.
the model isn't serializing to an object that gets sent along in this context. Obviously I am still doing something wrong. Question is...what?
This approach has worked for me.
Server Side:
First, create your ViewModel
public class TestViewModel
{
public string Id { get; set; }
public string Description { get; set; }
}
Then, in the Controller:
[HttpPost]
public ActionResult TestingViewModel(TestViewModel udata)
{
// Stuff.
}
Client Side:
function CallTestingViewModel() {
// We create the javascript object based on the
// definition of our C# ViewModel class.
var udata = {
Id: 1,
Description: 'This is a test.'
};
// Calling the C# method via AJAX.
$.when(GenericAjaxCall('Home/TestingViewModel', false, udata)).then(function (result) {
// do stuff
});
}
// Return the AJAX call as a Promise Object
function GenericAjaxCall(controllerUrl, async, clientData) {
return $.ajax({
type: 'POST',
async: async,
url: controllerUrl,
data: JSON.stringify(clientData),
contentType: 'application/json;',
dataType: 'json'
});
}
Hope it helps.
Your HttpPost Method must have one unique parameter which is your ViewModel. My advice: create a viewmodel per post WebMethod.
So here:
public class AlterPitchViewModel
{
public NoteViewModel note { get; set; };
public int Steps { get; set; };
}
And adapt your Javascript accordingly. No need to name your javascript model instance as the parameter in the ASP method -> one object arrives and only one parameter is available so no ambiguity.

Categories