Knockout.js映射并将大数据集添加到ko.observableArray

时间:2016-05-22 22:04:10

标签: javascript asp.net-mvc razor knockout.js

有许多使用底层数组向ko.observableArray添加大型数据集的示例如下:

ko.observableArray.fn.pushAll = function(valuesToPush) {
var underlyingArray = this();
this.valueWillMutate();
ko.utils.arrayPushAll(underlyingArray, valuesToPush);
this.valueHasMutated();
return this;  //optional
};

这样做的问题是我失去了我的观察力。当我使用chrome并在调试器中暂停时,我得到的数组中的值而不是函数c(),这是可观察的换行。我还必须观察其中的许多变量。

我发现以下内容有效:

var model = @Html.Raw(Json.Encode(Model));
vm.POs = ko.mapping.fromJS(model.POs);

问题是这很慢。如何使用底层数组添加,然后将可观察的包装添加到每个变量而不会出现性能问题?

以下是一些代码:

var vm = {
    POs: ko.observableArray([]),

    headersWithAccounting: ko.observableArray([
        {header_name: "DATE CREATED", property: "DATE_CREATED", state: ko.observable('')},
        {header_name: "DATE ISSUED", property: "DATE_ISSUED", state: ko.observable('')},
        {header_name: "USER CREATED", property: "NAME_USER", state: ko.observable('')},
        {header_name: "PO NUMBER", property: "NO_PO", state: ko.observable('')},
        {header_name: "ORDER STATUS", property: "NAME_STATUS", state: ko.observable('')},
        {header_name: "VENDOR", property: "NAME_VENDOR", state: ko.observable('')},
        {header_name: "TOTAL COST", property: "COST_TOTAL", state: ko.observable('')},
        {header_name: "CTU", property: "ID_CTU", state: ko.observable('')},
        {header_name: "ACCOUNTING CODE", property: "ACCOUNTING_CODE_NAME", state: ko.observable('')},
        {header_name: "CLOSE ORDER", property: "ACCOUNTING", state: ko.observable('')}
    ])
};

function PO() {
var self = this;

self.ID_ORDER = ko.observable();
self.DATE_CREATED = ko.observable();
self.DATE_ISSUED = ko.observable();
self.NAME_STATUS = ko.observable();
self.NAME_VENDOR = ko.observable();
self.NAME_USER = ko.observable();
self.COST_TOTAL = ko.observable();
self.ACCOUNTING_CODE_NAME = ko.observable();
self.ACCOUNTING_CODE_ID = ko.observable();
self.NO_PO = ko.observable();
self.SHOWDETAILS = ko.observable(0);
self.ID_TYPE = ko.observable(0);
self.DESCRIPTION = ko.observable('');
self.FILES = ko.observableArray();
self.POParts = ko.observableArray();
self.ACCOUNTING = ko.observable(0);
self.ID_CTU = ko.observable(0);
self.ACCOUNTING.subscribe(function(val) {
    if (vm.avoidCloseOrder() == 0) {
        $.ajax({
            type: "POST",
            url: '@Url.Action("AccountingCloseOrder", "Report")',
            dataType: 'JSON',
            data: {
                orderId: self.ID_ORDER()
            },
            success: function(msg) {
                if (msg != 'Good') {
                    window.location.href = msg;
                }
            },
            error: function (err) {
                alert("Error closing order, please try again");
            }
        });
    }
});
self.ACCOUNTING_CODE_ID.subscribe(function(val) {
    if (vm.avoidCloseOrder() == 0) {
        $.ajax({
            type: "POST",
            url: '@Url.Action("AccountingCodeChange", "Report")',
            dataType: 'JSON',
            data: {
                orderId: self.ID_ORDER(),
                accountingCodeId: self.ACCOUNTING_CODE_ID()
            },
            success: function(msg) {
            },
            error: function (err) {
                alert("Error closing order, please try again");
            }
        });
     }
  });
}

function POPart() {
    var self = this;

    self.CATEGORY = ko.observable();
    self.SUBCATEGORY = ko.observable();
    self.DESCRIPTION = ko.observable();
    self.PARTNO = ko.observable();
    self.QTY_ORDERED = ko.observable();
    self.QTY_RECEIVED = ko.observable();
    self.COST = ko.observable();
}

function FILE() {
    var self = this;

    self.LOCATION = ko.observable();
}

现在问题出在带有敲除绑定的剃刀代码中:

<div class="row">
    <div class="col-md-12">
        <div data-bind="foreach:POs">
            <table class="table-responsive">
                <thead data-bind="if: $index() == 0 || ($index() > 0 && vm.POs()[$index()-1].SHOWDETAILS() == 1)">
                    <tr data-bind="foreach:vm.headersWithAccounting">
                        <th>
                            <span data-bind="click:$root.sortPOs.bind(property), text:header_name" style="cursor:pointer"></span><i data-bind="css: state"></i>
                        </th>
                    </tr>
                </thead>
                <tbody class="clickabletbody">
                    <tr>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:DATE_CREATED"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:DATE_ISSUED"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:NAME_USER"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:NO_PO"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:NAME_STATUS"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:NAME_VENDOR"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:COST_TOTAL"></div>
                        </td>
                        <td data-bind="click:$parent.showDetailsFor">
                            <div data-bind="text:ID_CTU"></div>
                        </td>
                        <td>
                            @Html.DropDownList("ddlVendor", new SelectList(Model.ACCOUNTING_CODE_SELECTLIST, "Value", "Text"), "--Select Accounting Code--", new { @class = "form-control", data_bind = "value:ACCOUNTING_CODE_ID" })
                        </td>
                        <td>
                            <input type="checkbox" style="height:30px; width: 30px;" data-bind="checked:ACCOUNTING, enable:(NAME_STATUS() == 'ACCOUNTING' || NAME_STATUS() == 'CLOSED')" />  //PROBLEM RIGHT HERE!!!!
                        </td>
                    </tr>
                </tbody>
            </table>
            <table data-bind="if:SHOWDETAILS, fadeVisible:SHOWDETAILS" style="background-color:antiquewhite" class="table-responsive">
                <!-- ko if:(ID_TYPE() == 2 || ID_TYPE() == 3) -->
                <thead>
                    <tr>
                        <th>
                            DESCRIPTION
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>
                            <div data-bind="text:DESCRIPTION"></div>
                        </td>
                    </tr>
                </tbody>
                <!-- /ko -->
                <!-- ko if:(ID_TYPE() == 1) -->
                <thead>
                    <tr>
                        <th>
                            CATEGORY
                        </th>
                        <th>
                            SUBCATEGORY
                        </th>
                        <th>
                            DESCRIPTION
                        </th>
                        <th>
                            PART NO
                        </th>
                        <th>
                            QTY ORDERED
                        </th>
                        <th>
                            QTY RECEIVED
                        </th>
                        <th>
                            COST
                        </th>
                    </tr>
                </thead>
                <tbody data-bind="foreach:POParts">
                    <tr>
                        <td>
                            <div data-bind="text:CATEGORY"></div>
                        </td>
                        <td>
                            <div data-bind="text:SUBCATEGORY"></div>
                        </td>
                        <td>
                            <div data-bind="text:DESCRIPTION"></div>
                        </td>
                        <td>
                            <div data-bind="text:PARTNO"></div>
                        </td>
                        <td>
                            <div data-bind="text:QTY_ORDERED"></div>
                        </td>
                        <td>
                            <div data-bind="text:QTY_RECEIVED"></div>
                        </td>
                        <td>
                            <div data-bind="text:COST"></div>
                        </td>
                    </tr>
                </tbody>
                <!-- /ko -->
            </table>
            <table data-bind="if:SHOWDETAILS, fadeVisible:SHOWDETAILS" style="background-color:antiquewhite" class="table-responsive">
                <thead>
                    <tr>
                        <th>
                            Files
                        </th>
                    </tr>
                </thead>
                <tbody data-bind="foreach:FILES">
                    <tr>
                        <td>
                            <a data-bind="attr: {href: LOCATION, target: '_blank'}" class="btn btn-primary btn-md">Download File</a>
                        </td>
                    </tr>
                </tbody>
            </table>
            <div data-bind="if:SHOWDETAILS"><hr /></div>
        </div>
        <!-- /ko -->
    </div>
</div>

问题在于复选框,chrome控制台说错误: knockout-3.4.0.js:72 Uncaught TypeError:无法处理绑定“enable:function(){return(NAME_STATUS()=='ACCOUNTING'|| NAME_STATUS()=='CLOSED')}” 消息:NAME_STATUS不是函数

这是因为在值中它不再是具有敲除绑定的函数,它只是一个值,因此它不是函数,并且此错误是正确的。我失去了这个,因为使用底层数组只推送javascript值,而不是映射可观察函数。

目前使用可观察量的200个条目需要大约10秒才能映射,如果你问我,这是非常荒谬的。当我有1000+时会发生什么。即使我只加载其中的50个并开始使用ajax来收集幕后的其余内容,每次我获得更多数据时,它会滞后页面几秒钟,直到它加载所有数据。不确定如何解决这个问题。

编辑:

我刚刚有一个AHA时刻并修复了失败的绑定问题。 232条目现在大约需要4秒钟。仍然希望得到它更快但是我改变了。

function PO(data) {
    var self = this;

    self.ID_ORDER = ko.observable(data.ID_ORDER);
    self.DATE_CREATED = ko.observable(data.DATE_CREATED);
    self.DATE_ISSUED = ko.observable(data.DATE_ISSUED);
    self.NAME_STATUS = ko.observable(data.NAME_STATUS);
    self.NAME_VENDOR = ko.observable(data.NAME_VENDOR);
    self.NAME_USER = ko.observable(data.NAME_USER);
    self.COST_TOTAL = ko.observable(data.COST_TOTAL);
    self.ACCOUNTING_CODE_NAME = ko.observable(data.ACCOUNTING_CODE_NAME);
    self.ACCOUNTING_CODE_ID = ko.observable(data.ACCOUNTING_CODE_ID);
    self.NO_PO = ko.observable(data.NO_PO);
    self.SHOWDETAILS = ko.observable(0);
    self.ID_TYPE = ko.observable(data.ID_TYPE);
    self.DESCRIPTION = ko.observable(data.DESCRIPTION);

    self.FILES = ko.observableArray();

    if (data.FILES != null) {
        for (var i = 0; i < data.FILES.length; i++) {
            self.FILES.push(new FILE(data.FILES[i]));
        }
    }

    self.POParts = ko.observableArray();

    if (data.POParts != null) {
        for (var i = 0; i < data.POParts.length; i++) {
            self.POParts.push(new POPart(data.POParts[i]));
        }
    }

    self.ACCOUNTING = ko.observable(data.ACCOUNTING);

    self.ID_CTU = ko.observable(data.ID_CTU);

    self.ACCOUNTING.subscribe(function(val) {
        if (vm.avoidCloseOrder() == 0) {
            $.ajax({
                type: "POST",
                url: '@Url.Action("AccountingCloseOrder", "Report")',
                dataType: 'JSON',
                data: {
                    orderId: self.ID_ORDER()
                },
                success: function(msg) {
                    if (msg != 'Good') {
                        window.location.href = msg;
                    }
                },
                error: function (err) {
                    alert("Error closing order, please try again");
                }
            });
        }
    });

    self.ACCOUNTING_CODE_ID.subscribe(function(val) {
        if (vm.avoidCloseOrder() == 0) {
            $.ajax({
                type: "POST",
                url: '@Url.Action("AccountingCodeChange", "Report")',
                dataType: 'JSON',
                data: {
                    orderId: self.ID_ORDER(),
                    accountingCodeId: self.ACCOUNTING_CODE_ID()
                },
                success: function(msg) {
                },
                error: function (err) {
                    alert("Error closing order, please try again");
                }
            });
        }
    });
}

function POPart(data) {
    var self = this;

    self.CATEGORY = ko.observable(data.CATEGORY);
    self.SUBCATEGORY = ko.observable(data.SUBCATEGORY);
    self.DESCRIPTION = ko.observable(data.DESCRIPTION);
    self.PARTNO = ko.observable(data.PARTNO);
    self.QTY_ORDERED = ko.observable(data.QTY_ORDERED);
    self.QTY_RECEIVED = ko.observable(data.QTY_RECEIVED);
    self.COST = ko.observable(data.COST);
}

function FILE(data) {
    var self = this;

    self.LOCATION = ko.observable(data.LOCATION);
}

推送功能:

ko.observableArray.fn.pushAll = function(valuesToPush)
{
    var underlyingArray = this();
    this.valueWillMutate();
    ko.utils.arrayForEach(valuesToPush, function(item) {
        underlyingArray.push(new PO(item));
    });
    this.valueHasMutated();
    return this;
}

任何让这个超过4秒的想法?

2 个答案:

答案 0 :(得分:0)

我没有真正明白你的意思“我放松了我的绑定”。它可能是一个调试器artefact(它显示了observable的值)。

我也可能是一个“这个”问题。

我得到了这个代码片段(你可以在填充数组时使用点击绑定)

var elementVM = (function () {
    function elementVM(message) {
        this.myText = ko.observable(message);
    }
    elementVM.prototype.changeText = function () {
        this.myText(this.myText() + " changed");
    };
  
    return elementVM;
}());

var myVM = (function() {
  var getText = function(count) {
    return "My Text " + (count);
  };
  
  var myObservableArray = ko.observableArray([new elementVM(getText(0))]);
  
  return function() {
    this.myArray = myObservableArray;
    myVM.prototype.populate = function() {
      myObservableArray.valueWillMutate();
      
      for(var i = 1; i <= 1000; ++i) 
      {
        myObservableArray().push(new elementVM("My Text " + i));
      }
      
      myObservableArray.valueHasMutated();
    };
  };
}());

var vm = new myVM();

ko.applyBindings(vm);

setTimeout(function() {
  var start = new Date();
  vm.populate();
  var stop = new Date();
  document.getElementById("pushAll").innerHTML = "pushallTiming: " + (stop - start);
}, 1000);
li {
  list-style: none;
  border: 1px solid black;
  width: auto;
  text-align: center;
}

#pushAll {
  background-color: red;
  width: auto;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="pushAll"></div>
<ul data-bind="template: { name: 'my-template', foreach: myArray }"></ul>
 
<script type="text/html" id="my-template">
    <li data-bind="text: myText, click: changeText"></li>
</script>

答案 1 :(得分:0)

实际复制到阵列的时间不到1秒。它是valueHasMutated()函数,需要几秒钟,这只是KO的一部分。我很高兴不花很长时间将数据复制到数组中。我将尝试只分页50个条目,这应该有助于DOM加载更快。感谢所有回复的人。