Client side Validation and ValidationSummary is working fine for my project (MVC4 + Razor + Unobtrusive JS) but the Server side errors are not shown on my view and if there was any client side error, it does not get removed from the view (it does from the ModelState). I have tried on both Chrome14 and IE9
Server side errors are being added to the model as ModelState.AddModelError(string.Empty, ModelState.AllErrors()); and shown to the view as #Html.ValidationSummary(false).
Edit
Simple form submit is working fine, it shows multiple error messages returned from the server and updates error messages, but, ajax based form submit is not working error messages returned by ajax based form submit are not shown at all.
Here is a sample demonstration of how the request is being made
#*... View contents related to Master Model ...*#
#using (Ajax.BeginForm("ActionToAddRecord", new AjaxOptions()))
{
#Html.Action("ActionToAddRecord")
<input type="submit" value="Add Record"/>
}
#*... View contents related to Master Model ...*#
ActionToAddRecord is a partial view representing a model contained by a master model
Errors are returned as
[HttpGet]
public ActionResult ActionToAddRecord()
{
return View();
}
[HttpPost]
public ActionResult ActionToAddRecord(childModel model)
{
ModelState.AddModelError(string.Empty, "First error message");
ModelState.AddModelError(string.Empty, "Second error message");
return View(model);
}
Edit
I saw a similar functionality in templated MVC application by VS2010, the dialog based log in form. Error messages are returned as Json and then JS is being used to display them, IMO, seems like MS made Ajax based requests quite easy and concise (Ajax.BeginForm) but missing the error handling part. Right now i am not willing to use JS for this, there might be a better way to get this type of error handling dealt automatically.
Solved, with a small error.
Master View
#*Master View Contents*#
#using (Ajax.BeginForm("AddPaymentCurrency", new AjaxOptions { UpdateTargetId = "paymentCurrency" }))
{
<div id="paymentCurrency">
#{Html.RenderPartial("PaymentCurrency", Model.PaymentCurrencyNew);}
</div>
}
PaymentCurrency View
#*Model Editors*#
#Html.ValidationSummary(false)
<input type="submit" value="Add Payment Currency"/>
<div id="paymentCurrencyList" style="width:inherit; height:auto; overflow:auto;">
#Html.Action("PaymentCurrencyList")
</div>
Controller
[HttpPost]
public ActionResult AddPaymentCurrency(PaymentCurrency model)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError(string.Empty, ModelState.AllErrors());
return View("PaymentCurrency", model);
}
//Add login
return View("PaymentCurrency", model);
}
public ActionResult PaymentCurrencyList()
{
//var list = getList
return View(list);
}
small error
ValidationSummary is shown with fields highlighted and asterisk shown against them while adding invalid payment currency. Once a valid currency is added then ValidationSummary and asterisk is no more shown on invalid payment currency, just fields are highlighted.
Please help me to get it fixed, i would prefer not to change the current structure, otherwise i will start getting big error
Related
I have a form with some controls. There is a button on the form which loads a partial view. Inside the partial view, there are two required field textboxes along with a button. And when its clicked, I need to display error messages only for textboxes which are inside the partial view, but not for the fields in the actual form. And when I click form's submit button, all error messages must show up.
After partial view is loaded, I am re-initializing the validation plugin as below.
$('#test').removeData("validator");
$.validator.unobtrusive.parse('#test');
I tried using validation attribute described in below thread but its not working. Maybe it works for normally loaded views.
ASP.NET MVC Validation Groups?
However, I can validate individually by calling textbox1.valid() and textbox2.valid(). But I think I am missing standard way of doing it. Any help is appreciated.
you can do this by submitting your partial view using Ajax.BeginForm()
//In Partail View
#model SomeModel
#using (Ajax.BeginForm("SomeActionName", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "targetId"}))
{
#Html.EditorFor(mode=>model.FirstText)
#Html.EditorFor(mode=>model.SecText)
<input type="submit" value="save">
}
//In Controller
public ActionResult SomeAction(SomeModel model)
{
return PartaiulView(model);
}
here you can validate your Partial View
NOTE: when you submit you form using Ajax.BeginForm you must specify "UpdateTargetId" where your result will be appear on View.
//In View
<div id="targetId">
#Html.Partail("PartialView")
</div>
OR if you want to Redirect to another action if your model is valid then modified your action
public ActionResult SomeAction(SomeModel model)
{
if(ModelState.IsValid)
{
return Json(new {redirect = #Url.Action("SomeAction","SomeController")})
}
return PartaiulView(model);
}
then in partail view you can invoke OnSuccess method of Ajax.BeginForm
#using (Ajax.BeginForm("SomeActionName", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "targetId",OnSuccess="success"}))
{
}
<script type="text/javascript">
function success(data)
{
if(data.redirect)
{
windows.location = data;
}
}
</script>
check both way which one is suitable to you.
I have a strongly typed razor view for a model in my MVC 3 project. Basically its for editing the model.
The model contains an Id field for the database key and some other string fields (Its a viewModel and all but thats not the point of the question).
In the view I just have a form and a submit button and nothing else. When the View is posted to the controller the model in the controller has all fields empty EXCEPT for the Id field which seems to have been auto-magically filled up.
How and where does the Id field gets populated in the model without there being a corresponding 'input' element for it in the view.
This is probably a dumb question but I would appreciate even just a link to what I should read up on. Thanks.
I bet it comes from the url as route parameter.
For example you have the following controller:
public class HomeController: Controller
{
public ActionResult Index(int id)
{
vqr model = GetModel(id);
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// the model.Id property will be automatically populated here
// because the request was POST /home/index/123
...
}
}
and the following view:
#model MyViewModel
#using (Html.BeginForm())
{
<button type="submit">OK</button>
}
Now you navigate to GET /home/index/123 and you get the following markup:
<form action="/home/index/123" method="post">
<button type="submit">OK</button>
</form>
Notice the action attribute of the form? That's where the id comes from. Basically the Html.BeginForm() helper uses the current url when generating the action attribute, and since the current url is /home/index/123 it is what gets used.
And because if you have left the default routes in your Global.asax, the {id} route token is used at the end of the url, the default model binder successfully binds it to the Id property of your view model.
You are probably hitting a URL similar to the following: /MyObject/Edit/15
This is then returning the page that you have your blank form on.
What happens next is you have an HTML.BeginForm() which is posting BACK to /MyObject/Edit/15
Now because of the post back having the same format your routing rules are picking up the '15' and binding it back to your id.
Have you added the ID field as a hidden field?
e.g.
#Html.HiddenFor(x=> x.ID)
I'm looking into using partial views in MVC3 using Razor, and I get my partial view to render and it works fine.
What I'd like to do, though, is refresh the parent view when the partial view is submitted.
Code in my parent view to render partial view
<div id="mydiv">
#{ Html.RenderAction("Add", "Request"); }
</div>
Action for parent view is simple,
public ActionResult Index()
{
List<obj> reqs = //some query
return View(reqs);
}
In my partial view's get action I have:
public ActionResult Add()
{
AddRequestViewModel vm = new AddRequestViewModel();
//set some stuff on the VM here
return PartialView(vm);
}
In the post action called by the partial view, if modelstate isn't valid, return PartialView(vm)
If it is valid, I'd like the parent and partial views to refresh.
I tried RedirectToAction, but this can't be called in an action called by a partial, apparently, and I tried return Index();, but this causes an issue with the code used to render the partial view,
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[DatRequests.Models.ReqRequest]', but this dictionary requires a model item of type 'DatRequests.ViewModels.AddRequestViewModel'.
Any suggestions on how to do this would be appreciated. The purpose of the page is to show a list of elements, and the partial contains a form to add a new element to the list.
Edit: The partial's model is different, as it contains data for selection, which is from a db, which is why I tried RenderAction, but I'm not sure if there are other ways of doing this.
When the partial view is submitted normally you submit it to some controller action. You could either submit it using a normal request or an AJAX request. If you use a normal request you could perform a standard redirect to the Index inside the POST controller action that will handle the form submission. If you use AJAX, you could return a JSON result pointing to the url that you want to redirect:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
return Json(new { url = Url.Action("Index") });
}
and inside your AJAX success callback:
success: function(result) {
if (result.url) {
// we have a success
window.location.href = result.url;
} else {
// invalid modelstate => refresh the partial
$('#mydiv').html(result);
}
}
Probably RenderAction should not be used this way.
When using Html.RenderAction, a new/seperate request would be sent to the server. And you got another chance to load some data from db or somewhere else to display to the client. Also, you could apply OutputCache to this action. this is usually the way doing global cache.
Here you are doing a POST to the server. Either directly put a element here or using a partial view to do the Post. And in the corresponding action, do a RedirectToAction.
Do it with ajax or not isn't the point. my opinion is more about the right way using RenderAction
I have layout with login partial view (username, password and submit button) and models ( some controls and submit button)with validation(server side and client side) displayed in normal views (#RenderBody() in layout).
My problem is when do server side validation in any of my views it also validate the login partial view because it execute the httppost function of the login. how can I stop that??
login view controller
[HttpGet]
public ActionResult LogOn()
{
return PartialView();
}
//
// POST: /Account/LogOn
[HttpPut]
public ActionResult LogOn(LogOnModel model)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
ViewBag.UserName = model.UserName;
}
else
{
ModelState.AddModelError("", Resources.Account.Account.LoginFailureText);
}
}
return PartialView(model);
}
and model controller
public ActionResult MyModel()
{
ViewBag.DisplayThxMsg = false;
return View();
}
[HttpPost]
public ActionResult MyModel(Models.FeedbacksModel feedback)
{
if (ModelState.IsValid)
{
//do something
}
else{
//do another thing
}
return View(feedback);
}
I find your question very difficult to understand. Im guessing your problem is you have a login partial control displayed as part of site layout and is shown on all pages. So while submitting any page, the username password validation kicks in, and you want to prevent that.
Understand that all validation # server - side happens while model binding, As properties are bound to the posted fields, the attributes on the fields are looked at and honored / catered to. So to prevent server side validation simply put the login partial view in it's own form so it is not sent while submitting other forms on the page.
In short have 2 forms - one form for login and one for feedback. Don't put all input fields in the same form.
If you still have validation errors after that, then it is because of other reasons like, type conversion problems. The default model binder will add some errors for basic type conversion issues (for example, passing a non-number for something which is an "int").The sample DataAnnotations model binder will fill model state with validation errors taken from the DataAnnotations attributes on your model.
EDIT
If you look at line number 125
#using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");}
You have the above code which will render the login form.
It will do so inside the other form at line 45
<form id="form1" runat="server" method="post">
This has no end tag therefore it will encompass the whole document till </html>
You should change the structure from
<form id="form1" runat="server" method="post">
#using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");}
</form
to
<form id="form1" runat="server" method="post">
</form>
#using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");}
This line #using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");} will render this form <form id="LoginView1" action="LogOn"> and all child elements of it.
LATEST EDIT
In your layout page use this :
#Html.Partial("~/Views/Shared/LogOnPartial.cshtml", new LogOnModel())
instead of this :
#Html.Action("LogOnPartial", "Account")
The reason why it all works is, the LogOnPartial method marked with [HttpPost] is called because the request was in a POST context. What you want is, You just need the view without the action executing even when POSTing. The above code does that. It renders the view without calling the action method. MVC3 is sort of a stupid servent : It only knows that it should call the Action method marked with [HttpPost] when the request is in a post context. It doesn't know that the request is in a post context for another action (index) and not this one (logonpartial). So now you can remove this method
public ActionResult LogOnPartial()
{
return PartialView();
}
which will no longer be used.
Note that you need to change the account controller's LogOnPartial Method to return
return RedirectToAction("Index","Home"); instead of return PartialView(model); on successful login. And on FAILURE you cannot render a partialview as you have coded. You must return an entirely new View. It must neither be index nor LogonPartails - just return return View("Login_Error_View"); which has its own layout. Otherwise it will be difficult to control the workflow.
I have a form in MVC 3 razor view that I am trying to post to my controller.
I need these:
1) Post the form to the controller action.
2) The action should do something with the data &return a string status (OK if success or NOK if failed)
3) Based on the result I might redirect the user after a brief delay.
4) I also want to prevent duplicate submission (if possible)
This is how my view looks (I trimmed it):
#model <MyNameSpace.Model>
#{
ViewBag.Title = "Save";
Layout = "~/Views/Shared/MyMaster.cshtml";
}
#using (Html.BeginForm("save", "my_controller"))
{
<div>
#Html.TextBoxFor(m => m.Host, new { #style = "width: 520px" })
... set other fields on the form ...
<input type="submit" id="btnSubmit" value="Submit"/>
</div>
}
This is my controller:
public String Save(<ModelName> model)
{
return "OK";
}
This seems working in IE9. But nothing happens in FF 4 or Opera. HttpFox shows no activity.
What is missing?
Thanks
In ASP.NET MVC it is considered good practice to have your controller actions return ActionResults instead of strings. This way proper content type headers will be set, etc...
So for example:
[HttpPost]
public ActionResult Save(ModelName model)
{
return Content("OK", "text/plain");
}
or if you wanted to return some view:
[HttpPost]
public ActionResult Save(ModelName model)
{
return View("Success");
}
Your example looks kosher, so either something critical is missing from your example code, or you need to view the generated HTML to see what's missing.
Did you omit the code that actually displayed the view to the user? I'm unsure of how this would function without a bit more. I mocked up your code and put in what I considered correct.
[HttpGet]
public ViewResult Save()
{
var vm = new ModelTest();
return View(vm);
}
[HttpPost]
public ActionResult Save(ModelTest model)
{
//do stuff with model
//Set a value in TempData -- Meets requirement of storing a status
TempData["Message"] = "OK";
//RedirectToRoute -- Meets requirement of preventing multiple posts partially. Some javascript will also help with this
RedirectToRoute("routename");
}
Post happened as expected in FF 4.01/5.0
Your model looks ok, it's just the controller code appears lacking.
Hopefully this helps.