WebApi receiving null parameter when I post using jQuery in ASP.Net - c#

So I am trying to build a simple API, that allows me to create customers, I am using ASP.NET Web API on VS2012 and I am using the default project configuration.
Here is what I have:
HTML
<h2 id="msg"></h2>
<form onsubmit="return submitCustomer()">
<input type="text" name="name" id="name" />
<input type="text" name="email" id="email" />
<input type="text" name="phone" id="phone" />
<input type="submit" value="Sign Up" />
</form>
<script type="text/javascript">
function submitCustomer() {
$.ajax({
url: '/api/customer',
type: 'POST',
datatype: 'json',
success: function (newCustomer) {
var msg = 'Welcome ' + newCustomer.Name;
$('#msg').text = msg;
}
});
return false;
}
</script>
CONTROLER METHOD
// POST api/customer
public Customer Post(Customer customer)
{
CustomerService customerService = new CustomerService();
customerService.CreateCustomer(customer);
return customer;
}
MODEL
public class Customer : BaseModel
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private string _phone;
public string Phone
{
get { return _phone; }
set { _phone = value; }
}
private string _email;
public string Email
{
get { return _email; }
set { _email = value; }
}
private Region _region;
public virtual Region Region
{
get { return _region; }
set { _region = value; }
}
private List<Product> _products;
public virtual List<Product> Products
{
get
{
if (_products == null)
{
_products = new List<Product>();
}
return _products;
}
set { _products = value; }
}
private List<CustomerPromotion> _customerPromotions;
public virtual List<CustomerPromotion> CustomerPromotions
{
get
{
if (_customerPromotions == null)
{
_customerPromotions = new List<CustomerPromotion>();
}
return _customerPromotions;
}
set { _customerPromotions = value; }
}
private int? _regionId;
public int? RegionId
{
get
{
return _regionId;
}
set
{
_regionId = value;
}
}
}
What is happening is that when I submit the form I get to the POST method but customer is null, does anyone have any idea why this happens?

You are missing data in $.ajax function.
$.ajax({
datatype:'json',
data: yourformdata,
...
});

Related

Binding to ViewModel on PJax PostBack

I have a viewModel that I want to bind to pjax postback action:
public class MyViewModel
{
public MySetting mySetting{ get; set; }
public IList<MyDetail> myDetails{ get; set; }
}
This is the function I want to hit to handle the postBack
[HttpPost]
public ActionResult SaveMySettings(MyViewModel viewModel)
{
viewModel.mySettings; // This populate fine
viewModel.myDetails; // Is NULL
// handle saving here
return this.PAjax("myPage", null, viewModel);
}
My custom classes are generated using Entity Framework from DB:
public class MySetting
{
private bool _settingA;
[ColumnAttribute(Storage="_settingA", DbType="Bit NOT NULL")]
public bool settingA
{
get
{
return this._settingA;
}
set
{
if ((this._settingA!= value))
{
this.OnsettingAChanging(value);
this.SendPropertyChanging();
this._settingA= value;
this.SendPropertyChanged("settingA");
this.On_settingAChanged();
}
}
}
}
public class MyDetail
{
private bool _detailA;
[ColumnAttribute(Storage="_detailA", DbType="Bit NOT NULL")]
public bool detailA
{
get
{
return this._detailA;
}
set
{
if ((this._detailA!= value))
{
this.OnsettingAChanging(value);
this.SendPropertyChanging();
this._detailA= value;
this.SendPropertyChanged("detailA");
this.On_detailAChanged();
}
}
}
}
My ASPX page look like this:
#model MyProject.MyViewModel
<div id="main">
<form id="myForm" post="/Settings/SaveMySettings">
#Html.TextBoxFor(m => m.mySetting.settingA)
#for (int i = 0; i < Model.myDetails.Count(); i++){
#Html.CheckBoxFor(m => Model.myDetails[i].detailA, #checked = "checked" }) MyCheckBox #Model.myDetails[i].detailA
}
Save Settings
</form>
</div>
And I am trying to postback using this PJax code:
<script lang="javascript">
(function ($) {
$('#saveChanges').click(function () {
var f = $(this).closest('form');
$.pjax({
type: 'POST',
url: "/Settings/SaveMySettings",
container: '#main',
data: $("#myForm").serializeArray()
});
return false;
});
})(jQuery);
</script>
When I try this, MyViewModel only populate mySettings but not myDetails, it seem like that the list would not be binded, what am I doing wrong?

Submitting form from View to Controller isn't populating Model object

I have a Controller, View, and Model for an application I am taking over from someone else. The problem I'm having is that when the javascript submits the form, my model object in the Edit function of the Controller is not being filled with the values from the View, all fields are either null or 0. How can I get the object to be properly filled?
I have tried looking at Firebug, but I can't see anything that would indicate a problem. The string parameter id is correctly being passed in the URL, ex: firsttry.com/admin/distribution/edit/sanfran, where sanfran is the id string. Firebug is confirming that the javascript is correctly loaded when it would be called.
#section scripts
{
#Scripts.Render("~/bundles/qmmodules/admin/distribution")
}
DistributionController.cs
namespace JRI.QM.WebUI.Areas.Admin.Controllers
{
public class DistributionController : BaseAdminController
{
[HttpPost]
public ActionResult Edit(string id, DistributionViewModel model)
{
}
}
}
DistributionViewModel.cs
namespace JRI.QM.WebUI.Areas.Admin.Models.ViewModels
{
public class DistributionViewModel
{
public string name;
public string ipAddress;
public string description;
public Byte eyeDee;
public string destinationPath;
public List<JRI.DMP.BLL.Filter> filters;
}
}
Edit.cshtml
#model JRI.QM.WebUI.Areas.Admin.Models.ViewModels.DistributionViewModel
#{
ViewBag.Title = "Edit";
}
#using (Html.BeginForm("Edit", "Distribution", FormMethod.Post, new { id = "editRoleForm" }))
{
<div class="dialog" id="editRoleDialog">
#Html.TextBoxFor(x => x.name)
#Html.HiddenFor(x => x.ipAddress)
#Html.TextBoxFor(x => x.description)
#Html.HiddenFor(x => x.eyeDee)
#Html.HiddenFor(x => x.destinationPath)
#Html.HiddenFor(x => x.filters)
<div id="permissionsDialogFooter" class="dialogFooter newUser_dialogFooter">
<div class="qm_mediumButton submitButton">save</div>
<div class="qm_mediumButton cancelButton">cancel</div>
<br class="clearfloat" />
</div>
</div>
}
distribution.js
QM.ui.Role = (function ($) {
var _xhrInProgress = false;
function init() {
_bindEvents();
}
// private functions
function _bindEvents() {
var addEditRoleEvents = function (formObj) {
formObj.submit(function () {
var $this = $(this);
if ($this.valid()) {
$.post($this.attr("action"), $this.serialize(), function (response) {
if (response.Success) {
if (response.RedirectUrl) {
window.location.href = response.RedirectUrl;
}
}
});
}
return false;
});
formObj.find(".submitButton").click(function () {
formObj.submit();
});
};
// Edit role dialog
$(".dt-edit a").click(function () {
var $this = $(this);
$.get($this.attr("href"), function (data) {
$(".content").after(data);
QM.ui.Mask.show();
addEditRoleEvents($("#editRoleForm"));
});
return false;
});
}
return {
init: init,
};
})(jQuery);
$(document).ready(QM.ui.Role.init);
in RouteConfig.cs
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "JRI.QM.WebUI.Controllers" }
);
The default model binder of ASP.NET MVC, only work with properties. Your model class doesn't have any property, it does have fields.
To fix that use properties instead field in your model class, like below
public class DistributionViewModel
{
public string name { get; set; }
public string ipAddress { get; set; }
public string description { get; set; }
public Byte eyeDee { get; set; }
public string destinationPath { get; set; }
public List<JRI.DMP.BLL.Filter> filters { get; set; }
}
Also I suggest you to follow the C# code convetions, it says that you should use PascalCase for properties, you can see more details here https://msdn.microsoft.com/en-us/library/ms229043(v=vs.110).aspx
Following the convetion, your class should be like that
public class DistributionViewModel
{
public string Name { get; set; }
public string IpAddress { get; set; }
public string Description { get; set; }
public Byte EyeDee { get; set; }
public string DestinationPath { get; set; }
public List<JRI.DMP.BLL.Filter> Filters { get; set; }
}

Why Hidden Fields value are not posted in mvc 4?

In my MVC-4 application i take location from geolocation using javaScript and set latitude, longiture and accuracy in Hidden Fields. Values are set correctly in Hidden fields but during PostBack it shows null.
Here is my code :
Razor :
#using (Html.BeginForm()
{
#Html.HiddenFor(x => x.CurrentLocation.Latitude)
#Html.HiddenFor(x => x.CurrentLocation.Longitude)
#Html.HiddenFor(x => x.CurrentLocation.Accuracy)
<input type="submit" name="command" value="Start" />
}
JavaScript :
function SetLocation(position) {
console.log("{0},{1},{2}".format(position.coords.latitude, position.coords.longitude, position.coords.accuracy));
$("#CurrentLocation_Latitude").val(position.coords.latitude);
$("#CurrentLocation_Longitude").val(position.coords.longitude);
$("#CurrentLocation_Accuracy").val(position.coords.accuracy);
}
$(document).ready(function () {
$('form').submit(function () {
LocationService.getCurrentLocation(SetLocation);
});
});
But if I write this code then it`s work
$(document).ready(function () {
$('form').submit(function () {
$("#CurrentLocation_Latitude").val(100);
$("#CurrentLocation_Longitude").val(100);
});
});
This send proper value to the controller.
I don`t understand why this is happening.
Thank`s in advance.
Update 1 :
Model :
[ComplexType]
public class Location
{
public Location()
{
}
public Location(double latitude, double longiture, double? accuracy = null)
{
Latitude = latitude;
Longitude = longiture;
if (accuracy.HasValue)
Accuracy = accuracy.Value;
}
[DisplayName("Latitude : ")]
public double? Latitude { get; set; }
[DisplayName("Longitude : ")]
public double? Longitude { get; set; }
public double? Accuracy { get; set; }
}
public class ServiceInfoEditMetadata : ServiceInfo
{
public Int64 MachineId { get; set; }
[DisplayName("Client Name :")]
public string ClientName { get; set; }
[DisplayName("Site Name :")]
public string SiteName { get; set; }
public Location CurrentLocation { get; set; }
[DisplayName("Client Username :")]
public string ClientUsername { get; set; }
[DisplayName("Client Password :")]
public string ClientPassword { get; set; }
}
Controller :
public ActionResult Edit(Int64 id, ServiceInfoEditMetadata serviceInfoEditMetadata, string command)
{
try
{
switch (command)
{
case "Add":
AddMachineToServiceInfoDetails(serviceInfoEditMetadata);
return View(serviceInfoEditMetadata);
case "Start":
_serviceInfoService.StartService(serviceInfoEditMetadata, User.Identity.Name);
return RedirectToAction("Edit", serviceInfoEditMetadata.Id);
case "Update":
if (!ModelState.IsValid) return View(serviceInfoEditMetadata);
_serviceInfoService.UpdateServiceInfo(serviceInfoEditMetadata, User.Identity.Name);
return RedirectToAction("List");
}
return View(serviceInfoEditMetadata);
}
catch (Exception ex)
{
ModelState.AddModelError("ServiceInfoEditError", ex.Message);
return View(serviceInfoEditMetadata);
}
}
I assume your LocationService.getCurrentLocation is asynchronous. You should then delay form submission until it completes.
maybe you can try :
$(document).ready(function () {
var locationSet =false;
$('form').submit(function (event) {
if(!locationSet)
event.preventDefault();
LocationService.getCurrentLocation(
function(position){
SetLocation(position);
locationSet= true;
$('form').submit();
}
);
});
});
There could be more causes, because the mechanism (e.g mvc binding, ajax) of passing the fields' data is little complex.
I see that in the following js code you're passing handler to function 'SetLocation'. Try to invoke the function:
$(document).ready(function () {
$('form').submit(function () {
LocationService.getCurrentLocation(SetLocation(position));// 'SetLocation' with (position)
});
});

ASP.NET MVC pass ViewModel to different ViewModel

I have a product which has viewModel:
ProductViewModel
private int _id;
private string _name;
private string _type;
private int _selectedID;
public ProductViewModel(int id, string name, string type)
{
_ id = id;
_ name = name;
_ type = type;
}
public int ProductID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Type
{
get { return _type; }
set { _type = value; }
}
public int Selected
{
get { return _selected; }
set { _selected = value; }
}
And for showing a list of products I have another ViewModel:
ProductListViewModel
private PagedResult<ProductViewModel> _pagedResult;
private string _sortColumn = "Id";
private string _sortOrder = "ASC";
public LostAssetsListViewModel(PagedResult< ProductViewModel > products)
{
_pagedResult = products;
}
public PagedResult<ProductViewModel> List
{
get { return _pagedResult; }
set { _pagedResult = value; }
}
public string SortColumn
{
get { return _sortColumn; }
set { _sortColumn = value; }
}
public string SortOrder
{
get { return _sortOrder; }
set { _sortOrder = value; }
}
And in my View I'm using Product LisViewModel to show a list of products. And in every product I want to add radio button (to select that product)
<% = Html.RadioButtonFor(m => m.List[i].Selected, "false", Model.List[i]. ProductID)%>
For getting all the data in my controller I created ProductInputModel:
ProductInputModel
private int _id;
private string _name;
private string _type;
private int _selectedID;
public ProductInputModel(int id, string name, string type)
{
_ id = id;
_ name = name;
_ type = type;
}
public int ProductID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Type
{
get { return _type; }
set { _type = value; }
}
public int Selected
{
get { return _selected; }
set { _selected = value; }
}
In my controller input is empty :
[HttpPost]
public ActionResult Details(ProductInputModel input)
{}
I can't figure out whydata from ProductViewModel not passes to ProductInputModel. How I should get the selected product through ViewModels?
Your view models must have default parameterless constructors if you want to use them as action parameters. Otherwise the default model binder will not be able to instantiate it. You should make sure that you have a parameterless constructor to your ProductInputModel.

Use DisplayAttribute on class

As we can use System.ComponentModel.DataAnnotations.DisplayAttribute to set a label for a property, I want to use it for the class but it is not allowed on classes.
using System.ComponentModel.DataAnnotations;
[Display(Name = "A person")]
public class Person
{
[Display(Name = "A name")]
public string Name { get; set; }
}
Is anyone know a workaround for that ?
EDIT :
I want to use it on a strongly typed view. When I create a new strongly typed view, the class name is hard coded in HTML, like that :
#model Models.Person
<fieldset>
<legend>Person</legend>
<div class="display-label">
#Html.LabelFor(model => model.Name)
</div>
</fieldset>
I want to do something similar to the Name property.
The DisplayName attribute (from System.ComponentModel) performs a similar function and can be applied to a class.
MSDN
I really don't know if it's there another way to do this, but i usually to not hard code this i use create a variable in the view and then i called where i needed. In your case to do it a little more elegant i'll do
#{
var viewName = typeof(Foo).Name;
}
#model Models.Person
<fieldset>
<legend>#viewName</legend>
<div class="display-label">
#Html.LabelFor(model => model.Name)
</div>
</fieldset>
Using the Decorator Pattern, just wrap the DisplayAttribute with your own custom Attribute specifically for classes.
using System;
using System.ComponentModel.DataAnnotations;
namespace YourNameSpaceHere.Support
{
[AttributeUsage(AttributeTargets.Class)]
public class DisplayForClassAttribute : Attribute
{
protected readonly DisplayAttribute Attribute;
public DisplayForClassAttribute()
{
this.Attribute = new DisplayAttribute();
}
public string ShortName
{
get { return this.Attribute.ShortName; }
set { this.Attribute.ShortName = value; }
}
public string Name
{
get { return this.Attribute.Name; }
set { this.Attribute.Name = value; }
}
public string Description
{
get { return this.Attribute.Description; }
set { this.Attribute.Description = value; }
}
public string Prompt
{
get { return this.Attribute.Prompt; }
set { this.Attribute.Prompt = value; }
}
public string GroupName
{
get { return this.Attribute.GroupName; }
set { this.Attribute.GroupName = value; }
}
public Type ResourceType
{
get { return this.Attribute.ResourceType; }
set { this.Attribute.ResourceType = value; }
}
public bool AutoGenerateField
{
get { return this.Attribute.AutoGenerateField; }
set { this.Attribute.AutoGenerateField = value; }
}
public bool AutoGenerateFilter
{
get { return this.Attribute.AutoGenerateFilter; }
set { this.Attribute.AutoGenerateFilter = value; }
}
public int Order
{
get { return this.Attribute.Order; }
set { this.Attribute.Order = value; }
}
public string GetShortName()
{
return this.Attribute.GetShortName();
}
public string GetName()
{
return this.Attribute.GetName();
}
public string GetDescription()
{
return this.Attribute.GetDescription();
}
public string GetPrompt()
{
return this.Attribute.GetPrompt();
}
public string GetGroupName()
{
return this.Attribute.GetGroupName();
}
public bool? GetAutoGenerateField()
{
return this.Attribute.GetAutoGenerateField();
}
public bool? GetAutoGenerateFilter()
{
return this.Attribute.GetAutoGenerateFilter();
}
public int? GetOrder()
{
return this.Attribute.GetOrder();
}
}
}
Usage would be as follows:
[DisplayForClass(Name = "Approval Matrix")]
public class ApprovalMatrixViewModel
{
}

Categories

Resources