Javascript doesn't execute in view after a con

2019-04-12 13:45发布

问题:

I'm having an issue where after logging out, I redirect to my site's login page. However, the login page's javascript is not executing at all unless I refresh.

I'm using the following link to call the LogOff action of the Account controller (this code is in a separate controller):

@Html.ActionLink("Logout", "LogOff", "Account", null, new { data_icon = "gear", @class = "ui-btn-right" })

The LogOff method in the Account controller looks like this, where I pass a logout parameter in order to detect in the Login view that I came here from a logout action:

public ActionResult LogOff()
{
    return Redirect(Url.Action("LogOn", "Account", new RouteValueDictionary(new { logout = true })));
}

Checking in firebug, I noticed that the javascript

$(document).ready(function () { ... });

used in my login page doesn't execute at all.

Furthermore, the login page's url reads "localhost:#####/Account/LogOff" rather than the "localhost:#####/Account/LogOn?logout=True" which I would have expected.

If I try to submit the login form afterwards, then I'm sent to a blank page with the url "http://localhost:#####/?logout=True"

So I would like to know what I'm doing wrong that is preventing the page from loading with its javascript, and how I can fix the issue.

Thanks!

Update: Here is my LogOn controller method:

public ActionResult LogOn()
{
    List<SelectListItem> cpList = new List<SelectListItem>();

    // Retrieve the list of central points from web.config
    NameValueCollection centralPoints = (NameValueCollection)ConfigurationManager.GetSection("CentralPointDictionary");

    foreach (string cp in centralPoints)
        cpList.Add(new SelectListItem { Text = cp as string, Value = centralPoints[cp] as string });

    // Check for a cookie containing a previously used central point
    string selectedCP = string.Empty;
    if (ControllerContext.HttpContext.Request.Cookies["LastCentralPoint"] != null)
        selectedCP = ControllerContext.HttpContext.Request.Cookies["LastCentralPoint"].Value;
    else if (cpList.Count > 0)
        selectedCP = cpList[0].Text;
    LogOnModel loginModel = new LogOnModel { CentralPointsList = new SelectList(cpList, "Value", "Text", selectedCP) };
    return View(loginModel);
}

And the body of my View:

<div data-role="page" id="page">
    <div data-role="header" data-position="inline">
        <div id="languageContainer" data-role="fieldcontain">
        <select id="languageSelect" data-mini="true" data-theme="b" data-overlay-theme="d" data-native-menu="false" data-inline="true">
            <option value="English">English</option>
            <option value="Français">Français</option>
        </select>
        </div>
    </div><!-- /header -->

    <div data-role="content" id="contentFrame" class="frame">
        <div id="controlFrame">
            @using (Html.BeginForm())
            {
            <div id="centralPoint" class="frame">
                    @Html.DropDownListFor(model => model.CentralPointAddress, Model.CentralPointsList, new { id = "centralPointSelect", name = "centralPoint", data_mini = "true", data_inline = "true", data_native_menu = "false" })
            </div>
                <div id="errorMsg">@Html.ValidationSummary()</div>
            <div id="credentialsFrame" class="frame">
                @Html.TextBoxFor(m => m.UserName, new { id = "userField", type = "text", name = "Username" })
                @Html.PasswordFor(m => m.Password, new { id = "passField", type = "password", name = "Password" }) 
            </div>
            <input id="loginBtn" type="submit" value="Login" />
            <div id="rememberMe" class="frame">
                        @Html.CheckBoxFor(m => m.RememberMe, new { @class = "custom", data_mini = "true" })
                        @Html.LabelFor(m => m.RememberMe)
            </div>
            <div id="forgotPassFrame">
                <input id="forgotPass" type="button" data-mini="true" value="Forgot password?" />
            </div>
            @Html.HiddenFor(m => m.Encrypted, new { id = "encrypted", value = "false" })
            }
        </div>
        @using (Html.BeginForm("SuccessfulLogin", "Account", FormMethod.Get, new { id = "successForm" }))
        {
            <input id="returnUrl" name="returnUrl" type="hidden" />
        }
    </div><!-- /content -->
 </div><!-- /page -->

回答1:

Indeed, the problem was related to jQuery Mobile. From the JQM documentation: http://jquerymobile.com/test/docs/api/events.html

The first thing you learn in jQuery is to call code inside the $(document).ready() function so everything will execute as soon as the DOM is loaded. However, in jQuery Mobile, Ajax is used to load the contents of each page into the DOM as you navigate, and the DOM ready handler only executes for the first page. To execute code whenever a new page is loaded and created, you can bind to the pageinit event. This event is explained in detail at the bottom of this page.

Only for me, "pageinit" wasn't working, but "pagechange" did the trick.

So what I wound up doing was the following: extracting my original code from $(document).ready() and inserting it into a separate function. It now looks like this:

$(document).ready(DocumentReady);

$(document).bind("pagechange", function () {
    // check if we just logged out to ensure we don't call DocumentReady() 
    // twice in succession
    if (window.location.href.indexOf("LogOff") > -1) {
        DocumentReady();
    }
});

function DocumentReady() {
    // some initialization code
});

However...

This solved the first problem of my javscript not executing, but my UI was not showing the changes made by the javascript.

As the JQM docs state, the whole app is working in a single document. So my LogOff controller method (which redirected to LogOn) was causing a new login page to be injected (or appended) into the existing page. Then when my javascript code would, say, access a text input field with $("#userField"), it was accessing the field from the page which loaded at the very beginning of the application, not the one being displayed after logging out!

What I figured out later is that changing the page location by assigning a value to window.location causes a full refresh. So when logging out, rather than call a controller method, I can just do this:

$("#logoutBtn").click(function () {
    window.location = $.mobile.path.parseUrl(window.location.href).domain + "/Account/LogOn";
});

Since this causes the page to refresh completely, the $(document).ready() event is fired, meaning that what I said previously about binding to "pagechange" or "pageinit" is no longer necessary, though still very good to know about when working with jQuery mobile. The refresh also loads up a brand new DOM, so I can be certain that my javascript will affect the elements displayed on the page.



回答2:

use RedirectToAction() instead of redirect().