Reputation: 189
Let me start off by saying that I an new to Core 2.0 as well as MVC so please forgive my ignorance
I have used webforms for many years and would use AJAX to handle this, but I have had a hard time finding the answer for MVC.
I believe the issue I am having is that when I click the button on the page, that it is doing a full post and not using Ajax to partially post. The only reason this makes a difference is that on this login screen I have the login form fly down on page load.
<script type="text/javascript">
$().ready(function () {
setTimeout(function () { $('.card').removeClass('card-hidden'); }, 700)
});
</script>
If the user were to enter incorrect information and I have to display an error, the login form will once again fly in from the top.
So my question is two fold.
For starters is there any easy way to stop it from floating in on a postback? With webforms I could access the HTML directly in the code behind and remove the class before I return. Maybe I could do something like adding a state to my ViewModel and then I could switch the class based on that in Razer.
Alternatively it would be nice to be able to use AJAX like I am able to do in webforms as I am sure I will have additional more complex UI issues associated with page loading and current status of elements. Does anyone know of a good resource for this?
RECAP
Sample page: http://demos.creative-tim.com/material-dashboard-pro/examples/pages/login.html
Solution
First off a big thanks to David Lang for pointing me in the right direction. Between his post and the link I was able to work this out.
A word of caution, much of my issues were caused by a nuget package not installing correctly. The Microsoft unobtrusive Ajax installed, but because I didn't have a folder called scripts, it never put the JS files in my project. Once I got those in my project the rest became easier.
Start by adding the script for the jQuery unobtrusive to your page.
<script src="~/js/jquery.unobtrusive-ajax.min.js"></script>
Add the minimum form tags
<form asp-action="CheckPassword" data-ajax="true" data-ajax-method="post" data-ajax-success="onSuccess" >
In your controller check your data, and in my case return a viewModel (not a view)
[HttpPost]
public ActionResult CheckPassword([Bind("userName, password")] LoginVM loginModel)
{
if (loginModel.userName == "somename" && loginModel.password == "1234")
{
loginModel.invalidUser = false;
return Ok(loginModel);
}
else
{
loginModel.invalidUser = true;
return Ok(loginModel);
}
}
Create JS function to do something with the returned data.
var onSuccess = function (context) {
var errorMessage = $("#errorMessage");
errorMessage.html("");
if (context.invalidUser)
{
errorMessage.html("Invalid Credentials");
}
else
{
errorMessage.html("Valid user!");
}
};
Upvotes: 0
Views: 2470
Reputation: 21476
Since you're new to ASP.NET Core 2.0
, I am not going to explain things in details but I very want to encourage you to go research them and later on master them by yourself.
LoginViewModel.cs
namespace DL.SO.Project.Reporting.Accounts
{
public class LoginViewModel
{
[Required]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember my login?")]
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
}
Login.cshtml
@model DL.SO.Project.Reporting.Accounts.LoginViewModel
@{
ViewBag.Title = "Login";
Layout = "~/Views/Shared/_CenteredLayout.cshtml";
}
<div class="widget">
<form asp-area="" asp-controller="account" asp-action="login">
<input type="hidden" asp-for="ReturnUrl" />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username" class="required"></label>
<input type="text" class="form-control" asp-for="Username" autofocus="autofocus" />
<span class="form-text" asp-validation-for="Username"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="required"></label>
<input type="password" class="form-control" asp-for="Password" autocomplete="off" />
<span class="form-text" asp-validation-for="Password"></span>
</div>
<div class="form-check">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" asp-for="RememberMe" />
<span class="custom-control-indicator"></span>
<span class="custom-control-description">
@Html.DisplayNameFor(m => m.RememberMe)
</span>
</label>
</div>
<button type="submit" class="btn btn-primary btn-block">Login</button>
</form>
</div>
See the asp-controller
on the form element, and those asp-
attributes on inputs? They're built-in tag helpers from ASP.NET Core
.
For example, with asp-
attributes on form element, it will create a form with POST
method attribute and action attribute pointing to /account/login
. And hopefully you have an Account
controller Login
method to handle that request.
Optional: for client side validation to work, you need jquery-validation
and jquery-validation-unobtrusive
packages.
To use AJAX
for the form post, there are some additional steps beside building forms like above.
jquery-ajax-unobtrusive
.data-
on the form element.For example,
<form asp-area="" asp-controller="account", asp-action="login"
data-ajax="true" data-ajax-method="post" data-ajax-begin="onFormBegin"
data-ajax-complete="onFormComplete">
...
</form>
For a complete list of Unobtrusive ajax helpers, please see here: https://dotnetthoughts.net/jquery-unobtrusive-ajax-helpers-in-aspnet-core/
And then you need to define the callback functions.
@section scripts {
<script type="text/javascripts">
$(function() {
window.onFormBegin = function() {
...
};
window.onFormComplete = function(request, status) {
...
// if the status is not valid
// keep displaying the form?
// just an idea...
...
};
});
</script>
}
This is just my opinion. Whenever I deal with account login and other security related stuff, I tend to use regular form post instead of ajax. I don't want anything fancy there, because when something goes wrong, I want the server to give the user a fresh page, or redirect user to somewhere directly and immediately, instead of keeping the page that has invalid information and using ajax to communicate.
Again this is just my personal taste.
Upvotes: 1