jquery - 使用 jquery $.download 处理可能非常大的表单创建/发布的最佳方法

标签 jquery asp.net-mvc performance internet-explorer download

我的流程如下:

  1. 用户执行一个复杂的搜索,该搜索通过 ajaxly 完成,返回一堆 id(可能是 1,可能是 10000)

  2. 一旦他们有了用户,他们就可以选择一些内容,然后下载一个文件(这是一个基于 ID 和他们选择的内容的报告)

为了实现这一点,我使用了 $.download 的高度修改版本。

在这里看到:

jQuery.download = function (url, data, method, loadingHolderDivId) {
if (url && typeof data == 'object') {
    //for this version, data needs to be a json object.  
    //loop through the data object..
    $('#' + loadingHolderDivId).html($('#LoadingScreen').html());
    
    var theForm = $('<form></form>').attr('action', url).attr('method', method).attr('id', 'jqueryDownloadForm').attr('target', 'iframeX');

    $.each(data, function (propertyName, propertyVal) {
        if (propertyVal != null) {
            if (typeof propertyVal == 'object') {

                //HANDLE ARRAYS!
                for (var i = 0, len = propertyVal.length; i < len; ++i) {
                    theForm.append($("<input />").attr('type', 'hidden').attr('id', propertyName + i.toString).attr('name', propertyName).val(propertyVal[i]));
                }
            }
            else {
                theForm.append($("<input />").attr('type', 'hidden').attr('id', propertyName).attr('name', propertyName).val(propertyVal));
            }
        }
    });


    var iframeX;
    var downloadInterval;

    // remove old iframe if has
    $("#iframeX").remove();
    // create new iframe
    iframeX = $('<iframe src="javascript:false;" name="iframeX" id="iframeX"></iframe>').appendTo('body').hide();

    if ($.browser.msie) {
        downloadInterval = setInterval(function () {
            // if loading then readyState is “loading” else readyState is “interactive”
            if (iframeX && iframeX[0].readyState !== "loading") {
                $('#' + loadingHolderDivId).empty();
                clearInterval(downloadInterval);
            }
        }, 23);
    }
    else {
        iframeX.load(function () {
            $('#' + loadingHolderDivId).empty();
        });
    }


    theForm.appendTo('body').trigger('submit').remove();
    return false;
}
else {
    //they didn't fill in the params.  do nothing
}


};

基本上,它的作用是解析数据中的内容,并从中构建一个表单。当没有很多 id 时,这非常有效。但是当有 8000 时,在 IE 中需要 5 或 10 秒,这并不奇怪,众所周知 IE 在 dom 操作方面很糟糕。

另一个问题是在 IE 中。 $('#' + loadingHolderDivId).html($('#LoadingScreen').html()); 在完成表单构建之后才会真正发生。我猜测这是因为需要一秒钟才能完成此操作,并且在完成之前它已经忙于构建表单。

我以这种方式构建表单的原因是,默认模型绑定(bind)器会很高兴并将我的表单直接绑定(bind)到一个可爱的模型中。 id 列表被绑定(bind)到 ilist(整数)

以下是 Controller 操作的示例:

Function ExportUsers(ByVal model As ExportUsersPostModel) As ActionResult

这是模型的示例:

<Serializable()> _
Public Class ExportUsersPostModel
    Public Property FilterUserIds As IList(Of Integer) = New List(Of Integer)
    Public Property FilterColumnIds As IList(Of Integer) = New List(Of Integer)
    public property ShowThis as boolean
    public property OtherStuff as string = string.empty
    Public Property FormatId As Integer
End Class

所以实际的问题有两个:

  1. 如何在开始极其缓慢的死亡形式构建之前显示“正在加载”消息?

  2. 如何加快表单构建速度,或者以不慢的方式构建表单,但仍能让模型绑定(bind)器满意?

最佳答案

如果您能够将模型作为 JSON 传递,则可以创建自定义 ModelBinder 来处理将 JSON 映射到您的数据结构。我最近为无法自动映射的对象类型执行了此操作。 Json.Net提供了一个名为 JObject 的类,它接受 JSON 字符串并将其映射到动态 C# 对象。然后,您可以将动态对象映射到强类型对象。

要创建自定义 ModelBinder,只需创建一个继承自 IModelBinder 的类并实现 BindModel 方法即可。这是我的实现的副本。您的显然会略有不同:

internal class FilterBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");
        if (bindingContext == null)
            throw new ArgumentNullException("bindingContext");

        if ((controllerContext.HttpContext.Request.Form.Count > 1 || (controllerContext.HttpContext.Request.Form.Count == 1 && !string.IsNullOrWhiteSpace(controllerContext.HttpContext.Request.Form.AllKeys[0]))) || (controllerContext.HttpContext.Request.QueryString.Count > 1 || (controllerContext.HttpContext.Request.QueryString.Count == 1 && !string.IsNullOrWhiteSpace(controllerContext.HttpContext.Request.QueryString.AllKeys[0]))))
        {
            ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            string value = val == null || string.IsNullOrEmpty(val.AttemptedValue) ? string.Empty : val.AttemptedValue;
            if (string.IsNullOrEmpty(value)) return null;
            dynamic obj = JObject.Parse(value);
            return new FilterSet(obj);
        }
        else
            return null;
    }
}

我有很多检查来确保我得到的内容是有效的,您可能需要也可能不需要。然后,在获取 JObject 后,我​​将其传递给执行映射的构造函数。

关于jquery - 使用 jquery $.download 处理可能非常大的表单创建/发布的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5980548/

相关文章:

php - 使用数据库检查文本字段值

jquery - 单击导航栏中的元素时添加工具提示

javascript - 如何检测浏览器 JavaScript 是否关闭并显示通知

c# - 使用 Excel = Microsoft.Office.Interop.Excel 编译错误

performance - 最有效地在 TreeMap 中找到第 N 个键

javascript - jQuery 表单输入到数组中不起作用

c# - MVC 使用助手命名空间中的方法

jquery - 如何使用ajax删除表中的一行

mysql - MYSQL "IN"的性能

mysql - 在我的 RAILS 应用程序中将 2 个 SELECT 合并为一个 SELECT