I have an action that saves a record by calling my BLL entity's Save method. The entity takes care of its own internal validation and if a field is required but fails validation because a user didn't enter a value then the entity throws up an error. I'm catching that error in my action and returning the same view. The problem is the error isn't showing in my ValidationSummary.
Yes I realize I have view model validation by attibute with MVC but this entity is used elsewhere and must have redundant validation if the UI doesn't/can't do it, such as used in a batch service job.
Here is my action:
public ActionResult Edit(EntityModel model) {
if (ModelState.IsValid) {
var entity = new Entity(model.ID, model.Name, model.IsActive);
try {
entity.Save(User.Identity.Name);
return RedirectToAction("List");
}
catch (Exception ex) {
ModelState.AddModelError("", ex.Message);
}
}
return View(model);
}
Here is my View:
#model ELM.Select.Web.Models.EntityModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>DefermentTypeViewModel</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.IsActive)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.IsActive)
#Html.ValidationMessageFor(model => model.IsActive)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Why wouldn't the error I add to modelstate be shown in my validationsummary?
Change your View code:
#Html.ValidationSummary(true)
to:
#Html.ValidationSummary(false)
As per the MSDN Reference on ValidationSummary(), here is the method definition:
public static MvcHtmlString ValidationSummary(
this HtmlHelper htmlHelper,
bool excludePropertyErrors
)
Notice that the bool parameter, if you set it to true (like you originally did) you will exclude property errors. Change that to false and that should get you what you want.
Related
I am facing an issue when trying to create a reference of my model Restriction:
public int RestrictionID
public string portefeuille
public int AssetID
public int SegmentID
public int SubAssetID
public int Min
public int Max
public string logic_op
public virtual Asset Asset
public virtual Segment Segment
public virtual SubAsset SubAsset
That is instead of using the normal template in create view with #Html.EditorFor I am using a dropdown list with a script behind to fill the dropdown depending on the previous selected item that's work well, but when submitting nothing happen no error no redirecting, and of course the reference is not added to the database.
Here is my Create view:
<div class="editor-label">
#Html.LabelFor(model => model.portefeuille)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.portefeuille)
#Html.ValidationMessageFor(model => model.portefeuille)
</div>
#Html.DropDownList("Asset", ViewBag.AssetID as SelectList, "Select a Asset Class", new { id="Asset" })<br />
<select id="Segment" name="segment"></select><br /> //Here I am not using Html.EditorFor as EF does
<select id="subAsset" name="SubAsset"></select><br />
<div class="editor-label">
#Html.LabelFor(model => model.Min)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Min)
#Html.ValidationMessageFor(model => model.Min)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Max)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Max)
#Html.ValidationMessageFor(model => model.Max)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.logic_op)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.logic_op)
#Html.ValidationMessageFor(model => model.logic_op)
</div>
and my RestrictionController: ( I omitted useless parts)
public ActionResult Create()
{
ViewBag.AssetID = new SelectList(db.Assets, "AssetID", "Asset_Name");
return View();
}
//
// POST: /Restriction/Create
[HttpPost]
public ActionResult Create(Restriction restriction)
{
if (ModelState.IsValid)
{
db.Restrictions.Add(restriction);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.AssetID = new SelectList(db.Assets, "AssetID", "Asset_Name", restriction.AssetID);
return View(restriction);
}
Can somebody help to find where is the problem ?
Thank you for your help!
Name of the property in the model a should be same as name of the element in the html in order to bind values after the post back.That is why the code below
<select id="Segment" name="segment"></select><br /> //Here I am not using Html.EditorFor as EF does
<select id="subAsset" name="SubAsset"></select><br />
should be replaced by.
<select id="Segment" name="SegmentID"></select><br /> //Here I am not using Html.EditorFor as EF does
<select id="subAsset" name="SubAssetID"></select><br />
Always look at the HTML generated by any technology. This will help you to understand how the things are working.
Why is my #Html.ValidationMessageFo not working? When I run the application, nothing happens and it allows everything to be entered. And it also crashes when I try to edit an item in my edit view, which is below. I have the following:
<div class="editor-label">
#* #Html.LabelFor(model => model.Posted)*#
</div>
<div class="editor-field">
#Html.HiddenFor(model => model.Posted, Model.Posted = DateTime.Now)
#Html.ValidationMessageFor(model => model.sendinghome)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Cartypes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Cartypes)
#Html.ValidationMessageFor(model => model.Cartypes)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RegNum)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RegNum)
#Html.ValidationMessageFor(model => model.RegNum)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Regprice)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Image)
#Html.ValidationMessageFor(model => model.Regprice)
</div>
Here is how validation works.
Let's say you have the following model:
public class MyModel {
[Required]
public string MyProperty { get; set; }
}
Note the Required attribute, it is a data annotation attribute that specifies that MyProperty is a required field.
MyModel is used by the following view (MyView.cshtml):
#model MyNamespace.MyModel
#using (Html.BeginForm("MyAction", "MyController")) {
#Html.LabelFor(m => m.MyProperty)
#Html.TextBoxFor(m => m.MyProperty)
#Html.ValidationMessageFor(m => m.MyProperty)
<input type="submit" value="Click me">
}
Then, when this form gets posted to the MyAction action of MyController, the validation of your model will be performed. What you have to do is check whether your model is valid or not.
It can be done using the ModelState.IsValid property.
[HttpPost]
public ActionResult MyAction(MyModel model) {
if (ModelState.IsValid) {
// save to db, for instance
return RedirectToAction("AnotherAction");
}
// model is not valid
return View("MyView", model);
}
If the validation failed, the view will be rendered again using the different errors that are present in the ModelState object. Those errors will be used and displayed by the ValidationMessageFor helper.
Exactly, Bertrand explains it right, you could also use jquery validation too and eliminate the calls to the server validating on the browser. (asp.net mvc takes care of validating the rules on your model automatically)
I have two edit Action methods, one for HttpGet and one for HttpPost. The HttpGet method takes an id parameter, retrieves the appropriate object, and displays it for editing. The HttpPost method takes a parameter that should be the edited object; however, the ids do not match. Why is that mismatch occurring? I've included the code for my Edit.cshtml view, and for my two Action Methods.
The View:
#model WebApplicationPbiBoard.Models.Sprint
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Sprint</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.Start)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Start)
#Html.ValidationMessageFor(model => model.Start)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.End)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.End)
#Html.ValidationMessageFor(model => model.End)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
The Action Methods:
//
// GET: /Sprint/Edit/5
public ActionResult Edit(int id)
{
var sprint = Db.GetById(id);
return View(sprint);
}
//
// POST: /Sprint/Edit/5
[HttpPost]
public ActionResult Edit(Sprint editedSprint)
{
if (ModelState.IsValid)
{
Db.Save(editedSprint);
return RedirectToAction("Index");
}
else
return View(editedSprint);
}
Here's the GetById method. It's pretty much a wrapper around the NHibernate ISession.GetById method.
public T GetById(int id)
{
return Session.Get<T>(id);
}
Here's my solution. Thanks to everyone who helped me with debugging it.
The problem was that, to adhere to NHibernate best practices, the setter for my id property was private. That meant that the controller couldn't set it in the reconstructed model object, so the id was the default value of 0. To solve the problem, I passed the id in as a parameter to the controller and found the right object in my repository, which I then manually updated. Here's the code:
[HttpPost]
public ActionResult Edit(int id, DateTime start, DateTime end)
{
//get the right model to edit
var sprintToEdit = Db.GetById(id);
//copy the changed fields
sprintToEdit.Start = start;
sprintToEdit.End = end;
if (ModelState.IsValid)
{
Db.Save(sprintToEdit);
return RedirectToAction("Index");
}
else
return View(sprintToEdit);
}
I have a model with this property:
[AllowHtml]
[DisplayName("Widget for Table")]
[StringLength(1000, ErrorMessage = "Maximum chars 1000")]
[DataType(DataType.Html)]
public object TableWidget { get; set; }
And here is the create methods in controller:
//
// GET: /Admin/Table/Create
public ActionResult Create(int id)
{
Season season = _seasonRepository.GetSeason(id);
var table = new Table
{
SeasonId = season.SeasonId
};
return View(table);
}
//
// POST: /Admin/Table/Create
[HttpPost]
public ActionResult Create(Table a)
{
if (ModelState.IsValid)
{
_tableRepository.Add(a);
_tableRepository.Save();
return RedirectToAction("Details", "Season", new { id = a.SeasonId });
}
return View();
}
And last here is my view:
#model Stridh.Data.Models.Table
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name) #Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TableURL)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TableURL) #Html.ValidationMessageFor(model => model.TableURL)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SortOrder)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SortOrder) #Html.ValidationMessageFor(model => model.SortOrder)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TableWidget)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TableWidget) #Html.ValidationMessageFor(model => model.TableWidget)
</div>
<div class="editor-label invisible">
#Html.LabelFor(model => model.SeasonId)
</div>
<div class="editor-field invisible">
#Html.EditorFor(model => model.SeasonId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I add a "normal" message without html everything is saved OK, but when saving it says A potentially dangerous Request.Form...
Another strange thing is that I got this [AllowHtml] to work in another model class. I cant find why this is causing me troubble. Need your help. :-)
The way you are using AllowHtml should work. Make sure that you are not accessing the HttpRequest.Form collection anywhere else in your code (controller, filter, etc) as this will trigger ASP.NET Request Validation and the error you are seeing. If you do want access to that variable then you should access it via the following code.
using System.Web.Helpers;
HttpRequestBase request = .. // the request object
request.Unvalidated().Form;
I get the same problem and i solve it with the help of this post.
If you are on .net 4.0 make sure you add this in your web.config
<httpRuntime requestValidationMode="2.0" />
Inside the <system.web> tags
I had the same problem. My model class is named "GeneralContent" and has the property "Content". In my action method i used attribute like this:
public ActionResult Update(GeneralContent content)
when i renamed content argument to cnt, everything works well. I think MVC is confused when some attribude of model class has the same name as the argument in action method.
I also had this issue. I could not get a model property marked with [AllowHtml] to actually allow HTML, and instead encountered the same error you describe. My solution ended up being to mark the Controller action that accepts the posted model with the [ValidateInput(false)] attribute.
The answer that #marcind put me on the right track but my issue was that I was passing the FormCollection into the Controller method, so changing this...
public ActionResult Edit(MyClass myClass, FormCollection collection)
To this...
public ActionResult Edit(MyClass myClass)
Solved the problem.
Subsequently, I was able to access the heck out of the form collection with code like this without issue.
foreach (var key in Request.Form.AllKeys)
{
...
}
So, it was the passing the form collection parameter that caused the problem, not merely accessing the form collection.
If my model Product has a member ICollection<ProductOption> how do I rebuild my Product with the member collection in my Controller.Edit(...) method on post back from Edit?
(We can assume we never add or remove an option, only ever edit.)
Razor:
#model Models.Products.Product
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_GlobalLayout.cshtml";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
#Html.HiddenFor(model => model.Id)
<legend>Product</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
#foreach (ProductOption option in Model.Options)
{
<div style="border:1px solid black; margin: 5px; padding:5px 7px;">
#Html.Partial("_ProductOptionInput", option)
</div>
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
controller:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var p = _repository.ById(id);
UpdateModel<Product>(p);
_repository.SaveChanges();
return RedirectToAction("Index");
}
partial view:
#modelModels.Products.ProductOption
#Html.ValidationSummary(true)
<fieldset>
<legend>ProductOption</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.Term)
</div>
</fieldset>
UPDATE
My FormCollection in ActionResult Edit(int id, FormCollection collection) is essentially a dictionary, so it has the ProductOption values for one of the updated ProductOptions but not the rest of them as the keys (ie the ProductOptions property names) can't be repeated in the dictionary.
You either need to write your own model binder (either for ICollection<ProductOption> or one that pulls your entity out of the database instead of instantiating a new instance), or you can NOT take the model in as a parameter, and instead, pull it out of the database in your action method, then call TryUpdateModel from the controller.
HTH
I added the following in the Razor view and it works like a charm!
#Html.EditorFor(model => model.Options.ToList()[0], templateName: "ProductOptionInput", htmlFieldName: "Options[0]")
#Html.EditorFor(model => model.Options.ToList()[1], templateName: "ProductOptionInput", htmlFieldName: "Options[1]")