0

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.

  1. 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.

  2. 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

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!");
        }
    };
    

1 Answer 1

1

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.

Regular Form Post

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.

Ajax Form Post

To use AJAX for the form post, there are some additional steps beside building forms like above.

  • Need to install package jquery-ajax-unobtrusive.
  • Need to specify ajax callbacks using data- on the form element.
  • Need to define the callback functions.

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>
}

My Taste

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.

Sign up to request clarification or add additional context in comments.

3 Comments

First off thank you for your post. I am having a heck of a time trying to get this to work. I installed the Microsoft.jQuery.Unobtrusice.Ajax nuget package but it doesn't seem to put any js files in my project. The samples I have looked up all show you needing to reference the script like this '<script src="~/lib/Microsoft.jQuery.Unobtrusive.Ajax/jquery.unobtrusive-ajax.min.js"></script>' The problem is that I don't see the scripts anywhere. So right now it is still doing a full post back and not hitting my js methods.
First, it looks like you grab the package using NuGet package manager. I used to do that too but with Core they separate the responsibilities - NuGet for server side packages, Bower/Nodejs(npm) for client side packages such as css and javascript. You might consider using npm instead. Secondly, in order to serve static files, they need to be under wwwroot folder. You have 2 choices: 1) directly download packages and put them into wwwroot, i.e., you can set the output path of your Bower package manager 2) download packages to whatever folders you want, and use Gulp to copy/move them to wwwroot.
I came to the same conclusion. I had to download the package manually, then manually move the JS files into my js folder under wwwroot. I appreciate the help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.