This question already has an answer here:
Why is Model Binding not working in my POST action method?
(1 answer)
Closed 5 years ago.
I know there are tons of posts similar to this, but I cannot seem to find the answer after much investigation. On post back CompanySetting.NewSetting.SettingType and CompanySetting.NewSetting.SettingValue are null, despite putting text into the TextBoxFor fields and clicking submit.
Why is this?
Here is the view:
#model Project.Models.CompanySettingView
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<form>
<div class="form-group">
<label>Setting Type</label>
#Html.TextBoxFor(m => m.NewSetting.SettingType, new { #class = "form-control", #id = "SettingType" })
<br />
<label>Setting Value</label>
#Html.TextBoxFor(m => m.NewSetting.SettingValue, new { #class = "form-control", #id = "SettingValue" })
<br />
</div>
<div style="float: right;">
<button type="button" style="width:100px;" onclick="ClearFields()" class="btn btn-default">Clear</button>
<button type="submit" style="width:100px;" class="btn btn-primary">Submit</button>
</div>
</form>
}
Receiving Controller:
[HttpPost]
public ActionResult AddCompanySetting(CompanySettingView model)
{
...
}
CompanySettingView Class:
public class CompanySettingView
{
public List<CompanySetting> Settings = new List<CompanySetting>();
public CompanySetting NewSetting = new CompanySetting();
}
CompanySetting Class:
public class CompanySetting
{
public int CompanySettingId { get; set; }
public int CompanyId { get; set; }
public string SettingType { get; set; }
public string SettingValue { get; set; }
}
Answer from Comment
its to do with your object initialiser in the CompanySettingView. Change those to properties and initialise them in your constructor (make sure the class has a default constructor) and all should be golden
Something like this:
public class CompanySettingView
{
public List<CompanySetting> Settings { get; set; }
public CompanySetting NewSetting { get; set; }
public CompanySettingView()
{
this.Settings = new List<CompanySetting>();
this.NewSetting = new CompanySetting();
}
}
Related
Not sure why but the for some reason the view model is not binding the DepartmentId when I hit the submit button, I get an error:
Value cannot be null.
Parameter name: value
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Not sure which value is being null as the code does not break, rather after hitting the submit button that is the message that is being displayed.
I'm assuming it's the departmentId that's not being properly bound to the DepartmentID property in CoursePreReqViewModel.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: value
View:
#using (Html.BeginForm("Catalog", "Courses", FormMethod.Post, new { #class = "pure-form pure-form-aligned" }))
{
#Html.AntiForgeryToken()
<div class="row">
<div class="col">
<input id="myInput" class="form-control" type="text" placeholder="Search..">
</div>
<div class="col">
#Html.LabelFor(model => model.DepartmentId, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownListFor(model => model.DepartmentId, Model.DepartmentList, "Department", new { #class = "form-control required", id = "department-list" })
#Html.ValidationMessageFor(model => model.DepartmentId)
</div>
<div class="col">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
}
Controller:
[HttpPost]
public ActionResult Catalog(CoursePreReqViewModel viewModel)
{
DepartmentRepo dRepo;
CoursesRepo cRepo;
MajorPreRequisitesRepo reqRepo;
using (context)
{
dRepo = new DepartmentRepo(context);
cRepo = new CoursesRepo(context);
viewModel.PopulateDepermentSelectList(dRepo.GetAllDepartments());
reqRepo = new MajorPreRequisitesRepo(context);
viewModel.Courses = cRepo.GetAllCoursesAndPreReqsByDepartment(viewModel.DepartmentId);
}
return View(viewModel);
}
View model:
public class CoursePreReqViewModel
{
[Required]
[Display(Name = "")]
public int DepartmentId { get; set; }
public IList<Course> Courses { get; set; }
public IList<MajorPreRequisite> CoursesAndPreReqs { get; set; }
[Display(Name = "Department: ")]
public IList<Department> Departments { get; set; }
public CoursePreReqViewModel() { }
public SelectList DepartmentList
{
get
{
return new SelectList(Departments, "Id", "Name");
}
}
public void PopulateDepartmentSelectList(IList<Department> populatedDepartments)
{
Departments = populatedDepartments;
}
}
Sarthak here is another answer: this is the asp.net mvc fiddler https://dotnetfiddle.net/ARdtvr
!!!Display Name needs to be a value!!!
I can help you keep the code you have, and answer your question.
controller/classes:
public class Course
{
public int CourseId { get; set; }
}
public class MajorPreRequisite
{
public int MajorPreRequisiteId { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CoursePreReqViewModel
{
//Make sure to comment this out -or- put this field in view
//try this first with your code, before you change code eg using a dictionary
//[Required]
//[Display(Name = "")]
public int DepartmentId { get; set; }
public IList<Course> Courses { get; set; }
public IList<MajorPreRequisite> CoursesAndPreReqs { get; set; }
[Display(Name = "Department: ")]
public IList<Department> Departments { get; set; }
public CoursePreReqViewModel() { }
public Dictionary<string, string> DepartmentList { get; set; }
public void GetAllCoursesAndPreReqsByDepartment(IList<Course> populateCourses)
{
Courses = populateCourses;
}
}
public class HomeController : Controller
{
[HttpPost]
public ActionResult Catalog(CoursePreReqViewModel viewModel)
{
//Put a breakpoint herre to see departmentid of user choice
return View();
}
public ActionResult Index11()
{
CoursePreReqViewModel viewModel = new CoursePreReqViewModel();
Dictionary<string, string> depts = new Dictionary<string, string>();
depts.Add("1", "deptOne");
depts.Add("2", "deptTwo");
viewModel.DepartmentList = depts;
IList<Course> courses = new List<Course>();
courses.Add(new Course { CourseId = 1 });
courses.Add(new Course { CourseId = 2 });
viewModel.GetAllCoursesAndPreReqsByDepartment(courses);
return View(viewModel);
}
view:
#model WebApplication4what2.Controllers.CoursePreReqViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index11</title>
</head>
<body>
#using (Html.BeginForm("Catalog", "Home", FormMethod.Post, new { #class = "pure-form pure-form-aligned" }))
{
<div class="row">
<div class="col">
<input id="myInput" class="form-control" type="text" placeholder="Search..">
</div>
<div class="col">
#Html.LabelFor(model => model.DepartmentId, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownListFor(model => model.DepartmentId, new SelectList(Model.DepartmentList, "Key", "Value"), Model.DepartmentId)
#Html.ValidationMessageFor(model => model.DepartmentId)
</div>
<div class="col">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
}
</body>
</html>
I'm using custom templates within my MVC project to display various object types in different ways. Some are working and others are not! For any that don't work, they are being passed into my object.cshtml custom template rather than their own.
Here's an example. In this example, I'm creating an address lookup type which I want to render a first line of address and postcode field with a lookup button.
My ViewModel has the following:
namespace MyProject.Views
{
public class AddressLookup
{
public string Postcode { get; set; }
public string FirstLine { get; set; }
}
public class RegistrationViewModel
{
[DisplayName("Address Lookup")]
public AddressLookup addressLookup { get; set; }
}
}
My view looks like this:
#model RegistrationViewModel
<div class="well" id="form-well">
<h2>Register New User</h2>
<h3>Step 1 of 3 - Login Details</h3>
#using (Html.BeginForm("RegisterNewUser", "Controller", FormMethod.Post, new { #class = "form-horizonal" }))
{
#Html.AntiForgeryToken();
#Html.EditorForModel(Model);
#Html.ValidationSummary();
<div style="padding: 15px;" class="form-group col-md-offset-3">
<input type="submit" value="Next" class="btn btn-lg btn-success" />
</div>
}
</div>
My AddressLookup.cshtml looks like this:
#using MyProject
#model AddressLookup
#Html.TextBoxFor(x => x.FirstLine)
#Html.TextBoxFor(x => x.Postcode)
<p>
<button class="btn btn-info" type="button" onclick="alert('lookup');" value="Add new address">Lookup address</button>
</p>
But when debugging, it runs the code in the object.cshtml in the EditorTemplates folder and not the one for AddressLookup.
Any thoughts?
Thanks
Simon
Adding a UIHint to the property in my view model worked (although I don't fully understand why).
namespace MyProject.Views
{
public class AddressLookup
{
public string Postcode { get; set; }
public string FirstLine { get; set; }
}
public class RegistrationViewModel
{
[DisplayName("Address Lookup")]
[UIHint("AddressLookup")]
public AddressLookup addressLookup { get; set; }
}
}
Thanks
Simon
I am making an MVC application. I am creating a View that uses a ViewModel and also use database entities in this view passed by the controller.
Controller:
public ActionResult AddGroup(AddGroupViewModel model)
{
ClassDeclarationsDBEntities1 entities=new ClassDeclarationsDBEntities1();
return View(entities.Subjects.ToList());
}
ViewModel:
public class AddGroupViewModel
{
[Required]
[Display(Name = "Subject")]
public string subject_name { get; set; }
[Required]
[Display(Name = "Number of Groups")]
public int qty { get; set; }
}
And finally my view:
#model List<ClassDeclarationsThsesis.Classes.Subject>
#model ClassDeclarationsThsesis.Models.AddGroupViewModel
#{
ViewBag.Title = "Add Groups";
}
<h2>Add Groups to subjects</h2>
#using (Html.BeginForm("AddGroup", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create new groups.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#{
List<SelectListItem> listItems1 = new List<SelectListItem>();
}
#foreach (var subject in Model)
{
listItems1.Add(new SelectListItem
{
Text = subject.name,
Value = subject.name,
Selected = true
});
}
#Html.LabelFor(m => m.subject_name, new {#class = "col-md-2 control-label"})
<div class="col-md-10">
#Html.DropDownListFor(m => m.subject_name, listItems1, new {#class = "form-control"})
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.qty, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.qty, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
As you see, I am trying to use two models in my view. But gives an exception (well how to distinguish them). How do I combine those two models in a view?
Edit:
So I did what suggested in answer, but now I get such exception:
{"The class 'ClassDeclarationsThsesis.Models.Subject' has no parameterless constructor."}
The class looks like this:
namespace ClassDeclarationsThsesis.Models
{
using System;
using System.Collections.Generic;
public partial class Subject
{
private int v;
private int userid;
public Subject(int v, int userid, string name)
{
this.class_id = v;
this.user_id = userid;
this.name = name;
}
public int class_id { get; set; }
public int user_id { get; set; }
public string name { get; set; }
public virtual Group Group { get; set; }
public virtual Subjects_Users Subjects_Users { get; set; }
public virtual Task Task { get; set; }
}
}
How do I solve it?
Since you already have a view model, I'd use that:
#model ClassDeclarationsThsesis.Models.AddGroupViewModel
And simply add a property to that view model for the collection you also want to use:
public class AddGroupViewModel
{
[Required]
[Display(Name = "Subject")]
public string subject_name { get; set; }
[Required]
[Display(Name = "Number of Groups")]
public int qty { get; set; }
public List<Subject> Subjects { get; set; }
}
Then simply create an instance of that from your controller to send to the view:
var entities = new ClassDeclarationsDBEntities1();
var model = new AddGroupViewModel();
model.Subjects = entities.Subjects.ToList();
// set your other properties too?
return View(model);
Then in the view simply refer to the property on the Model instead of the model itself when you need that collection:
#foreach (var subject in Model.Subjects)
Basically, while you can use only one type for your model (since there's only one Model property available to the view in the framework), that type can be anything you like, even a custom view model type that you define.
This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 7 years ago.
I have a class from EF.
public partial class Customer
{
public Customer()
{
this.Customer_CustomerSpecialConcern = new HashSet<Customer_CustomerSpecialConcern>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public virtual ICollection<Customer_CustomerSpecialConcern> Customer_CustomerSpecialConcern { get; set; }
}
When I pass the model from controller to view everything works fine (can access Customer_CustomerSpecialConcern values).
The problem is when I post back model to a controller to save the changes the property Customer_CustomerSpecialConcern is null.
Here is how I use it in view.
#foreach (var ccsc in Model.Customer_CustomerSpecialConcern)
{
<div class="form-group fields-container col-md-3">
<label class="field-label control-label col-md-10" for="">#ccsc.CustomerSpecialConcern.Title</label>
<div class="col-md-1 field-input">
#Html.EditorFor(model => ccsc.Value)
#Html.HiddenFor(model => ccsc.Value)
</div>
</div>
}
Please, I need help to get the values of this collection property to controller. Thank you.
Update - Customer_CustomerSpecialConcern class details
public partial class Customer_CustomerSpecialConcern
{
public int Id { get; set; }
public int Customer_Id { get; set; }
public int CustomerSpecialConcern_Id { get; set; }
public bool Value { get; set; }
public virtual Customer Customer { get; set; }
public virtual CustomerSpecialConcern CustomerSpecialConcern { get; set; }
}
Please try this,
#for (int i = 0; i < Model.Customer_CustomerSpecialConcern.Count(); i++)
{
<div class="form-group fields-container col-md-3">
<label class="field-label control-label col-md-10" for="">#Model.CustomerSpecialConcern[i].Title</label>
<div class="col-md-1 field-input">
#Html.EditorFor(model => model.Customer_CustomerSpecialConcern[i].Value)
#Html.HiddenFor(model => model.Customer_CustomerSpecialConcern[i].Value)
</div>
</div>
}
Check this article.
I tried your example, and this is how it looks
public ActionResult Index()
{
var customer = new Customer
{
Name = "Name",
Surname = "Surname",
Email = "email#email.com",
Mobile = "mobile...",
Customer_CustomerSpecialConcern = new List<Customer_CustomerSpecialConcern>
{
new Customer_CustomerSpecialConcern
{
Value = true
},
new Customer_CustomerSpecialConcern
{
Value = true
}
}
};
return View(customer);
}
View:
#model WebApplication1.Models.Customer
#{
ViewBag.Title = "Customer";
var customer_CustomerSpecialConcern = Model.Customer_CustomerSpecialConcern.ToList();
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
for (int i = 0; i < Model.Customer_CustomerSpecialConcern.Count(); i++)
{
<div class="form-group fields-container col-md-3">
<label class="field-label control-label col-md-10" for=""></label>
<div class="col-md-1 field-input">
#Html.CheckBoxFor(model => customer_CustomerSpecialConcern[i].Value)
</div>
</div>
}
<input type="submit" value="Save"/>
}
I can't get the ObjectId for a model to return back with a value using the MongoDB csharp official library. When I submit the form that posts back to the controller, the PageID always comes back {000000000000000000000000} in the model. The HTML rendered has the valid id, that one field never comes back properly from the form post.
The html renders: <input id="PageID" name="PageID" type="hidden" value="4f83b5ccd79f660b881887a3" />
This is what I have.
Model:
public class PageModel
{
public PageModel()
{
CanEdit = false;
CancelRequest = false;
}
[BsonId]
public ObjectId PageID { get; set; }
[Display(Name = "Page URL")]
public string PageURL { get; set; }
[AllowHtml]
[Display(Name = "Content")]
public string Value { get; set; }
[Display(Name = "Last Modified")]
public DateTime LastModified { get; set; }
[BsonIgnore]
public bool CancelRequest { get; set; }
[BsonIgnore]
public bool CanEdit { get; set; }
}
Controller Post:
[HttpPost]
public ActionResult Edit(PageModel model)
{
// check to see if the user canceled
if (model.CancelRequest)
{
return Redirect(model.PageURL);
}
// check for a script tag to prevent
if (!model.Value.Contains("<script"))
{
// save to the database
model.LastModified = DateTime.Now;
collection.Save(model);
// return to the page
return Redirect(model.PageURL);
}
else
{
ModelState.AddModelError("Value", "Disclosures discovered a script in the html you submitted, please remove the script before saving.");
}
return View(model);
}
View:
#model LeulyHome.Models.PageModel
#{
ViewBag.Title = "";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
<fieldset>
<legend>Edit Page</legend>
<div class="control-group">
#Html.LabelFor(m => m.PageURL, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(m => m.PageURL, new { #class = "input-xlarge leu-required span9" })
#Html.ValidationMessageFor(m => m.PageURL, null, new { #class = "help-block" })
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.Value, new { #class = "control-label" })
<div class="controls">
#Html.TextAreaFor(m => m.Value)
#Html.ValidationMessageFor(m => m.Value, null, new { #class = "help-block" })
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.LastModified, new { #class = "control-label" })
<div class="controls">
#Html.DisplayFor(m => m.LastModified)
#Html.HiddenFor(m => m.LastModified)
</div>
</div>
#Html.HiddenFor(m => m.PageID)
#Html.HiddenFor(m => m.CancelRequest)
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save Page</button>
<button type="button" class="btn btn-danger" id="cancelBtn">Cancel Changes</button>
</div>
</fieldset>
}
#section Footer {
<script type="text/javascript" src="#Url.Content("~/Content/ckeditor/ckeditor.js")"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
// adjust the editor's toolbar
CKEDITOR.replace('Value', {
toolbar: [["Bold", "Italic", "Underline", "-", "NumberedList", "BulletedList", "-", "Link", "Unlink"],
["JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock"],
["Cut", "Copy", "Paste", "PasteText", "PasteFromWord", "-", "Print", "SpellChecker", "Scayt"], ["Source", "About"]]
});
$("#cancelBtn").click(function () {
$("#CancelRequest").val("True");
$("#updateBtn").click();
});
});
</script>
}
It looks like you are sending a string value for PageID which you are expecting an object of type ObjectId.
The model binding isn't going to know how to take this string and turn it into an ObjectId. If you take a look at the ObjectId class in the MongoDriver you will see that it is pretty hairy.
We use mongodb quite a bit and for our id's we simply use strings which provide a lot of flexibility. I am not sure the use case for which you need the ObjectId class as your document Id but you may not need it.
So from here it seems like you have two options.
Change your document id's to strings or something else
Write a custom model binder for the ObjectId class
Hope that helps :)
Create Binding:
public class ObjectIdBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
return !string.IsNullOrWhiteSpace(result.AttemptedValue) ? ObjectId.Parse(result.AttemptedValue) : ObjectId.Empty;
}
}
Create ModelBinderConfig:
namespace Nasa8x.CMS
{
public class ModelBinderConfig
{
public static void Register(ModelBinderDictionary binder)
{
binder.Add(typeof(ObjectId), new ObjectIdBinder());
binder.Add(typeof(string[]), new StringArrayBinder());
binder.Add(typeof(int[]), new IntArrayBinder());
}
}
}
Register on Global.cs:
protected void Application_Start()
{
ModelBinderConfig.Register(ModelBinders.Binders);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
If you don't need the PageID property in your C# Class to be of Type ObjectId, but want to take advantage of this Type on the MongoDB-Side, you can let the Driver take care of the conversion in your class-Definition:
public class PageModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string PageID { get; set; }
}