开发者

How to use $(this) inside MVC3 Ajax.ActionLink OnBegin,OnComplete Events

My Controller creates a list of links like this

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

I have some javascript like this

function BeginUpdatePage() {
 $("#MainMenu li").removeClass('selected');
 $(this).par开发者_StackOverflowent().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.');
}

in the BeginUpdatePage function line 1 and 3 execute fine, but line 2 "$(this).parent().addClass('selected');" does not visiably do anything... (I have declared that class in my css).

I have looked at Jquery docs (Jquery.ajax()) it says that "this" is the element that was clicked. I have also checked in "jquery.unobtrusive-ajax.js" that was in my project by default. This file also passes "this" when executing my function.

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

What am I doing wrong here... I have seen other examples that have used this technique but I have been failing all day!

How can I get to the element that was clicked, inside my Begin, complete functions.


UPDATE Jan 20, 2014: The official 3.1 version of the unobtrusive ajax script contains this change, and was published at the same time as the MVC 5.1 release (Jan 17th). Go check check it out :-)

Original response

I came across this a while back to achieve the result you are looking for. VERY simple to do.

Arround approximately line # 100 of the jquery.unobtrusive-ajax.js file, add this one line of code.

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

That's it, you should be able to use this to reference the source element now.


You could try passing it like this:

OnBegin = "function() { BeginUpdatePage(this); }"

and then:

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

or simply use jQuery without any Ajax.* stuff:

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

and then:

$(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;
    });
});


Thanks guys for all your suggestions, Darin, although your solution works perfectly, I wanted to try and use the built in Ajax utilities.

I have found a work around after looking at Jquery.Ajax() Docs and also inspecting "jquery.unobtrusive-ajax.js" this is more of a Hack than proper use, im not sure who put the "jquery.unobtrusive-ajax.js" file together but if anybody knows how to email them, maybe send them a link to this page :-)

this is the interesting part of "jquery.unobtrusive-ajax.js" in this function "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"])
    });

When this code calls the apply method on each step beforeSend, Complete, error, it passes in two perams, "this" and "arguments"...

Wich works create, but in the context "this" is the XHR (XMLHttpRequest) not the element that was clicked... also by inspecting the function headers you can see that it passes the XHR object as the first peram. So why do we need it to be the calling context as well?

My solution... change the "jquery.unobtrusive-ajax.js" and "jquery.unobtrusive-ajax-min.js" files...

rather than pass "this" (XHR) on the apply method, lets send the actual element instead!

So the whole function now looks like this:

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

Now when you create your functions to handle these events you can use "this" to get the actual element.

example:

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.');
}

Now for some people it my be easier to just go with Darin's solution, to just write you own Jquery to handle the click event, hey you probably have better control over it all,

but Im a believer that the built in stuff should work too. without having to mess with it. So I hope either somebody can prove me wrong, and there is actually a better way of doing this, or the guys who put this script together could change it.

unless they have good reason for doing it this way? explanations welcome :-)

Thanks again everybody who had good suggestions for this question I hope this helps people in the future.


How about checking event.srcElement?


I've got another solution that uses the AJAX URL to select the original link that was clicked. This solution will only work properly if there's only one ActionLink with the exact URL:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜