This is probably again a newbie question.
When I create an ASP.NET MVC2 application, an Account Controller with an Action LogIn is created like this:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Now, I don't want to have a login page, I want to have login controls as part of a bigger page. So, I changed Login.aspx to Login.ascx and I am integrating it in my main view either with Html.RenderPartial or Html.RenderAction.
Both works like a charm if the login is successfull. If it is not, the
return View(model)
is killing me. What I want is to go back to my main page (call it Home/Index) but with the error information of the partial view.
return RedirectToAction("Index", "Home")
Obviously doesn't work.
Hints?
RenderAction method is used when some information is need to show on multiple pages. Hence partial view should have its own model.
To create a partial view, right click on Shared folder -> select Add -> click on View.. Note: If the partial view will be shared with multiple views, then create it in the Shared folder; otherwise you can create the partial view in the same folder where it is going to be used.
The major advantage of partial view is that we can reuse the partial view logic.
It does not require to have a controller action method to call it. Partial view data is dependent of parent model. Caching is not possible as it is tightly bound with parent view (controller action method) and parent's model.
This is certainly not a newbie question and I have combed up and down the web for an answer to this problem and so far the best solution I've found was kind of hidden in this tutorial here. This is what Darin Dimitrov was suggesting with the Ajax refresh. I'll summarize the important parts of that link and why this isn't easily fixed : /
Ajax refresh based on weird lover
The solution with the ajax refresh pretty much hinges on the following function (weird lover uses ControllerContext but it didn't exist for me so I have ControllerExtension):
ControllerExtension.RenderPartialViewToString(this,"mypartial", (object)model)
This function is what takes your model + modelstate and rerenders your partial view into an html string. You can then take that string and send it back in a json object to some javascript to refresh the view. I used jquery and it looks like this,
$(document).ready(function () {
var partialViewUpdate = function (e) {
e.preventDefault(); //no postback
var partialDiv = $(this).parent(".partial");
$.post($(this).attr("action"),
$(this).serialize(),
function (json) {
if (json.StatusCode != 0) {
// invalid model, return partial
partialDiv.replaceWith(json.Content);
}
else if (json.Content != null && json.Content != "") {
window.location.replace(data.Content);
};
});
$(".partial").find("form")
.unbind('submit')
.live("submit", partialViewUpdate);
};
Jquery explanation:
Why it doesn't "just work" normally
So the reason the partials' don't just work with modelstate validation is that you can't return View(model) with the POST because MVC will resolve that to the route address of the partial view (login.ascx) instead of where the partial is embedded (index.aspx).
You also can't use RedirectAction() because that will send it to (index.aspx) controller function, which is the equivalent of clearing everything and refreshing the index.aspx page. However if you use that ActionFilter suggested by Chino and Thabaza then when your page is refreshed and the login.ascx controller function is fired off again it will pick up that tempdata. This however doesn't work if refreshing the page causes a hassle with client-side code such as popup modals (i.e. if you refresh your popup is gone).
Say it aint so
I would prefer if it "just worked" so if anyone knows the correct/better way of doing this pleaaaase share it! I still feel that the Ajax refresh and ActionFilter solutions are not a clean way of doing it because it almost makes it look like partial views with forms are impossible to use without some sort of "trick".
Yeah redirecttoaction but provide an error message with it in the tempdata so you should do something like this
TempData["errorMsg"] = "incorrect values provided";
return RedirectToAction("Index", "Home")
Of course in the main index you should have a div that displays the message
<%= html.Encode(TempData["errorMsg"]) %>
EDIT I see you want to maintain the modelstate that might be a problem but what you could do is pass the modelstate in the index action or pass the modelstate object in the tempdata. What you can do then is check if a there are modelstate errors in the object and if there are check the field and add the error to the right field.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With