Working on my first MVC 4 app, and I’m having a problem with my login screen. It is an intranet app for a company with common PCs on a manufacturing floor, so they need to be able to log in to the app on an individual basis. (The site is also in jQuery mobile and has a Javascript touch keyboard, but I don’t think they need be involved in this question)
The problem is that if someone fails to log in, the Javascript code that sets it so that enter key presses in the Username field send the focus to the Password field stops working.
I have a hack that works, (using setTimeout, 100) but it makes me feel icky and dirty.
Here’s my LogOn form:
@ModelType FAToolAdmin.LogOnModel
@Code
ViewData("Title") = "Log In"
End Code
@Using Html.BeginForm()
@<div class="content-primary">
<div data-role="header" data-position="fixed" data-theme="b">
<h2>Account Info</h2>
</div>
<fieldset>
<div class="editor-label">
@Html.LabelFor(Function(m) m.UserName)
</div>
<div class="editor-field">
@Html.EditorFor(Function(m) m.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(Function(m) m.Password)
</div>
<div class="editor-field">
@Html.EditorFor(Function(m) m.Password)
</div>
<p>
<input type="submit" value="Log In" class="notfocus"/>
</p>
@* This Html.ValidationSummary will show a ul element containing any login errors that were returned.
The call here has been edited to send an empty string, because sending 2 lines of text regarding
a login failure is a little excessive. You failed to login. Type everything again and move on
with your day.*@
@Html.ValidationSummary(True, "")
</fieldset>
</div>
End Using
<script type="text/javascript">
//This is the script that adds the keyboards to the screen.
$(function () {
//setTimeout(function () {
//This is the script that makes it so that when the enter button is pressed on the screen that the
//focus is changed to the password box (rather than having the form be submitted).
// set the focus to the UserName field and select all contents
$('#UserName').focus().select();
// bind the keypress event on the UserName field to this little piece of code here
var $inp = $('#UserName');
$inp.bind('keydown', function (e) {
var key = e.which;
// if this is keycode 13 (enter), then prevent the default form behavior and change focus to the Password box.
if (key == 13) {
e.preventDefault();
// find the Password input box and set the focus to it.
$('#Password').focus();
}
});
//}, 100);
});
</script>
and here’s my controller:
Imports System.Diagnostics.CodeAnalysis
Imports System.Security.Principal
Imports System.Web.Routing
Public Class AccountController
Inherits System.Web.Mvc.Controller
' GET: /Account/LogOn
<AllowAnonymous()>
Public Function LogOn() As ActionResult
Dim model As New LogOnModel
Return View(model)
End Function
'
' POST: /Account/LogOn
<AllowAnonymous()>
<HttpPost()>
Public Function LogOn(ByVal model As LogOnModel, ByVal returnUrl As String) As ActionResult
Try
If ModelState.IsValid Then
If Membership.ValidateUser(model.UserName, model.Password) Then
FormsAuthentication.SetAuthCookie(model.UserName, False)
If Url.IsLocalUrl(returnUrl) AndAlso returnUrl.Length > 1 AndAlso returnUrl.StartsWith("/") _
AndAlso Not returnUrl.StartsWith("//") AndAlso Not returnUrl.StartsWith("/\\") Then
Return Redirect(returnUrl)
Else
Return RedirectToAction("Index", "Home")
End If
Else
ModelState.AddModelError("", "The user name or password provided is incorrect.")
End If
Else
' if you just click on login without ever having entered anything into the username and password fields, you'll get this message.
ModelState.AddModelError("", "Please enter a valid user name and password.")
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
' If we got this far, something failed, redisplay form
Return View(model)
End Function
'
' GET: /Account/LogOff
<AllowAnonymous()>
Public Function LogOff() As ActionResult
FormsAuthentication.SignOut()
Session.Abandon()
Return (RedirectToAction("Index", "Home"))
End Function
End Class
and model:
Public Class LogOnModel
<Required()> _
<Display(Name:="SomeCompany Username")> _
Public Property UserName() As String
<Required()> _
<DataType(DataType.Password)> _
<Display(Name:="Password")> _
Public Property Password() As String
End Class
So my question is, how do I get it set up so that someone who types in a bad username (and hence triggers a ModelState.AddModelError and Return View(model)) is sent back to a version of the page where the enter press script has been loaded?
I feel like I’m missing something fundamental. Appreciate any help someone can give. Otherwise, setTimeout it is.
I think the jQueryMobile might be your problem – jqm uses ajax by default to submit all forms, so you’re not getting a full postback. I ran into a similar problem and that was the root cause.
Try using an overload for
Html.BeginFormand addnew {data_ajax="false"}to the HttpAttributes