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?
Related
I have a controller, view and model like below
//model
public class ItemViewModel
{
public string name {get;set;}
public decimal? TotalPrice { get; set; }
}
//controller
[HttpPost]
[MultipleButton(Name = "action", Argument = "Submit")]
public ActionResult Submit(ItemViewModel model)
{
decimal? a = model.TotalPrice;
Console.WriteLine(a);
}
//view
#Html.EditorFor(model => model.TotalPrice, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" name="action:Submit" class="btn btn-primary" value="Submit"/>
when I debug the model.TotalPrice is always null when there is a commas in number for example 10,234,776, but if there is no comma it gets the actual result. any idea?
//Change your ItemViewModel like
public class ItemViewModel
{
public string name { get; set; }
public string TotalPrice { get; set; }
}
//Introduce your custom Parser to handle the comma case
private bool MyTryParse(string toParse, out decimal result)
{
//Remove commas
string formatted = toParse.Replace(",", "");
return decimal.TryParse(formatted, out result);
}
//And your controller be like
[HttpPost]
[MultipleButton(Name = "action", Argument = "Submit")]
public ActionResult Submit(ItemViewModel model)
{
decimal a = null;
if (MyTryParse(model.TotalPrice, out a))
{
//Success code
}
else
{
//some error code
}
Console.WriteLine(a);
}
Note: Not maybe the best but a workable solution. Hope this helps your case.
Your code needs a decimal, the text box needs a string. The ViewModel is where you do the conversion.
private decimal totalPriceDecimal;
private string totalPriceString;
public string TotalPrice
{
get
{
return this.totalPriceString();
}
set
{
this.totalPriceString = value;
decimal temp;
if (int.TryParse(value, out temp))
{
this.totalPriceDecimal = temp;
}
}
}
Then your code that needs a decimal can use totalPriceDecimal, while you can bind the string to the text box.
More importantly, if the user enters something that can't be parsed, you know and can display an error message.
i tried it and it is working fine
the following is the code and the view assigned
[HttpPost]
public ActionResult Submit(ItemViewModel model)
{
decimal? a = model.TotalPrice;
Console.WriteLine(a);
return View();
}
public ActionResult Submit()
{
return View();
}
//=====================View============================
#model WebApplication4.Controllers.ItemViewModel
#Html.BeginForm()
{
#Html.EditorFor(model => model.TotalPrice, new { htmlAttributes = new { #class = "form-control" } })
}
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; }
}
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)
});
});
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,
...
});
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
{
}