ModelState.IsValid == false, although all model values are inserted - asp.net-mvc-4

today I have the problem, that after i inserted all data to a formular to create a new Product, the programm say that ModelState.IsValid==false.
When i look into the modelState during debugging there is a Error on field 0. The error: "The CuId field is required".
To prevent that i set CuId right in the Creat POST action like so in the ProductController.cs:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product)
{
int lastcu = db.Customers.Max(l => l.Id);
product.CuId = last;
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Create", "NewIssue");
}
return View(product);
}
But again it sets the same error.
My view looks like that. Actually the model.CuId should already set there:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CuId, "Customer")
#ViewBag.Cuname
#Html.HiddenFor(model => model.CuId, new { id = "lastcu" })
</div>
My GET Controller looks like this:
public ActionResult Create()
{
int lastcu = db.Cu.Max(l => l.Id);
//gives the id a Name
var lastcuname = db.Customers.Find(lastcu).Name;
//show to User by creating the product
ViewBag.Cuname = lastcuname;
ViewBag.CuId = lastcu;
return View();
}
When I look in debug mode into the values of the model product, all fields are filled (also CuId) except of the the foreign key what is bind to the product.CuId and the Id of the product what is set automatically from the database.
Hopefully you can help me. Thanks in advance.

As for the first part of your question, ModelState is populated by the DefaultModelBinder when the method is first called. If property CuId has the [Required] attribute and its value is not posted back, then an error is added to ModelState and therefore ModelState.IsValid is false. Just setting a property of your model does not remove ModelState values.
As for the second part of your question, your not passing you model to the view in the GET method so #Html.HiddenFor(m => m.CuId) generates a hidden input with no value (because the value of model.CuId is null or its default). All you currently doing is passing some values to the view using ViewBag (not good practice) which you never even use. Instead, pass the model to the view as follows.
public ActionResult Create()
{
int lastcu = db.Cu.Max(l => l.Id);
var lastcuname = db.Customers.Find(lastcu).Name;
// Initialize a new instance of the model and set properties
Product model = new Product()
{
CuId = lastcu,
Cuname = lastcuname // assume this is a property of your model?
};
return View(model); // return the model
}
Side note: #Html.LabelFor(model => model.CuId, "Customer") generates a html <label> which is an accessibility element. Clicking on it sets focus to its associated form control. But you don't have an associated form control (just a hidden input which cannot receive focus)

Related

Check record if exist (instantly) in MVC

I want to check instantly, after i type something in text box, if a record exist in my database. I managed to return on my page number of how many times record exist in database, but i want to return a message (if exists or not).
So, the question is: How can I display a message if record exist or not?
PS. I`m using ASP.NET MVC
Here is my code:
Model class:
public class AdminModel
{
[Remote("IsUniq", "Home", HttpMethod = "POST")]
public string FirstName { get; set; }
}
My controller action(HomeController):
[HttpPost]
public JsonResult IsUniq(string FirstName)
{
IPAdressProgramEntities r = new IPAdressProgramEntities();
var user = r.spLineExist(FirstName);//spLineExist - procedure in SQL- return how many time record exist in database
return Json(user); //return on my page how many times record exists
}
And this is my view:
#using (Html.BeginForm())
{
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
}
PS WebConfig is configured and also scripts are included in my view.
Thank you.
If you simply want to show message when your count is greater than 0, Add an Error message property to your data annotation.
[Remote("IsUniq", "Home", HttpMethod = "POST", ErrorMessage = "Exist")]
public string FirstName { get; set; }
and return true of false from your action method. To show the error message, you need to return false from the method.
var responseToSend = user!=0; //user is the count returned by your existing code
return Json(responseToSend);
If you want to show both the messages (Exists/ Not exists), you may consider handling the check yourself with a little jQuery ajax call than relying on the remote data annotation. So simply remove the data annotation.
And listen to the keyup event on this input field, read the value, send to server to check it exist or not, based on the result, show appropriate message
$(function () {
$("#FirstName")
.keyup(function () {
$.post('/Home/IsUniq?FirstName=' + $(this).val(), function (res) {
if (res) {
$("span[data-valmsg-for='FirstName']").text("Not Available")
} else {
$("span[data-valmsg-for='FirstName']").text("Available")
}
})
});
});
Make sure you return True or False from your action method.
[HttpPost]
public JsonResult IsUniq(string FirstName)
{
//If exists
return Json(true);
else
return Json(false);
}

MVC 4 ModelState invalid because it is trying to auto-fill values and not accept new ones

I have a comments section. In the view there is only a comments editor box.
#using (Html.BeginForm(new { courseID = #ViewBag.courseID, userName = #User.Identity.Name }))
{
#Html.ValidationSummary(true)
<div class="NewComment">
<div class="editor-field">
#Html.TextAreaFor(model => model.CommentText, new { maxLength = 500})
#Html.ValidationMessageFor(model => model.CommentText)
</div>
<input type="submit" class="PostComment" value="Post Comment" />
<div id="Counter" class="CommentCounter"/>
</div>
}
The model has the course the comment is linked too, date, comment text, and user. The other values get filled in the create post method.
[HttpPost]
public ActionResult AddComment(CourseComment coursecomment, int courseID, String userName)
{
userName = userName.Split('\\')[1];
coursecomment.CommentDate = System.DateTime.Now;
coursecomment.CourseID = courseID;
coursecomment.UserName = db.Users.FirstOrDefault(u => u.UserName == userName).UserID;
if (ModelState.IsValid)
{
db.CourseComments.AddObject(coursecomment);
db.SaveChanges();
}
return RedirectToAction("Details", "Course", new { courseID = courseID });
}
The problem occurs here. The model is trying to use the userName parameter for the value for courseComment.UserName before I actually do the work and set it. After it gets set the ModelState doesn't change.
Example, domain\abc123 gets passed into the post method and also set in the ModelState for UserName. I do some work, change the userName to abc123 and find the linked ID, lets say ID = 1, to that user with said name, then plug that into the courseComment.UserName ModelState still leaves the domain\abc123 in there and the model stays invalid.
Now, this was working original, then I changed the underlying database around, mainly just column names and some relations.
My solution for this.
Move receiving the username from the post method
[HttpPost]
public ActionResult AddComment(CourseComment coursecomment, int courseID)
{
coursecomment.CommentDate = System.DateTime.Now;
coursecomment.CourseID = courseID;
coursecomment.UserName = db.Users.FirstOrDefault(u => u.UserName == userName).UserID; //Moved
if (ModelState.IsValid)
{
db.CourseComments.AddObject(coursecomment);
db.SaveChanges();
}
return RedirectToAction("Details", "Course", new { courseID = courseID });
}
to a get method.
[HttpGet]
public JsonResult GetUserName(string userName)
{
var ret = db.Users.FirstOrDefault(u => u.UserName == userName).UserID;
return Json(ret, JsonRequestBehavior.AllowGet);
}
Then changed the view to be like
#Html.HiddenFor(model => model.UserName)
....
<script type="text/javascript">
$(function () {
var userName = '#User.Identity.Name.Split('\\')[1]';
$.ajax({
url: '#Url.Action("GetUserName", "CourseComment")',
data: { userName: userName },
type: 'get'
}).done(function (data) {
$('#UserName').val(data);
});
});
The problem is that all the view cares about is what's in ModelState. This is confusing to many devs, but it's logical when you think about it.
Essentially, ModelState is composed from the values of Model, of course, but then also from values in ViewBag, ViewData, and Request, which override anything set via Model. To understand why, imagine a scenario where the user is editing an existing object, but makes an error in one of the values, causing the form to be returned to correct their error. If the values from Model were use, the users edits would be completely undone, replaced with the original values on the object. However, by using the values from Request, ModelState preserves the users submitted values, allowing them to only make the necessary corrections.
Long and short, you have to be very careful about naming request parameters, ViewBag properties, etc., the same as properties on your model. Probably the simplest solution in your scenario is to just change the request param, userName to something else.
Also, for what it's worth, ModelState is case insensitive, so UserName is the same as userName, username, USERNAME or UsErNaMe.

ASP.NET mvc Ajax Helper DropDownListFor send selected item value as parameter to controller

Problem
I want my Ajax form to pass the value of the selected DropDownListFor to the controller but I can't figure out why the controller is not taking any value.
I am working with ASP.NET MVC and I would like to stick with the helper functions as much as I can.
View
#using (Ajax.BeginForm(new AjaxOptions {
HttpMethod = "Get",
UpdateTargetId = "UserResults",
InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace }))
{
#Html.DropDownListFor(Model => Model.Roles, new SelectLi(ViewBag.Groups
as System.Collections.IEnumerable, "Value", "Text"), "Select a group",
new { id = "Value", onchange = "$(this.form).submit();" })
}
#Html.Partial("_UsersGroup", ViewData)
Controller
public ActionResult test(int? selectGroup)
{
// Generate dropdownlist Groups
List<SelectListItem> groupList = new List<SelectListItem>();
var query = from s in db.Roles select s;
if (query.Count() > 0)
{
foreach (var v in query)
{
groupList.Add(new SelectListItem { Text = v.Name, Value =
v.ID.ToString() });
}
}
ViewBag.Groups = groupList;
// End
// This part is supposed to take the passed value as parameter
if (selectGroup == null)
{
// To do code comes here, which takes selectGroup as parameter
}
Details
The form should pass a value based on the selection to the controller which takes it as "selectGroup".
ps. this is my first time asking a question, I'm sorry if I made mistakes.
The parameter of you method needs to match the name of the control which is name="Roles" so the method should be
public ActionResult test(int? roles)
Other potential issues with your code
Your controller generates List<SelectListItem> for use by the dropdownlist. There is no need for the unnecessary extra overhead of then creating a new SelectList (which is IEnumerable<SelectListItem>) from it. The view code can simply be #Html.DropDownListFor(m => m.Roles, (IEnumerable<SelectListItem>)ViewBag.Groups, "Select a group")
Do not use Model (capital M) in the expression. If you make any other reference to the model in your view (e.g. #Model.SomeProperty) you will get an error. Using lower case model => model.somProperty is OK but you can simply use m => m.someProperty
The helper will generate an id attribute (in you case id="Role") so it seems unclear why you are adding new { id = "Value", ..}, especially since you don't appear to be referencing the element by its id anywhere
Learn to use Unobtrusive Javascript rather than polluting you mark up with behavior. Remove the onclick attribute and use $('#Roles').change(function() { $('form').submit(); });

Adding a record to the database based on input passed from a link on another form MVC 4

I have been using ASP.NET MVC 4 for a while but I have not yet come across a situation where i need to insert a value into the database from a scaffolded Create view which is based on a value passed from another view. I have tried to infer from the Edit view to try and modify my code to work but I have run into a snag. I got an error similar to this post. Here is my code from the view passing the value
#Html.ActionLink("Allocate", "Create", "Allocation", new { id=item.requestID}, null)
this is from the list of requests already in the database from the Index view
here is my code on the controller that is trying to force the Create method to use the ID passed from the link above
public ActionResult Create(int id = 0)
{
Request request = db.Requests.Find(id);
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", request.requestID);
return View(request);
}
then here is the posting code to the db
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Allocation allocation)
{
if (ModelState.IsValid)
{
db.Allocations.Add(allocation);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", allocation.requestID);
return View(allocation);
}
Basically what I am trying to do is allocate funds to a request made where by the allocation is entered into the db based on the request id. I am trying to prevent the user from choosing the request id from a drop down list. When I run this i get an error
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.Request_A52006F7570E0448EE323CB36858E4D13EED0BAD958340B32FF166708545DA8C', but this dictionary requires a model item of type 'BudgetAllocation.Models.Allocation'.
If theres anyone out there who can help me out with this please do as soon as you can. I appreciate all the effort offred!!!!!
//EDIT
Here is my Create view
#model BudgetAllocation.Models.Allocation
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Allocation</legend>
#Html.HiddenFor(model => model.requestID)
<div class="editor-label">
#Html.LabelFor(model => model.allocAmount, "Amount")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.allocAmount)
#Html.ValidationMessageFor(model => model.allocAmount)
</div>
<p>
<input type="submit" value="Allocate" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The problem is your view id strongly typed with BudgetAllocation.Models.Allocation while in get action of Create you are passing object of type BudgetAllocation.Models.Request thats why you are getting the exception.
You have to pass object of type BudgetAllocation.Models.Allocation in Create get action as well because you view is strongly typed to it.
public ActionResult Create(int id = 0)
{
Request request = db.Requests.Find(id);
return View(request) // <-------------- here is the mistake
}
it should return allocation object, something like this, it is just an example may be you need to do some other thing instead of selecting:
public ActionResult Create(int id = 0)
{
Allocation allocation = db.Allocations.Find(x=>x.requestID == id);
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", request.requestID);
return View(allocation);
}
UPDATE:
you simply need to do like this not return allocaiton object return simply View:
public ActionResult Create(int id = 0)
{
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", request.requestID);
return View();
}

Cascade Drop down list

I am trying to create a cascading drop down list, and I am getting an error.
This contains what I am try to do with in my controller class:
public ActionResult Create()
{
ViewBag.Status = new SelectList(db.pjt_Statuses, "pjt_Status_ID", "StatusName");
ViewBag.CategoryID = new SelectList(db.pjt_Categories, "pjt_Category_ID", "CatName");
return View();
}
public ActionResult SubCategory(int id)
{
var SubCategory = from s in db.pjt_SubCategories
where s.CategoryID == id
select s;
return Json(SubCategory.ToList());
}
// POST: /Project/Create
[HttpPost]
public ActionResult Create(pjt_Projects pjt_projects)
{
if (ModelState.IsValid)
{
pjt_projects.CreationDate = DateTime.Now;
db.pjt_Projects.Add(pjt_projects);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Status = new SelectList(db.pjt_Statuses, "pjt_Status_ID", "StatusName", pjt_projects.Status);
ViewBag.CategoryID = new SelectList(db.pjt_Categories, "pjt_Category_ID", "CatName", pjt_projects.CategoryID);
return View(pjt_projects);
}
View
This shows what I am try to do on the front end. I am getting a error with the bold line in my view code below. The error I am getting is:
There is no ViewData item of type IEnumerable<SelectListItem> that has the key pjt_SubCat_ID.
Code:
<div class="span3 offset1">
#Html.LabelFor(model => model.CategoryID, "Category")
#Html.DropDownList("CategoryID", String.Empty)#*, null, new { #onchange = "FetchSubCategories();" })*#
<br />
#Html.ValidationMessageFor(model => model.CategoryID)
</div>
<div class="span3 offset1">
<label>Sub Category</label>
#Html.DropDownList("pjt_SubCat_ID", String.Empty)
<br />
#Html.ValidationMessageFor(model => model.SubCategoryID)
</div>
Can someone advise me what is going wrong here?
The problem is that the engine is trying to get the SelectList from the viewbag and it cannot find it. I am guessing that you want the Dropdown to display the items you have stored in the viewbag under "pjt_SubCat_ID" but you are not adding that item to the viewbag anywhere in the code that you have listed in your question.
If that is supposed to be the data returned from the SubCategory() method, you will not find that data in the viewbag and you should have a javascript that calls this method on the click event of the original SelectList (category id one) and binds the returned data to the subcategory one.