Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC Ajax.BeginForm doesn't work

Tags:

asp.net-mvc

<script src="../../Scripts/MicrosoftAjax.debug.js" type="text/javascript"></script>

<script type="text/javascript">
    function loginOK()
    {
        var item = document.getElementById('statusLabel');
        item.innerHTML = "OK";
        document.getElementById('LoadImg').style.visibility = 'hidden';
    }

    function process()
    {
        var lab = document.getElementById('statusLabel');
        lab.innerHTML = 'Checking...';
        lab.style.color = 'Black';
        document.getElementById('LoadImg').style.visibility = 'visible';
    }

    function fail()
    {
        var lab = document.getElementById('statusLabel');
        lab.innerHTML = 'Login is being used';
        lab.style.color = 'Red';
        document.getElementById('LoadImg').style.visibility = 'hidden';
    }
</script>

 <div style="width:30%; float:left;">
     <label for="Login">Login:</label>
     <%= Html.TextBoxFor(model=>model.Login) %>
     <%= Html.ValidationMessageFor(model=>model.Login) %>

     <img id="LoadImg" alt="" src="../../Content/Images/ajax-loader.gif" style="visibility:hidden;"/>
     <br />
     <label id="statusLabel" />
     <br />
     <%=Ajax.ActionLink("CheckLogin","CheckLoginAvailability", "Account",
        new AjaxOptions { UpdateTargetId = "statusLabel", OnBegin = "process", OnFailure = "fail", OnSuccess="loginOK"})%>
 </div>

and, in the AccountController:

    [AcceptVerbs(HttpVerbs.Post)]
    public void CheckLoginAvailability(string login)
    {
        //do some job
    }

And, FireBug says that /Account/CheckLoginAvailability is not found. Also, after callback that ActionLink is hidden. Why ?

like image 559
Tony Avatar asked Oct 14 '22 00:10

Tony


1 Answers

You are talking about Ajax.BeginForm in your question but this is nowhere to be seen in the markup you provided. There are a couple of issues that I can see with your code:

  1. Your action method doesn't return an ActionResult. Yeah I know, you will say that this is possible, right, but that's against any good practices, conventions and rendering your controllers unit-test friendly.
  2. You are using Microsoft Ajax which will mix markup and javascript which IMHO is bad for multiple reasons: increasing bandwidth which of course leads to decreased performance, incapacity to externalize javascript into separate files in order to cache them by client browsers, having to write things like document.getElementById, innerHTML, style.color, style.visibility, etc... which is not guaranteed to work cross browser.

Here's what I would suggest you to improve this. While this doesn't answer your question, take it as an alternative approach.

As always the first thing to deal with is to define a model which in your case might look something like this:

public class LoginViewModel
{
    public string Login { get; set; }
}

Of course you might wish to add other fields such as Password, but this is out of scope for the moment. The next step is to write a controller dealing with this model (in parallel you should be already setting a unit-test for the future controller to prepare the ground):

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // Simply return the Login form
        return View(new LoginViewModel());
    }

    [HttpPost]
    public ActionResult Index(LoginViewModel model)
    {
        // Deal with the actual authentication, etc...
        throw new NotImplementedException();
    }

    [HttpPost]
    public ActionResult CheckLoginAvailability(LoginViewModel model)
    {
        // TODO: query your datasource to determine whether 
        // model.Login is taken
        // For this purpose we will suppose that it is taken
        bool isLoginTaken = true;

        // return a JSON object containing the result
        return Json(new { IsLoginTaken = isLoginTaken });
    }
}

The last part is to paint the screen:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<SomeNs.Models.LoginViewModel>" %>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Login</title>
    <!-- Use a separate CSS to avoid mixing markup with styling -->
    <link rel="stylesheet" type="text/css" href="<%: Url.Content("~/content/site.css") %>" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
    <!-- Always use HTML helpers when dealing with Urls -->
    <script type="text/javascript" src="<%: Url.Content("~/scripts/login.js") %>"></script>
</head>
<body>
    <% using (Html.BeginForm()) { %>
        <%: Html.LabelFor(x => x.Login) %>:
        <%: Html.TextBoxFor(x => x.Login) %>
        <%: Html.ValidationMessageFor(x => x.Login) %>
        <br/>
        <!-- Always use HTML helpers when dealing with Urls -->
        <img id="loadImg" alt="" src="<%: Url.Content("~/content/images/ajax-loader.gif") %>" style="display:none;" />
        <br />
        <div id="statusLabel"></div>
        <br />
        <!-- Give this link an id so that we can easily identify it from javascript -->
        <%: Html.ActionLink("CheckLogin", "CheckLoginAvailability", "Home", null, new { id = "checkLogin" })%>
        <input type="submit" value="Login" />
    <% } %>
</body>
</html>

And the last part is to unobtrusively attach our javascript (using jQuery of course) in the login.js file:

// When the DOM is ready
$(function () {
    // Attach a click handler to the checkLogin link
    $('a#checkLogin').click(function () {
        // When this link is clicked send an AJAX POST request
        // to the address this link is pointing to
        $.ajax({
            type: 'POST',
            url: this.href,
            // Pass as parameter in the POST body the login
            // entered by the user
            data: { login: $('#Login').val() },
            beforeSend: function () {
                // show the spinner image before sending any AJAX request
                // to inform the user of an ongoing activity
                $('#loadImg').show();
            },
            complete: function () {
                // hide the spinner image when the AJAX request completes
                // no matter if it succeeded or not
                $('#loadImg').hide();
            },
            success: function (result) {
                // if the AJAX request succeeds
                // query the IsLoginTaken property
                // of the resulting JSON object
                if (result.IsLoginTaken) {
                    // Show the status label with red if the login is taken
                    $('#statusLabel').html('Login is being used').css('color', 'red');

                } else {
                    // Show the status label in black if the login is not taken
                    $('#statusLabel').html('OK').css('color', 'black');
                }
            }
        });
        return false;
    });
});
like image 136
Darin Dimitrov Avatar answered Oct 18 '22 14:10

Darin Dimitrov