从Ajax表单加载的MVC4 WebGrid - 在排序和分页时多次调用Controller

时间:2014-03-08 16:59:20

标签: ajax asp.net-mvc-4 sorting paging webgrid

我在视图中有以下内容

@using (Ajax.BeginForm("Search", "Home", null,
                               new AjaxOptions
                                   {
                                       InsertionMode = InsertionMode.Replace,
                                       HttpMethod = "POST",
                                       UpdateTargetId = "gridContent",
                                   }, new { @class = "search" }))
{
    <input type="submit" value="Search" />
}
<div id="gridContent">
</div>

返回/ Home / Search

@model List<TestTable.Models.People>
@{
Layout = null;
}
@{
var grid = new WebGrid(Model, canPage: true, canSort: true, rowsPerPage: 5,             ajaxUpdateContainerId: "tableDiv"); grid.Pager(WebGridPagerModes.NextPrevious);
}
<div id="tableDiv">
    @grid.GetHtml(
        columns: grid.Columns(
        grid.Column("Name", " Name")
))
</div>

这在MVC3中运行良好,但MVC4会在每次新搜索时发送脚本, 为每个分页和排序查询单击每个提交按钮单击一个新的额外请求。 以下是它的外观:

"http://localhost:59753/Home/Search".
"http://localhost:59753/Home/Search?sort=Name&sortdir=ASC&__swhg=1394297281115"
"http://localhost:59753/Home/Search". 
"http://localhost:59753/Home/Search?sort=Name&sortdir=ASC&__swhg=1394297284491"
"http://localhost:59753/Home/Search?sort=Name&sortdir=ASC&__swhg=1394297284490"

任何想法如何解决? 提前谢谢!

8 个答案:

答案 0 :(得分:12)

发生这种情况的原因是因为WebGrid控件每次渲染时都会将以下脚本注入到DOM中(在每次提交AJAX表单的情况下,因为WebGrid位于您注入的部分中) DOM):

<script type="text/javascript">
    (function($) {
        $.fn.swhgLoad = function(url, containerId, callback) {
            url = url + (url.indexOf('?') == -1 ? '?' : '&') + '__swhg=' + new Date().getTime();

            $('<div/>').load(url + ' ' + containerId, function(data, status, xhr) {
                $containerId).replaceWith($(this).html());
                if (typeof(callback) === 'function') {
                    callback.apply(this, arguments);
                }
            });
            return this;
        }

        $(function() {
            $('table[data-swhgajax="true"],span[data-swhgajax="true"]').each(function() {
                var self = $(this);
                var containerId = '#' + self.data('swhgcontainer');
                var callback = getFunction(self.data('swhgcallback'));

                $(containerId).parent().delegate(containerId + ' a[data-swhglnk="true"]', 'click', function() {
                    $(containerId).swhgLoad($(this).attr('href'), containerId, callback);
                    return false;
                });
            })
        });

        function getFunction(code, argNames) {
            argNames = argNames || [];
            var fn = window, parts = (code || "").split(".");
            while (fn && parts.length) {
                fn = fn[parts.shift()];
            }
            if (typeof (fn) === "function") {
                return fn;
            }
            argNames.push(code);
            return Function.constructor.apply(null, argNames);
        }
    })(jQuery);
</script>

此脚本已粘贴到WebGrid帮助程序中,一旦在WebGrid上启用AJAX,就无法对其进行操作。在这个脚本中,您无疑会注意到它如何以生动的方式订阅分页锚点的click事件:

$(containerId).parent().delegate(containerId + ' a[data-swhglnk="true"]', 'click', function() {
    $(containerId).swhgLoad($(this).attr('href'), containerId, callback);
    return false;
});

除了每次点击提交按钮,你都将这个脚本注入你的DOM(因为你的WebGrid是部分的),并且基本上你订阅的click事件时,这都是甜蜜和花花公子。分页锚定多次。

如果这个WebGrid助手的作者让你有可能用标准的delegate处理程序注册来替换这个click,这本来是很好的,因为它本来就是理想的创建多个事件注册,但遗憾的是作者没有给你留下这种可能性。他们只是假设WebGrid将成为初始DOM的一部分,因此也就是他们的脚本。

一种方法是订阅Ajax表单提交的OnBegin处理程序,只需undelegate现有的事件处理程序,因为刷新DOM后它们将被覆盖:

@using (Ajax.BeginForm("Search", "Home", null,
    new AjaxOptions
    {
        InsertionMode = InsertionMode.Replace,
        OnBegin = "callback",
        HttpMethod = "POST",
        UpdateTargetId = "gridContent",
    }, new { @class = "search" }))
{
    <input type="submit" value="Search" />
}

<div id="gridContent"></div>

<script type="text/javascript">
    var callback = function (a) {
        $('#tableDiv').parent().undelegate('#tableDiv a[data-swhglnk="true"]', 'click');
    };
</script>

但说实话,我个人只是讨厌所有这些自动生成的脚本,并且从不使用任何Ajax.*助手的东西以及在WebGrid上激活AJAX。我更喜欢使用jQuery不引人注意地AJAX化我想要的元素,这使我能够更好地控制正在发生的事情。这样我只需将WebGrid帮助器自动生成的一堆javascript外化到一个单独的js文件中,我将包含在我的View中,并且不需要注销和清理创建的重复事件处理程序的混乱遵循标准的做事方式。

答案 1 :(得分:2)

有点晚了,但我遇到了类似的问题,找不到它的任何描述,所以我想在这里添加它以防万一它可以帮助某人。

无论我尝试什么,排序和分页请求总是重复的。我尝试了这里描述的修复程序,但即使在我完成任何类型的AJAX更新之前,我也得到了重复。

我暂时搁置了这个问题,在对我的分页不满意后,我创建了自己的部分视图。当删除旧的分页时,重复不再是.....没有花时间试图找出原因,只是快乐它已经解决了。

所以我删除了这个:

        @grid.Pager(mode: WebGridPagerModes.All, firstText: "First", previousText: "Prev", nextText: "Next", lastText: "Last")

正如我所说,万一它可以帮助别人。

答案 2 :(得分:1)

每次渲染网格时,看起来使用“on”/“live”事件绑定分页和排序链接。在渲染网格html或ajaxUpdateCallback方法之前,可以解决网格元素事件的绑定问题。

$( '#tableDiv')andSelf()解除绑定();

答案 3 :(得分:1)

我已经为MVC 5解决了这个问题,你需要使用ajax调用而不是使用ajax形式,捕获ajax响应并使用下面的webgrid帮助器替换部分页面的DOM:

var data = data.replace('<script type="text/javascript">', '<script type="text/javascript"> $(".table").undelegate();');
    $('#YourParentDivIDWherePartialIsRendered').undelegate();
    $.ajax
           (
               {
                   contentType: "application/json; charset=utf-8",
                   type: 'POST',
                   url: '/YourController_Name/YourAction_Name',
                   data: JSON.stringify(YourModel),
                   success: function (data) {
                       //Added to undelegate the old events tagged to the partial view's grid.
                       var data = data.replace('<script type="text/javascript">', '<script type="text/javascript"> $(".table").undelegate();');
                       $('#YourParentDivIDWherePartialIsRendered').undelegate();
                       $('#accountSearch-grid').html(data);
                       $(document).foundation();
                   },
                   error: function (xhr, status, error) {
                       alert(error);
                   }

               });

答案 4 :(得分:0)

将此脚本放入Index.cshtml或js文件

<script type="text/javascript">
(function($) {
    $.fn.swhgLoad = function(url, containerId, callback) {
        url = url + (url.indexOf('?') == -1 ? '?' : '&') + '__swhg=' + new Date().getTime();

        $('<div/>').load(url + ' ' + containerId, function(data, status, xhr) {
            $containerId).replaceWith($(this).html());
            if (typeof(callback) === 'function') {
                callback.apply(this, arguments);
            }
        });
        return this;
    }

    $(function() {
        $('table[data-swhgajax="true"],span[data-swhgajax="true"]').each(function() {
            var self = $(this);
            var containerId = '#' + self.data('swhgcontainer');
            var callback = getFunction(self.data('swhgcallback'));

            $(containerId).parent().delegate(containerId + ' a[data-swhglnk="true"]', 'click', function() {
                $(containerId).swhgLoad($(this).attr('href'), containerId, callback);
                return false;
            });
        })
    });

    function getFunction(code, argNames) {
        argNames = argNames || [];
        var fn = window, parts = (code || "").split(".");
        while (fn && parts.length) {
            fn = fn[parts.shift()];
        }
        if (typeof (fn) === "function") {
            return fn;
        }
        argNames.push(code);
        return Function.constructor.apply(null, argNames);
    }
})(jQuery);

然后,在类

中处理你的网格html字符串
string html = _grid.GetHtml(
            columns: _columns,
    ...
            ).ToHtmlString();
Regex reg1 = new Regex("<script(.|\n)*?</script>", RegexOptions.IgnoreCase);
string _script = reg1.Match(html).Value.ToString();
html = html.Replace(_script, "");

在索引文件中: @ MvcHtmlString.Create(@html)

那个'全部

答案 5 :(得分:0)

实际上解决方案$('#tableDiv').parent().off('click', '#tableDiva[data-swhglnk="true"]');工作正常,但它仍然是分页调用网址中的 __ swhg ,所以这里是删除额外 __ swhg 的代码使用AJAX调用页面。

$(document).ajaxComplete(function () {
    $('a[data-swhglnk="true"]').click(function () {        
        $(this).attr("href", $(this).attr("href").replace(/(^|&)__swhg=([^&]*)/, ''));
    });
});

答案 6 :(得分:0)

如果有人经历过这些并且仍然遇到问题,我相信我找到了真正的问题。该脚本从未在页面上呈现两次,因此接受的答案没有意义。

问题出在这一行:

 $('table[data-swhgajax="true"],span[data-swhgajax="true"]').each(function() {

如果寻呼机不在表的页脚中(通过在webgrid的设置中定义它)而是通过调用grid.pager()来定义它,它将把寻呼机放在span中。这意味着当调用上面的行时,它会将click事件绑定到表的父级(gridContent)和span的父级(gridContent)。

有几个选项,但我选择做的基本上是顶部回答所说的,并删除该元素的代理,如下所示:

$("#gridContent").off("click", "**");

然后重新绑定相同的单击函数,但只将其绑定到span。所以上面引用的行改为:

$('span[data-swhgajax="true"]').each(function () {

它按预期工作。这当然会破坏作为表格一部分的同一页面上的任何寻呼机。

答案 7 :(得分:0)

首先从Web Grid中删除ajax Update Callback并将以下java脚本代码添加到web grid或web grid Container div:

$("#WebgridContainerDiv table  a").click(function (event) {
        event.preventDefault();
        var href = $(this).attr("href");
        $.ajax({
            url: href,
            dataType: 'html',
            success: function (data) {
                $("#WebgridContainerDiv").empty();
                $("#WebgridContainerDiv").html(data);
            }
        })
    });