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

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; }
}

Related

Form-data is not binding to viewmodel with a generic type

I have an application written using C# on the top of ASP.NET Core 5.0.
I have the following view-model
public class TestVM
{
public Name { get; set; }
public MenuViewModel<string> State { get; set;}
public TestVM()
{
State = MenuViewModel<string>();
}
}
Here is a stripped down version of my MenuViewModel
public class MenuViewModel
{
[BindNever]
public IEnumerable<SelectListItem> Items { get; set; }
}
public class MenuViewModel<T> : MenuViewModel
{
public T Value { get; set; }
}
The problem, is when the post request comes in, the viewModel.State.Value is null. When I evaluate Request.Form I do see the key State.Value with the correct value of CA
Here is a stripped down of my action method in the controller.
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Store(TestVM viewModel)
{
if(ModelState.IsValid)
{
// do some
}
return View(viewModel);
}
How can I bind the form data from the request to State.Value property correctly?
Updated I created an editor-template to allow me to render the MenuVieModel. The ~/Views/Shared/EditorTemplates/MenuViewModel.cshtml contains the following code
#model dynamic
#{
if (!(Model is MenuViewModel m))
{
return;
}
dynamic obj = new System.Dynamic.ExpandoObject();
obj.Class = "form-control";
if (Html.ViewData.ModelMetadata.IsRequired)
{
obj.Required = true;
}
}
#Html.DropDownList("Value", m.Options, Html.ViewData.ModelMetadata.Placeholder, obj)
Firsly,you need know that 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.
Here is a working demo you could follow:
Model:
public class TestVM
{
public string Name { get; set; }
public MenuViewModel<string> State { get; set; }
public TestVM()
{
State =new MenuViewModel<string>();
}
}
public class MenuViewModel
{
[BindNever]
public IEnumerable<SelectListItem> Items { get; set; }
}
public class MenuViewModel<T> : MenuViewModel
{
public T Value { get; set; }
}
View:
#model dynamic
#{
if (!(Model is MenuViewModel m))
{
return;
}
dynamic obj = new System.Dynamic.ExpandoObject();
obj.Class = "form-control";
if (Html.ViewData.ModelMetadata.IsRequired)
{
obj.Required = true;
}
}
<form asp-action="Store">
#*change here,And I do not find Options in your MenuViewModel,So I change it to Items*#
#Html.DropDownList("State.Value", m.Items, Html.ViewData.ModelMetadata.Placeholder, obj)
<input type="submit" value="post" />
</form>
Controller:
public IActionResult Index()
{
var model = new MenuViewModel<string>()
{
Items = new List<SelectListItem>() {
new SelectListItem() { Value = "-1", Text = "--- Select ---" },
new SelectListItem() { Value = "org1", Text = "org1" },
new SelectListItem() { Value = "org2", Text = "org2" },
new SelectListItem() { Value = "org3", Text = "org3" }
}
};
return View(model);
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Store(TestVM viewModel)
{
if (ModelState.IsValid)
{
// do some
}
return View(viewModel);
}
Result:

TextboxFor in MVC is not binding to Model inside a for loop

My Viewmodel (contains a number of submodels) is:
public class ViewModel
{
public IEnumerable<tblQuestion> DemoQuestions { get; set; }
public IEnumerable<tblQuestion> TechQuestions { get; set; }
public List<tblAnswer> DemoQ1Answers { get; set; }
public List<tblAnswer> DemoQ2Answers { get; set; }
public List<tblAnswer> TechAnswers { get; set; }
public string selectedAnswerIDforQ1 { get; set; }
public string selectedAnswerIDforQ2 { get; set; }
}
My tblAnswer model is like below:
public partial class tblAnswer
{
public int AnswerID { get; set; }
public Nullable<int> QuestionID { get; set; }
public string AnswerDescription { get; set; }
public string Q1Other { get; set; }
}
This is my view code:
#using (Html.BeginForm("Display", "Questionnaire", FormMethod.Post))
{
foreach (tblQuestion question in Model.DemoQuestions)
{
<p>#question.QuestionDescription</p>
#for (int i = 0; i < Model.DemoQ1Answers.Count(); i++)
{
if (question.QuestionID == 1)
{
<div class="col-md-4">
#Html.RadioButtonFor(m => Model.selectedAnswerIDforQ1, Model.DemoQ1Answers[i].AnswerID)
#Model.DemoQ1Answers[i].AnswerDescription
#Html.HiddenFor(m => Model.DemoQ1Answers[i].AnswerID)
#Html.HiddenFor(m => Model.DemoQ1Answers[i].AnswerDescription)
#if (Model.DemoQ1Answers[i].AnswerID == 4)
{
#Html.TextBoxFor(m => Model.DemoQ1Answers[i].Q1Other)
}
</div>
}
}
}
<input type="submit" value="Submit" />
}
When I press submit, my textbox value in the model is null. Means, mymodel.DemoQ1Answers[3].Q1Other is null. All other model values are bound with model values in the view except this one.
This is my controller:
[HttpPost]
public ActionResult Display(ViewModel mymodel)
{
foreach (var demoqstns in mymodel.DemoQuestions)
{
int AnswerSelected = 0;
string otherText = null;
if (qstnID == 1)
{
AnswerSelected = Convert.ToInt32(mymodel.selectedAnswerIDforQ1);
if (AnswerSelected == 4)
{
otherText = mymodel.DemoQ1Answers[3].Q1Other;
}
}
else if (qstnID == 2)
{
AnswerSelected = Convert.ToInt32(mymodel.selectedAnswerIDforQ2);
}
}
}
Please help.
you have to use for loop instated of foreach
for (var i = 0; i < Model.Orders.Count(); i++)
#Html.HiddenFor(x => Model.Orders[i].CustomerID)
the index value i appears in the lambda expression for each form element being generated by the loop, The TextBoxFor HtmlHelper is used, rather than the more general (and automagic) DisplayFor and EditorFor.

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?

how to perform specific action on submit button in asp.net mvc4

I am working with Dual ListBoxes and using two buttons to move data from one listbox to another..
Here following code I have done to move data from one Listbox to another
InstituteInformation.cs
public class InstituteInformation
{
public int Id { get; set; }
public string InstituteName { get; set; }
}
MemberAccessRights.cs
public class MemberAccessRights
{
public int Id { get; set; }
public List<InstituteInformation> AvailableNames { get; set; }
public int[] AvailableSelected { get; set; }
public List<InstituteInformation> RequestedNames { get; set; }
public string[] RequestedSelected { get; set; }
public string SavedRequested { get; set; }
}
//Controller
//
// GET: /MemberDetails/Create
public ActionResult Create()
{
Wrapper1 MD = new Wrapper1();
MD.MAR = new MemberAccessRights{ AvailableNames = getAllInstituteNameList(), RequestedNames = new List<InstituteInformation>() };
return View(MD);
}
//
// POST: /MemberDetails/Create
[HttpPost]
public ActionResult Create(Wrapper1 MD, string add, string remove)
{
try
{
ModelState.Clear();
RestoreSavedState(MD);
if (!string.IsNullOrEmpty(add))
AddNames(MD);
elseif (!string.IsNullOrEmpty(remove))
AddNames(MD);
SaveState(MD);
using (varMDConext = new WrapperDB())
{
MDConext.MBIDBS.Add(MD.MBI);
MDConext.MACDBS.Add(MD.MAC);
MDConext.MARDBS.Add(MD.MAR);
MDConext.MODBS.Add(MD.MO);
}
returnRedirectToAction("Index");
}
catch
{
return View(MD);
}
}
#regionSupportFuncs
void SaveState(Wrapper1 MD)
{
MD.MAR.SavedRequested = string.Join(",", MD.MAR.RequestedNames.Select(p =>p.Id.ToString()).ToArray());
////Available Names = All - Requested
MD.MAR.AvailableNames = getAllInstituteNameList().Except(MD.MAR.RequestedNames).ToList();
}
//RestoreSavedState
void RestoreSavedState(Wrapper1 MD)
{
MD.MAR.RequestedNames = newList<InstituteInformation>();
if (!string.IsNullOrEmpty(MD.MAR.SavedRequested))
{
string[] nameids = MD.MAR.SavedRequested.Split(',');
var name = getAllInstituteNameList().Where(p =>nameids.Contains(p.Id.ToString()));
MD.MAR.RequestedNames.AddRange(name);
}
}
//AddNames
void AddNames(Wrapper1 MD)
{
if (MD.MAR.AvailableSelected != null)
{
var names = getAllInstituteNameList().Where(p =>MD.MAR.AvailableSelected.Contains(p.Id));
MD.MAR.RequestedNames.AddRange(names);
MD.MAR.AvailableSelected = null;
}
}
//RemoveNames
void RemoveNames(Wrapper1 MD)
{
if (MD.MAR.RequestedSelected != null)
{
MD.MAR.RequestedNames.RemoveAll(p =>MD.MAR.RequestedSelected.Contains(p.Id.ToString()));
MD.MAR.RequestedSelected = null;
}
}
#endregion
View
List of Financial Institute
<%:Html.ListBoxFor(model=>model.MAR.AvailableSelected,new MultiSelectList(Model.MAR.AvailableNames,"Id","InstituteName",Model.MAR.AvailableSelected)) %>
<div>
<input id="add" name="add" type="submit" value=">>" />
<br />
<input id="remove" name="remove" type="submit" value="<<" />
</div>
<%:Html.ListBoxFor(m=>m.MAR.RequestedSelected,new MultiSelectList(Model.MAR.RequestedNames,"Id","Name",Model.MAR.RequestedSelected)) %>
But there is the problem is that when I click on add(>>) or remove(<<) button the action is performed on the complete page just like the submit button which save the data from that page to db. Here I wanted to know how to perform the action button after clicking the add(>>) or remove(<<) button.
please help to solve this
The idea was that a form could contain more than one submit button issuing a form post to a different way.

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)
});
});

Categories

Resources