An internal web software that is being used by a warehouse. It contains a table of orders with items that need to be scanned and prepared for delivery.
It has 3 sorting options, 12 main and secondary filters. The table was created by ng-repeat, and there are lots of references for scope variables.
It contains one main array with about 2000 objects inside, and also has a few more object helpers as well - some are small and some are big.
The main array needs to be refreshed by AJAX every minute to refresh the table, since there are several people who use it simultaneously.
Array example:
[0:{Barcode:"1234", Description:"Hisense 50 Inch Quad Core UHD 4K Smart Led TV", Docbal: 0, Docdate:"17/10/2017", Docnum:111111, FastOrder:0, GrpCode:111, NotInStock:true, Notes:"Hello", OnHand:0, OpenQty:1, OrderNumber:1736604, PartOfNotInStock:true, Total:2123, cntOrdersToDemand:6, commited:true, isMultiple:false, multipleChecked:true, relevantStock:0, scanned:0, shelf:null}]
Every scan executes a series of events that contain lots of data searching and updating in the clients objects.
almost all functions and variables are in the scope and being called and used by ng-click etc.
At the beginning there weren't performance problems since the array was pretty small, but now it got bigger and I saw that there are up to 60,000 watchers (!) in the program.
Is there a possibility to maintain the current structure with a few changes?
The table tr creation:
<tr ng-repeat="docs in itemsData | filter: searchText | filter: filterTable(currentFilter) : true | orderBy: orderType" ng-class="{'notinstock':docs.NotInStock,'text-center': true, 'marked': docs.OpenQty==docs.scanned}" ng-show="( (multipleDocs==docs.isMultiple) || (multipleDocs=='all') ) && ((docs.Docbal>0)!=noDebt) && ((docs.PartOfNotInStock>0)!=notInStock) && ((docs.FastOrder==0) != fastOrder)" docnum="{{docs.Docnum}}" orderNumber="{{docs.OrderNumber}}" barcode="{{docs.Barcode}}">
<td>
<div class="copyDiv" ng-show="(!docs.FastOrder)">
<i ng-click="searchItems(docs.Docnum, 'doc')" title="לחיצה לחיפוש / חזרה מחיפוש" ng-class="{'fa fa-search-minus': searchOnDoc==true, 'fa fa-search-plus':searchOnDoc==false}" class="searchIcon itemClick" aria-hidden="true"></i>
<span title="לחיצה להעתקה" ng-click="clipboardCopyDoc('doc'+$index)" id="copyClickDoc{{'doc'+$index}}" class="clipboard itemClick" ngclipboard data-clipboard-text="{{docs.Docnum}}"> {{docs.Docnum}}</span>
</div>
<div class="docDate" ng-show="(!docs.FastOrder)">{{docs.Docdate}}</div>
<span ng-switch on="docs.shipping" ng-click="openInvoice(docs.Docnum)" title="{{ docs.FastOrder ? '' : 'לחיצה לצפייה בפרטי חשבונית' }}" ng-class="{'itemClick' : !docs.FastOrder}">
<span ng-switch-when="shipping" class="label label-by">Chita</span>
<span ng-switch-when="shipping_ev" class="label label-purplew">Buzzr</span>
<span ng-switch-when="pickup" class="label label-bw">איסוף</span>
<span ng-switch-when="boxit" class="label label-pinkw">Boxit</span>
<span ng-switch-when="postil" class="label label-rw">דואר</span>
<span ng-switch-default class="label label-brownw">לא צוין</span>
</span>
<span ng-show="docs.FastOrder" ng-click="cancelFastOrderSure(docs.OrderNumber)" class="label label-danger itemClick cancelOrderBt" title="לחיצה לביטול ההזמנה המהירה"><i class="fa fa-fast-backward"></i> לחץ כדי להחזיר למכירות</span>
</td>
<td>
<div class="copyDiv">
<i ng-click="searchItems(docs.OrderNumber, 'doc')" title="לחיצה לחיפוש / חזרה מחיפוש" ng-class="{'fa fa-search-minus': searchOnDoc==true, 'fa fa-search-plus':searchOnDoc==false}" class="searchIcon itemClick" aria-hidden="true"></i>
<span title="לחיצה להעתקה" ng-click="clipboardCopyDoc('ord'+$index)" id="copyClickDoc{{'ord'+$index}}" class="clipboard itemClick" ngclipboard data-clipboard-text="{{docs.OrderNumber}}"> {{docs.OrderNumber}}</span>
</div>
<div class="docDate" ng-show="(docs.FastOrder)">{{docs.Docdate}}</div>
<span class="itemClick" ng-click="openOrder(docs.OrderNumber)" title="לחיצה לצפייה בפרטי ההזמנה">
<span ng-show="docs.FastOrder" class="label label-danger"><i class="fa fa-fast-backward"></i> הזמנה מהירה</span>
<span ng-show="!docs.FastOrder" class="label label-default">הזמנה רגילה</span>
</span>
</td>
<td>
<div class="copyDiv">
<i ng-click="searchItems(docs.Barcode, 'bar')" title="לחיצה לחיפוש / חזרה מחיפוש" ng-class="{'fa fa-search-minus': searchOnBar ==true, 'fa fa-search-plus':searchOnBar ==false}" class="searchIcon itemClick" aria-hidden="true"></i>
<span title="לחיצה להעתקה" ng-click="clipboardCopyBar($index)" id = "copyClickBar{{$index}}"class = "clipboard itemClick" ngclipboard data-clipboard-text="{{docs.Barcode}}"> {{docs.Barcode}}</span>
</div>
</td>
<td ng-bind-html="renderDoc(docs)"></td>
<td class="itemClick" title="לחיצה להצגת סיריאלים זמינים" ng-click="peekAtSerials(docs.Barcode,docs.Docnum,docs.OrderNumber)">{{docs.scanned}}/{{docs.OpenQty}}</td>
<td class="notes" ng-bind-html="renderHtml(docs.Notes)"></td>
<td>{{docs.cntOrdersToDemand}}/{{docs.relevantStock}}</td>
<td>{{ getShelf(docs); }}</td>
<td class="balance" ng-bind-html="renderHtml(getBal(docs.Docbal))"></td>
</tr>
The program needs to work also on a tablet, so this architecture of burdening the client with so much computing concerns - using so much memory and resources - makes me wonder if I need to change the app to be completely server side based.
I already tried to create the table using JavaScript only, and compile it. It reduced the watchers to 1600, but it took lots of memory and CPU, and every time the table was refreshed, the program got stuck for a few seconds. also it means that on every filter it needs to be created again. Is there a better way to do this?
what I did:
HTML:
<tbody id="tBody" compile="tr" ng-show="trNow"></tbody>
<tbody id="tBody" compile="tr2" ng-show="!trNow"></tbody>
compile directive:
myApp.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}]);
functions that create the table:
function makeTable(){
makeTr(function(tr){
switch($scope.trNow){
case true: //tr
$scope.tr2 = tr;
$scope.trNow = false;
$scope.tr = "";
break;
case false: //tr2
$scope.tr = tr;
$scope.trNow = true;
$scope.tr2 = "";
break;
}
}
);
}
async function makeTr(callBack){
var tr = "";
for (var i=0; i<itemsDataView.length; i++){
var titleText= itemsDataView[i].FastOrder ? '' : 'לחיצה לצפייה בפרטי חשבונית';
var trClassNIS = itemsDataView[i].NotInStock ? 'notinstock' : '';
var trClassMark = (itemsDataView[i].OpenQty == itemsDataView[i].scanned) ? 'marked' : '';
var trClass = itemsDataView[i].NotInStock ? 'notinstock' : '';
var trClass = itemsDataView[i].NotInStock ? 'notinstock' : '';
var trClass = itemsDataView[i].NotInStock ? 'notinstock' : '';
var showSearch = $scope.searchOnDoc==true ? 'fa fa-search-minus' : 'fa fa-search-plus';
var showNoFast = !itemsDataView[i].FastOrder ? '<div class="copyDiv">'+
'<i ng-click="searchItems('+itemsDataView[i].Docnum+',\'doc\')" title="לחיצה לחיפוש / חזרה מחיפוש" class="'+showSearch+' searchIcon itemClick" aria-hidden="true"></i>'+
'<span title="לחיצה להעתקה" ng-click="clipboardCopyDoc(\'doc\'+'+i+')" id="copyClickDodoc'+i+'" class="clipboard itemClick" ngclipboard data-clipboard-text="'+ itemsDataView[i].Docnum+'"> '+itemsDataView[i].Docnum+'</span>'+
'</div><div class="docDate">'+itemsDataView[i].Docdate+'</div>' : '';
var switchShippingClass = !itemsDataView[i].FastOrder ? 'itemClick' : '';
var switchShipping;
switch(itemsDataView[i].shipping){
case "shipping":
switchShipping = '<span class="label label-by">Chita</span>';
break;
case "shipping_ev":
switchShipping = '<span class="label label-purplew">Buzzr</span>';
break;
case "pickup":
switchShipping ='<span class="label label-bw">איסוף</span>';
break;
case "boxit":
switchShipping ='<span class="label label-pinkw">Boxit</span>';
break;
case "postil":
switchShipping ='<span class="label label-rw">דואר</span>';
break;
default:
switchShipping ='<span class="label label-brownw">לא צוין</span>';
}
var fastOrderSpan = itemsDataView[i].FastOrder ? '<br><span ng-click="cancelFastOrderSure('+itemsDataView[i].OrderNumber+')" class="label label-danger itemClick cancelOrderBt" title="לחיצה לביטול ההזמנה המהירה"><i class="fa fa-fast-backward"></i> לחץ לביטול הזמנה מהירה</span>' : "";
var fastOrderDiv = itemsDataView[i].FastOrder ? '<div class="docDate">'+itemsDataView[i].Docdate+'</div>' : "";
var fastOrdeSpanYesNo = itemsDataView[i].FastOrder ? '<span class="label label-danger"><i class="fa fa-fast-backward"></i> הזמנה מהירה</span>' : '<span class="label label-default">הזמנה רגילה</span>';
tr+='<tr class="'+trClassNIS +' text-center '+trClassMark+'" docnum="'+ itemsDataView[i].Docnum+'" orderNumber="'+ itemsDataView[i].OrderNumber+' "barcode="'+ itemsDataView[i].Barcode+'">'+
'<td>'+showNoFast+
'<span ng-click="openInvoice('+itemsDataView[i].Docnum+')" title="'+ titleText +'" class="'+switchShippingClass+'">'+
switchShipping+
'</span>'+
fastOrderSpan+
'</td>'+
'<td>'+
'<div class="copyDiv">'+
'<i ng-click="searchItems('+itemsDataView[i].OrderNumber+', \'doc\')" title="לחיצה לחיפוש / חזרה מחיפוש" class="'+showSearch+' searchIcon itemClick" aria-hidden="true"></i> '+
'<span title="לחיצה להעתקה" ng-click="clipboardCopyDoc(\'ord\'+'+i+')" id="copyClickDocord'+i+'" class="clipboard itemClick" ngclipboard data-clipboard-text="'+itemsDataView[i].OrderNumber+'"> '+itemsDataView[i].OrderNumber+'</span>'+
'</div>'+
fastOrderDiv+
'<span class="itemClick" ng-click="openOrder('+itemsDataView[i].OrderNumber+')" title="לחיצה לצפייה בפרטי ההזמנה">'+
fastOrdeSpanYesNo+
'</span>'+
'</td>'+
'<td>'+
'<div class="copyDiv">'+
'<i ng-click="searchItems('+itemsDataView[i].Barcode+', \'bar\')" title="לחיצה לחיפוש / חזרה מחיפוש" class="'+showSearch+' searchIcon itemClick" aria-hidden="true"></i> '+
'<span title="לחיצה להעתקה" ng-click="clipboardCopyBar('+i+')" id = "copyClickBar{{'+i+'}}" class = "clipboard itemClick" ngclipboard data-clipboard-text="'+itemsDataView[i].Barcode+'"> '+itemsDataView[i].Barcode+'</span>'+
'</div>'+
'</td>'+
'<td>'+$scope.renderDoc(itemsDataView[i])+'</td>'+
'<td class="itemClick" title="לחיצה להצגת סיריאלים זמינים" ng-click="peekAtSerials('+itemsDataView[i].Barcode+', '+itemsDataView[i].Docnum+','+itemsDataView[i].OrderNumber+')">'+itemsDataView[i].scanned+'/'+itemsDataView[i].OpenQty+'</td>'+
'<td class="notes">'+$scope.renderHtml(itemsDataView[i].Notes)+'</td>'+
'<td>'+itemsDataView[i].cntOrdersToDemand+'/'+ itemsDataView[i].relevantStock+'</td>'+
'<td>'+$scope.getShelf(itemsDataView[i])+'</td>'+
'<td class="balance">'+$scope.renderHtml($scope.getBal(itemsDataView[i].Docbal))+'</td>'+
'</tr>';
}
callBack (tr);
}