如何在MVC3的Ajax.ActionLink的OnBegin和OnComplete事件中使用$(this)?

10
我的控制器创建了一个链接列表,像这样:
<ul id="MainMenu">
@foreach (var item in Model.MenuItems)
{
    <li>@Ajax.ActionLink(item.Caption,
    item.ActionName,
    item.ControllerName,
    new AjaxOptions
    {
        UpdateTargetId = "pageBody",
        OnBegin = "BeginUpdatePage",
        OnComplete = "EndUpdatePage",
        OnFailure = "FailUpdatePage"
    })
    </li>
}
</ul>

我有一些像这样的JavaScript代码

function BeginUpdatePage() {
 $("#MainMenu li").removeClass('selected');
 $(this).parent().addClass('selected');
 $("#pageBody").fadeTo('slow', 0.5);
}

function EndUpdatePage() {
 $("#pageBody").fadeTo('slow', 1);    
}

function FailUpdatePage() {
 alert('There has been an error loading this page please try again.');
}

在BeginUpdatePage函数中,第1行和第3行正常执行,但是第2行"$(this).parent().addClass('selected');"没有明显的作用......(我已在我的css中声明了该类)。

我查看了Jquery文档(Jquery.ajax()),它说"this"是被点击的元素。我还检查了默认情况下存在于我的项目中的"jquery.unobtrusive-ajax.js"文件。该文件在执行我的函数时也会传递"this"。

"result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this,arguments);"

我在这里做错了什么...我看过其他使用这种技术的示例,但我一整天都失败了!

如何在我的Begin和complete函数内访问被点击的元素。


1
你是否使用Firebug检查过是否真的没有添加“selected”类? - Edgar Villegas Alvarado
Microsoft AJAX与JQuery不同,所以我认为您参考了错误的文档。 - Wizard of Ogz
检查Firebug是必须的。您可以在回调脚本上设置断点并在控制台中检查this - Wizard of Ogz
@Wizard of Ogz - 它绝对没有添加类。Microsoft Ajax 扩展了 jQuery。它使用 Jquery.Ajax() 来发送请求。如果你查看 "jquery.unobtrusive-ajax.js",你会看到 "function asyncRequest(element, options)" 这是它调用你分配给 onBegin 属性的函数的地方...现在我一直在看它,我可以看到它将 "this" 设置为 XHR。 - MichaelLake
@MichaelLake - 我应该更好地表达我的评论。我想确保 AjaxOptions 回调函数(OnBegin、OnComplete、OnFailure)传递的参数与您在 jQuery 函数中期望的参数相同。干杯! - Wizard of Ogz
这是一个老问题,但现在已经发布了一个真正的修复程序,在不显眼的Ajax 3.1中(截至2014年1月17日),您应该考虑将所选答案更改为Yarx的:) - iCollect.it Ltd
5个回答

10

更新于2014年1月20日:官方的3.1版本不显眼的ajax脚本包含了此更改,并与MVC 5.1发布同时发行(1月17日)。去查看一下吧 :-)

原始回复:

我之前发现了这个,可以实现您想要的结果。非常容易实现。

在jquery.unobtrusive-ajax.js文件的大约100行左右,添加这一行代码。

options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

options.context = element; // <--- Add this line

method = options.type.toUpperCase();
if (!isMethodProxySafe(method)) {
    options.type = "POST";
    options.data.push({ name: "X-HTTP-Method-Override", value: method });
}

就这样,现在你应该能够使用this来引用源元素了。


感谢您对此事的跟进。我们计划很快查看一下。 - Yishai Galatzer
我看到这个今天被更改了。感谢更新!!期待正式发布 :-) - Nick Albrecht
+1:刚刚更新到3.1版本,this现在是UI中被点击的项目。太棒了!感谢您告诉我们这个变化。现在应该将其作为正确答案。 - iCollect.it Ltd

4
您可以尝试像这样传递它:
OnBegin = "function() { BeginUpdatePage(this); }"

然后:

function BeginUpdatePage(element) {
    $("#MainMenu li").removeClass('selected');
    $(element).parent().addClass('selected');
    $("#pageBody").fadeTo('slow', 0.5);
}

或者只是使用jQuery,而不需要任何Ajax.*的东西:

<ul id="MainMenu">
    @foreach (var item in Model.MenuItems)
    {
        <li>
            @Html.ActionLink(
                item.Caption,
                item.ActionName,
                item.ControllerName,
                null,
                new { @class = "someLink" }
            )
        </li>
    }
</ul>

然后:
$(function() {
    $('.someLink').click(function() {
        var link = $(this);
        $.ajax({
            url: this.href,
            beforeSend: function() {
                $("#MainMenu li").removeClass('selected');
                $(link).parent().addClass('selected');
                $("#pageBody").fadeTo('slow', 0.5);           
            },
            complete: function() {
                $("#pageBody").fadeTo('slow', 1); 
            },
            success: function(result) {
                $('#pageBody').html(result);
            },
            error: function() {
                alert('There has been an error loading this page please try again.');
            }
        });
        return false;
    });
});

感谢您在这里的输入,我尝试为您投票,但是我还没有足够的声望 :-( 所以我会在这里发表我的评论。谢谢您的帮助,您的解决方案本来可以起作用,但我想使用内置的东西使其工作。 - MichaelLake

3
感谢大家提供的所有建议,Darin,虽然您的解决方案完美运行,但我想尝试使用内置的Ajax工具。我在查看Jquery.Ajax()文档和检查“jquery.unobtrusive-ajax.js”后找到了一个解决方法,这更像是一种黑客而不是正确的用法,我不确定谁组合了“jquery.unobtrusive-ajax.js”文件,但如果有人知道如何给他们发送电子邮件,也许可以将此页面链接发送给他们 :-)。这是“jquery.unobtrusive-ajax.js”的有趣部分,在此函数“function asyncRequest(element, options)”中。
$.extend(options, {
        type: element.getAttribute("data-ajax-method") || undefined,
        url: element.getAttribute("data-ajax-url") || undefined,
        beforeSend: function (xhr) {
            var result;
            asyncOnBeforeSend(xhr, method);
            result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
            if (result !== false) {
                loading.show(duration);
            }
            return result;
        },
        complete: function () {
            loading.hide(duration);
            getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(this, arguments);
        },
        success: function (data, status, xhr) {
            asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
            getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(this, arguments);
        },
        error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
    });

当这段代码在每个步骤 beforeSend、Complete、error 上调用 apply 方法时,它传递了两个参数,“this”和“arguments”... 这样做是有效的,但是在上下文中,“this”是 XHR(XMLHttpRequest),而不是被点击的元素... 另外,通过检查函数头,您可以看到它将 XHR 对象作为第一个参数传递。那么为什么我们还需要它成为调用上下文呢?
我的解决方案是... 修改 "jquery.unobtrusive-ajax.js" 和 "jquery.unobtrusive-ajax-min.js" 文件...
与其在 apply 方法上传递“this”(XHR),不如发送实际的元素!
所以整个函数现在看起来像这样:
function asyncRequest(element, options) {
    var confirm, loading, method, duration;

    confirm = element.getAttribute("data-ajax-confirm");
    if (confirm && !window.confirm(confirm)) {
        return;
    }

    loading = $(element.getAttribute("data-ajax-loading"));
    duration = element.getAttribute("data-ajax-loading-duration") || 0;

    $.extend(options, {
        type: element.getAttribute("data-ajax-method") || undefined,
        url: element.getAttribute("data-ajax-url") || undefined,
        beforeSend: function (xhr) {
            var result;
            asyncOnBeforeSend(xhr, method);
            result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(element, arguments);
            if (result !== false) {
                loading.show(duration);
            }
            return result;
        },
        complete: function () {
            loading.hide(duration);
            getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(element, arguments);
        },
        success: function (data, status, xhr) {
            asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
            getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(element, arguments);
        },
        error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
    });

    options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

    method = options.type.toUpperCase();
    if (!isMethodProxySafe(method)) {
        options.type = "POST";
        options.data.push({ name: "X-HTTP-Method-Override", value: method });
    }

    $.ajax(options);
}

现在,当您创建处理这些事件的函数时,可以使用“this”来获取实际元素。
例如:
function BeginUpdatePage(xhr) {
 $("#MainMenu li").removeClass('selected');
 $(this).parent().addClass('selected');
 $("#pageBody").fadeTo('slow', 0.5);
}
function EndUpdatePage(xhr,status) {
 $("#pageBody").fadeTo('slow', 1);    
}
function FailUpdatePage(data,status,xhr) {
 alert('There has been an error loading this page please try again.');
}

现在,对于一些人来说,可能更容易采用Darin的解决方案,编写自己的Jquery来处理点击事件,嘿,你可能能够更好地掌控一切,但我相信内置的东西也应该能够正常工作,而不必去破坏它。所以我希望有人能够证明我是错的,并且实际上有更好的方法来做到这一点,或者这个脚本的制作者可以改变它。除非他们有充分的理由这样做?欢迎解释 :-) 再次感谢所有为这个问题提供好建议的人,我希望这能帮助未来的人们。

0

我有另一种解决方案,它使用 AJAX URL 来选择被点击的原始链接。如果只有一个具有完全相同 URL 的 ActionLink,则此解决方案才能正常工作:

function BeginUpdatePage() {
    // Get the URL that will be posted to
    var targetUrl = this.url;
    var sourceLink = null;

    // loop through all action links applicable
    $('#MainMenu a').each(function () {
        var href = $(this).attr('href');

        // the targetUrl contains the entire URL - including http://
        // so we need to do an indexOf to see if the href is contained 
        // within the target URL
        if (targetUrl.indexOf(href) > -1) {
            sourceLink = $(this);

            return false;
        }
    });

    // we've now got the link that was clicked - do with it as you wish...
    sourceLink.parent().addClass('selected');
}

0

检查一下 event.srcElement 怎么样?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接