I have a page that I want to have a list of items (header items) with some details and a link to view and a link to edit them. In addition at the top of the list I want to have a form to get the basic details (start and end date) and create a new header item using that data.
What I've done:
created a header controller
created an index view (gets list of items)
created a startHeader view (has a form with start, end date and a button, form has an actionName of "addHeader")
added the "ValidationMessageFor" for the start and end date
added the startHeader view as a partial view to the index view
Everything generally appears to be working except for some validation. The required field validation appears to be working as expected (i.e. if you don't put in a start date and click the button it says "Start Date is required").
However I've added some additional validation that doesn't appear to be working:
added IValidatableObject interface to the header class
added validation code
if (this.StartDate > this.EndDate)
{
yield return new ValidationResult("Start Date must be prior to End Date.", new[] { "StartDate" });
}
when the button is pressed this code runs as expected
however it doesn't stay on the index screen and display the validation message
rather it goes through to the addHeader view
i had expected this to work the same as the required field validation and display the validation message
Is there something I'm missing here?
why is this validation running, but not displaying the error message?
do i need to add something to get this validation to run and remain on the page?
is there a better method of doing something like this?
It could be that your required field validation is displaying via Unobtrusive Client Validation (your model might include the data annotation [Required]). This validation runs on the client-side via JavaScript, and so doesn't post back to the server at all.
The validation code you have written, contrastingly, runs after the form has posted-back to the server. You will need to trap this in you addHeader() method. Something along these lines:
[HttpPost]
public ActionResult addHeader(addHeader model)
{
if (!ModelState.IsValid)
{
return View();
}
else
{
//Do Work to add your header...
return View("Index");
}
}
As long as you have #Html.ValidationSummary(true) in your view, then it will display the ValidationResult automatically. See this for more details.
Related
I have some strange errors in displaying model. I am creating something like chat. Users sending messages between yourself. When user selected one message in his inbox, and has clicked on the button "Answer", for example, form send submit(). Then information about selected message is displaying.
And in this moment I have a problem. If fields, that's displaying message properties are #Html.DisplayFor(), all works fine. Info of the message refreshed with change of selected message. But if properties displaying with #Html.TextAreaFor or Html.TextBoxFor it didn't happend with changing selected message.
And, if user clicked on "view" button, which display model in #Html.DisplayFor()
model displaying refresh, and refresh in #Html.DisplayFor() many many times. And as soon as click on "Answer" button, ie dipaly model in #Html.TextBoxFor(), model stopped to refresh display on changing selected message.
I drawed an image, for easier understanding. :)
Many Many Thanks!
I guess you are modifying the value you have bound the TextBoxFor on the HttpPost action:
[HttpPost]
public ActionResult SomeAction(MyViewModel model)
{
model.SomeProperty = "some new value";
return View(model);
}
and in the view you have:
#Html.TextBoxFor(x => x.SomeProperty)
If so, then this is by design. The HTML input helpers (such as TextBoxFor, TextAreaFor, CheckBoxFor, ...) are first looking in the ModelState when rendering their values and only after that in the model itself. So if you intend to modify some of the model properties in your POST action make sure that you remove it from the ModelState:
[HttpPost]
public ActionResult SomeAction(MyViewModel model)
{
ModelState.Remove("SomeProperty");
model.SomeProperty = "some new value";
return View(model);
}
I have the following model:
public class PersonListModel
{
....
[Required(ErrorMessage=AppConstants.MustSelectRecordToAttachMessage)]
public String SelectedPersonId { get; set; }
}
and the following view:
#using (Html.BeginForm("Attach", "Person", FormMethod.Post, new { #id = attachRecordFormId, targetDivId = personListId, #class = "inlineForm" }))
{
.....
#Html.HiddenFor(x => x.SelectedPersonId);
.....
<br />#Html.ValidationMessageFor(x => x.SelectedPersonId)
}
The hidden SelectedPersonId field is populated via some javascript hooked to the keyup event of one of the elements on my page.
My problem is that the required validation message shows immediately this partial view is displayed, not just when the form is submitted. It also displays again after the partial view is rendered again via an Ajax post.
I have very similar views that do not exhibit this problem, but 2 views (including the one above) that do exhibit this problem. I have been through a process of elimination to try to work out what is different between the views that work correctly and the 2 that exhibit this incorrect behavior, however I have not been able to locate the cause of the problem.
I presume that something is causing the unobtrusive validation to fire when the problem views are loaded. How can I track this down?
My problem is that the required validation message shows immediately this partial view is displayed
This could happen if the controller action that is displaying the view (containing the partial) takes the view model as argument:
public ActionResult Display(MyViewModel model)
{
... if this action is called with a GET request and you have missed
to pass a value for the "SelectedPersonId" query string parameter
you will get a validation error in the corresponding view
return View(model);
}
The reason for this happening is because your action is taking a model => the default model binder is kicking in attempting to populate your view model and when it attempts to set a value for the SelectedPersonId property it will automatically add a validation error to the model state if there's no corresponding value in the request because your model property is decorated with the [Required] attribute.
It also displays again after the partial view is rendered again via an Ajax post.
That's normal and could happen if the target POST action is taking your view model as argument and rendering a partial:
[HttpPost]
public ActionResult Process(MyViewModel model)
{
... if this action is called with a POST request and you have missed
to pass a value for the "SelectedPersonId" form parameter
you will get a validation error in the corresponding partial view
return PartialView(model);
}
I currently have a summary message at the top of my form, inviting my user to login. If the form does not validate on submit, I wish for this to then be replaced with the relevant error.
The only way I can think of is by hiding the initial text (perhaps in a #Html.Label) and then showing a #Html.ValidationSummary. However, I feel there is most likely a far more efficient way of doing this.
Can anybody help?
I would have an #Html.Label helper, and use the ViewBag object to pass data to it. That way in your controller when you test for ModelState.IsValid, if that is false you can set the ViewBag property so that it passes to the label helper.
Make sense? That's how I'd do it.
turn on the validation summary
#Html.ValidationSummary(true)
in your post ActionResult check the ModelState
[HttpPost]
public ActionResult Foo( Bar _bar){
if(ModelState.IsValid){
//noral course of action
return RedirectToAction("Foo"); //Redirect to the GET method
}
ModelState.AddModelError("", "|Your Message Here");
return View(_bar);
}
No, there is no problem with the approach.
I generally use single div with jquery for the stuff like form validation etc.
I create a single div on page where any kind of message can be displayed for user guideance.
ex:
if I've display some kind of information to user, it will be displayed as following:
div id='divMessage'</div>
$('#divMessage').html('Please fill the following form.').removeClass().addClass('msg');
While in case of some error same div will be used to display the error message:
$('#divMessage').html('some error occurred.').removeClass().addClass('errmsg');
I hope till will be of any use.
I'm using MVC3 (razor) and i'm trying to get the following working.
I have a list of snippets. These snippets have some general settings and then have a translation for an unknown ammount of languages.
Now i'm trying to do the following:
On the 'Create' page (url: Screen) of a snippet i set the general settings. under that there is a list of filled translations (empty at the start). When you press the 'Opslaan' button, i want the form to save the general settings and the list of translations.
When i push the 'Add' button i want to submit the complete viewmodel (settings + list of translations) to an other page where i can fill in a translation. After i filled in a translations, i want to return to this page (url: Screen). Here, a translation is filled in the list.
Now i'm doing someting wrong, because i cant get the viewmodel to submit to the 2nd page.
this is my code:
button 'add translation':
#Html.ActionLink("Add", "CreateTranslation", new { oSnippeteditviewmodel = this.Model }, null)
SnippetController:
public ActionResult Create()
{
SnippetEditViewModel oItem = new SnippetEditViewModel();
oItem.lSnippetsPerLanguage = new List<SnippetPerLanguageEditViewModel>();
return View(oItem);
}
[HttpPost]
public ActionResult Create(SnippetEditViewModel Snippeteditviewmodel)
{
if (ModelState.IsValid)
{
Snippeteditviewmodel.Bookmark = Snippeteditviewmodel.Bookmark.Replace(' ', '_');
_repoSnippet.CreateSnippet(Snippeteditviewmodel);
return RedirectToAction("Index");
}
return View(Snippeteditviewmodel);
}
public ActionResult CreateTranslation(SnippetEditViewModel oSnippeteditviewmodel)
{
return View(oSnippeteditviewmodel);
}
And in the controller, action CreateTranslation the object 'oSnippeteditviewmodel' stays null.
annyone who has a simular problem? Or a solution?
First, you should try to generate action link like this
#Html.ActionLink("Add", "CreateTranslation", this.Model, null)
In this case mvc will try to pass correctly serialized model values for your link and if your model is simple enough, CreateTranslations will get its model correctly. But, I would not do it that way. Generated link is static. What if user changes Snippet values on client side? When it comes to adding Translation, all the changed form values will be lost (Link will pass initial, server generated values). So, you should try one of the followings
Create the form with two buttons, one for CratingTranslation and one for Saving. When creating translation, dynamically change form's action and method parameters to GET the CreateTranslation action. This way, form will serialize all its current Snippet settings and pass to desired action, and you get the current snippet model passed to CreateTranslation action.
Use ajax. Just dynamically inject translation creation input fields into same page. That's simple and more user friendly (no bundle of navigations), and more http traffic is reserved (Passing all the translations and snippet to second page, and then returning all of these + 1 translation could get you in trouble). I would reccomend this approach. This is far more simple than first or your approaches.
I am not getting you properly but if you wanna add data by "create" controller then you don't need to specify object in "oSnippeteditviewmodel". You can get all form data by
Request.Form["controlName"]
and fill the Snippeteditviewmodel data member by above and save that.
I am submitting an ajax form and returning a partialresult to the view. However, when validation fails I only want to tell the user why it failed with a js method without reloading the model for the view. How do I specify what to return in the controller?
I'd return a error text/html and check for that on success of the jQuery call and if it exists then just alert it out.
What might be nicer is if you return an error partial view and display that as a modal div giving the user a nice looking error message rather than just an alert.
for that matter you could return html/text and place that within a div on the page and simply show the div modal or otherwise.
i hate the simple alert messages from j/script. grey and ugly with a horrible ding sound. but that's just me.
EDIT
I'm going to make the assumption that you are going to return a partial view representing the error. This is I think the easiest and the best way.
Create an error object somewhere. Below is a very simple object. Add properties as required.
public class MyErrorObj
{
public string errorText{get;set;}
}
Then in your controller you might have the following code. It assumes that you have a partial view called "ErrorDisplay" which inherits from MyErrorObj.
public ActionResult jQueryCheckError()
{
MyErrorObj errObj = new MyErrorObj { errorText = "error happened" };
return PartialView("ErrorDisplay", errObj);
}
Then in your view, not your partial view but your page;
$.post("/MyViewFolder/jQueryCheckError", { PARAMETERS GO HERE }, function(newErrorHTML) {
$(".MyErrorDivContainer").html(newErrorHTML);
$(".MyErrorDivContainer").html(newErrorHTML).slideDown(300);
});
The above posts to your controller action, then when it returns it uses the html that was returned and puts it into a div with a class name of "MyErrorDivContainer".
Now, you can check to see if the returned html is empty and then choose not to display the error message.
You could return a jSON object, itterate through the error collection and render the html to the same div if you wanted to. It's a little more involved but do-able.
Let me know if you want that code as well or if this is enough.
http://devlicio.us/blogs/billy_mccafferty/archive/2009/02/07/beware-of-asp-net-mvc-javascriptresult.aspx
public ActionResult DoSomething() {
script s = "$('#some-div').html('Updated!');";
return JavaScript(s);
}
But I'd suggest returning something like "Json(new { Type = "ValidationError", Errors = ModelState.SelectMany(x => x.Errors)})" - and process it on client side. Or, at least, wrap your returned JavaScript into common class, like "return new JavaScriptError(ModelState)".