ASP.NET MVC 3 Razor - Sign Up and Login Form on the same page/view - asp.net-mvc-3

I am using ASP.NET, MVC3, Razor. In my site, I have login form on all pages (in the top header) from where the user can log in at anytime. The issue arises when another page with another form comes. I am not able to figure out how to implement that. I have tried a lot , but I think I need a very small example to solve my problem. The example can be as simple like a registration form and login form on the same page and they work separately, all their validations and other stuff. The login form and registration form can be partial views or anything that some one can suggest which is reusable.
Here's the code of _Register.cshtml(shared view)
#model MVCRnD.Models.RegisterModel
#using (Html.BeginForm("Register","Account")) {
}
Code for about.cshtml on which the above shared view was placed
#Html.Partial("_Register")
Code for Account Controller
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
The partial view i.e. registration form is rendering fine. But when I click on submit button, it posts the whole form but take me to the register /Account/Register which is actually correct. But I want to be on same page and does not go to the other form. Do you want me to send the full code ? I can upload it somewhere and send you the link.

Not sure why you're having problems, but putting two forms on the same page is a very common practice regardless of what server-side language you use.
#using (Html.BeginForm("Logon", "Account")) {
...
}
#using (Html.BeginForm("Register", "Account")) {
...
}
Which translates to:
<form action="/Account/Logon" method="post">
...
</form>
<form action="/Account/Register" method="post">
...
</form>

Related

Validate only ajax loaded partial view

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.

Partial View HttpPost invoked instead of HttpGet

I'm working on the admin part of an MVC webapp. I had the idea to use "widgets" for a single Admin panel. I'll explain my intentions first.
I have a languages table, and for that I'd like to create a partial view with a dropdownlist for those languages and a single button "Edit", that would take the user to a non-partial view to edit the language. After clicking save, the users would be redirected to the Index view, which would just show the dropdownlist again.
So I have a "Index.cshmtl", and an "EditLanguage.cshtml" as non-partial views, and a "LanguageWidget.cshtml" as a partial view.
First the user sees the Index view.
public ViewResult Index()
{
return View();
}
This view has the following code in it:
#using CodeBox.Domain.Concrete.ORM
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Administration</h2>
#Html.Action("LanguageWidget")
The Partial view "LanguageWidget" just contains the following code, and when the user submits it posts to the HttpPost annotated method in my controller:
#using (Html.BeginForm("LanguageWidget", "Admin"))
{
#Html.DropDownListFor(model => model.SelectedItem, Model.Languages)
<input type="submit" value="Edit"/>
}
This is the HttpPost method for the widget:
[HttpPost]
public ActionResult LanguageWidget(LanguageWidgetModel model)
{
var lang = langRepo.Languages.FirstOrDefault(l => l.LanguageId == model.SelectedItem);
return View("EditLanguage", lang);
}
This takes the user to the language edit page, which works fine.
But then! The user edits the language and submits the page, which invokes the "EditLanguage" HttpPost method, so the language is saved properly.
[HttpPost]
public ViewResult EditLanguage(Language model)
{
if (ModelState.IsValid)
{
langRepo.SaveLanguage(model);
TempData["message"] = string.Format("{0} has been saved!", model.Name);
return View("Index");
}
else
{
return View(model);
}
}
So, when I return the "Index" view - which seems logical I guess - the controller still assumes this is a HttpPost request, and when it renders the Index view, it invokes the "LanguageWidget" method, assuming it has to render the HttpPost method.
This leads to the LanguageWidget HttpPost method, which returns a full view with layout, returning just that, so I have my layout, with view, containing a layout, with the editview.
I don't really see how I could fix this?
I'm pretty sure it's a design flaw from my part, but I can't figure it out.
Thanks in advance!!
Consider using:
return RedirectToAction("Index")
instead of:
return View("Index");
It might seem more logical if the user is actually redirected to Index instead of
remaining at the EditLanguage. And if the user hits the refresh button no data will be resent using this approach.

How to display ValidationSummary using multiple forms using MVC

I have looked at many of the solutions on offer on this site and others for my problem but none of them seem to work perfectly for my solution.
On my Layout page I have a login area on the top of the screen. This is always present unless a user is logged in. This login form has a ValidationSummary on it and every time I post back using another form on the site the validation for this login form is being triggered.
I'm almost certain that this is down to how I call this login page from my Layout page. It is not a partial view that is in a Shared folder, it is in an Area in my project. On my Layout page I call the login form like this:
#Html.Action("LogOn", "Account", new { area = "Store" })
The logon page contains the following:
#model Project.ViewModels.LogOn_ViewModel
#{
Layout = null;
}
#using (Ajax.BeginForm("LogOn", "Account", new { #area = "Store" }, new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "LoginContainer", LoadingElementId = "actionLoaderImage" }, new { id="LogonForm" }))
{
#Html.AntiForgeryToken()
<div class="loginArea">
<div class="notRegistered">
<h4>Not registered yet?</h4>
Register now<br/>
#Html.ActionLink("Register", "Index", "SignUp", new { area = "Store" }, new { #class = "greenButton" })
</div> <!-- /notRegistered -->
<div class="loginForm">
<div class="formFields">
<label class="inline">#Html.LabelFor(m => m.UserName)</label>
#Html.TextBoxFor(m => m.UserName)
<label class="inline">#Html.LabelFor(m => m.Password)</label>
#Html.PasswordFor(m => m.Password)
<input type="submit" name="LogIn" value="Log in" class="blueButton" />
<img id="actionLoaderImage" src="#Url.Content("~/Content/web/images/loader.gif")" alt="icon" style="margin-right:15px; display:none;" />
#Html.ValidationSummary()
</div>
</div> <!-- /loginForm -->
</div> <!-- /loginArea -->
}
The login controller is standard stuff:
// GET: /Account/Logon
public ActionResult LogOn()
{
// if logged in show logged in view
if (User.Identity.IsAuthenticated)
return View("LoggedIn");
return View();
}
// POST: /Account/Logon
[HttpPost]
public ActionResult LogOn(LogOn_ViewModel model)
{
if (ModelState.IsValid)
{
if (SecurityService.Login(model.UserName, model.Password, model.RememberMe))
{
return View("LoggedIn");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return PartialView(model);
}
I suspect that what is happening here is that Html.Action is 'posting' the login form if a post is occurring elsewhere on the page. This makes sense as the layout page itself would be posted as part of a form post action.
I tried implementing the custom Validator examples from some other SO questions and blogs (http://blogs.imeta.co.uk/MBest/archive/2010/01/19/833.aspx) but I found that using those examples would not display the validation summary with client side validation which is not much use to me.
The solution I am looking for would need to allow both client and server side validations to appear for the correct form. Does anyone have an example of something like this using MVC? I'd like to avoid manually looking after the client side validation using jquery if possible and just to use the framework to handle the work for me.
Thanks for your suggestions,
Rich
After playing around with this issue for longer than I care to admit, the answer as always was simple once you know how!
The solution to this issue for anyone else who comes up against it is to rename your action so your POST action is a different name to your GET action. This way, when the Layout page is posted back and triggers the PartialView to also be posted back there is only a GET action for your PartialView so the ValidationSummary will not be triggered.
Based on my example above I have altered the form to postback to another action for my login partial view and this has solved this issue for me.
In my Layout page the link to the partial in the store area remains the same:
#Html.Action("LogOn", "Account", new { area = "Store" })
The logon form action is then altered to point to a new action - not called the same name as the GET action:
#using (Ajax.BeginForm("ConfirmLogon", "Account", new { #area = "Store" }, new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "LoginContainer", LoadingElementId = "actionLoaderImage" }, new { id="LogonForm" }))
{
....
}
Then I just renamed my controller action so that it has a different action name for the POST action so that when the layout page is called using a post and the partial is loaded up with a POST action that it doesn't call my logon POST action:
// POST: /Account/ConfirmLogon
[HttpPost]
public ActionResult ConfirmLogon(LogOn_ViewModel model)
{
if (ModelState.IsValid)
{
if (SecurityService.Login(model.UserName, model.Password, model.RememberMe))
{
return PartialView("LoggedIn");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return PartialView("Logon",model);
}
Hopefully this will help some others out with this issue. Seems so obvious now but drove me nutty :D

Creating a ajax login dialog box with MVC 3 and jQueryUI

I'm using Asp.Net MVC 3 (with Razor) and jQuery/jQueryUI to create a dialog login box.
I'm still quite new to these technologies and have run into a problem.
The login form is in a partial view, I use the following jQuery code to load it into the page:
$('#log-in').click(function () {
if (ServerModel.UserId == 0) {//User not logged in, open login dialog
$("<div></div>")
.addClass("dialog")
.addClass("form-dialog")
.attr("id", "login-dialog")
.appendTo("body")
.dialog({
title: 'LOGIN',
close: function () { $(this).remove() },
modal: true,
width: 323,
resizable: false
})
.load(ActionUrls.LogOn);
}
});
ActionUrls.LogOn, has the path for the Logon action method in the controller.
The partial view with the login form looks along these lines:
#using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "login-dialog" }))
{
//Login form fileds in here with submit button at the end
}
Here's the code for the controller:
[HttpGet]
public ActionResult LogOn()
{
return PartialView("_Logon");
}
[HttpPost]
public ActionResult LogOn(LogOnModel model)
{
if (ModelState.IsValid)
{
CustomSqlMembershipProvider provider = new CustomSqlMembershipProvider();
if (provider.ValidateUser(model.UserName, Security.ComputeHash(model.Password)))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// something failed, redisplay partial view with the model
return PartialView("_Logon", model);
}
This all works fine, however the problem I'm having, is that when the user is authenticated, as can be seen in the controller code. I try to RedirectToAction("Index", "Home"), this with the intent that the page should reload with the user logged in and the dialog closed.
However at the moment, just the login dialog box reloads with the whole page content in it. I understand that this is probably normal behavior, as I'm telling the form in the logon view to update the UpdateTargetId of the dialog box.
So the question is, can I reach my desired result of the whole page reloading and if so, how??
Any help and hints would be most appreciated.
/Ola
Instead of using a form parameter you can use jquerys ajax method and send in the username and password and in the success function of the ajax method if the user is successfully authenticated redirect the user to your index page.
note the index page should still have a way to determine that the user is authenticated or not else anyone can typein the url.
What do you think?
On your .load ajax call, the data that is returned contains redirect information. I'm not sure if this is only for JSON POSTS, but it's worth a try:
...
.load(ActionUrls.LogOn, function(data) {
if (data.redirect) {
window.location.href = data.redirect;
else {
// other
}
});
Update:
The callback event that you need to hook onto is the one that submits the login information, not the one that loads the login page. I asked you to hook onto the wrong ajax call on top, the one you need to latch onto is this one:
#using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "login-dialog" }))
{
//Login form fileds in here with submit button at the end
}
Unfortunately, I do not know how to hook onto that event. This article may help:
How to get an ASP.NET MVC Ajax response to redirect to new page instead of inserting view into UpdateTargetId?
I noticed it's an old question, but I hope my reply may help to someone.
For something similar I redirected to a 'helper' page in the controller action, which contained (for me almost) only some JavaScript in the scipt tag (or you should link it of course).
In this case that's enough:
window.location.href = "http://www.foo.com";
...and I also find and close the dialog there:
$('#login-dialog').dialog('close');
For me it was well, but I also appreciate better solutions.

server side validation in the main view cause also validation in partial view, how to stop that?

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.

Resources