This is a migrated thread and some comments may be shown as answers.

Serious performance issues

10 Answers 305 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Lior
Top achievements
Rank 1
Lior asked on 16 Apr 2015, 05:32 AM

Hey,

 

I'm working on a web-app that utilizes Kendo UI Grid & AngularJS.

I'm not trying to do anything crazy, just basic use of displaying results in a grid.

In my testing environment, I load approx 1,800 rows, 11 columns (the loading process loads all rows at once, no client/server paging - just saying ;)).

Once the data is loaded onto the grid, the app becomes unusable, everything is practically frozen. 

 

Are there any known performance issues? are there any guidelines I should follow using Kendo UI Grid  + AngularJS?

I noticed the javascript memory of the app starts at 220M and rising constantly when trying to filter/sort/etc'.

 

In anyway, here's the code for the controller.

Would appreciate the help.

001.define(['app'], function(app) {
002.    app.controller('MainCtrl', function ($scope, $http, $compile, $routeParams, $modal, $timeout, $q, MainService, LoginService) {
003. 
004.        /* ========================================== */
005.        /*           GENERAL INITIALIZATIONS          */
006.        /* ========================================== */
007. 
008.        // Set the culture
009.        kendo.culture("he-IL");
010. 
011.        // Define scope variables
012.        $scope.VM = {};
013.        $scope.VM.isInitialized = false;
014.        $scope.VM.filter = {};
015.        $scope.VM.editor = {};
016.        $scope.VM.collections = {};
017.        $scope.VM.kendoGrid = null;
018. 
019.        // Define the view permissions
020.        $scope.VM.permissions = {
021.            allow_edit: true,
022.            allow_sign: true,
023.            allow_cancel: true
024.        };
025. 
026.        /* ========================================== */
027.        /*        INITIALIZING THE COLLECTIONS        */
028.        /* ========================================== */
029. 
030.        /* The list of all suppliers */
031.        $scope.VM.getSuppliers = function() {
032.            MainService.getSuppliers().then(function(data) {
033.                var suppliers = [];
034.                data.forEach(function (supplier) {
035.                    suppliers.push({ value: supplier.name, text: supplier.name});
036.                });
037.                $scope.VM.collections.suppliers = suppliers;
038.            });
039.        } ();
040.        $scope.VM.collections.suppliers = [];
041.        /* The list of all expensesCause */
042.        $scope.VM.getExpensesCause = function() {
043.            MainService.getExpensesCause().then(function(data) {
044.                var expenses_cause = [];
045.                data.forEach(function (cause) {
046.                    expenses_cause.push({ value: cause.expense_name, text: cause.expense_name});
047.                });
048.                $scope.VM.collections.expensesCause = expenses_cause;
049.            });
050.        } ();
051.        $scope.VM.collections.expensesCause = [];
052.        /* The list of all subExpensesCause */
053.        $scope.VM.getSubExpensesCause = function() {
054.            MainService.getSubExpensesCause().then(function(data) {
055.                var sub_expenses_cause = [];
056.                data.forEach(function (cause) {
057.                    sub_expenses_cause.push({ value: cause.sub_expense_name, text: cause.sub_expense_name});
058.                });
059.                $scope.VM.collections.subExpensesCause = sub_expenses_cause;
060.            });
061.        } ();
062.        $scope.VM.collections.subExpensesCause = [];
063. 
064.        /* ========================================== */
065.        /*      INITIALIZING THE DEFAULT FILTER       */
066.        /* ========================================== */
067. 
068.        $scope.VM.filter.status = "active";
069.        $scope.VM.filter.from_date = moment().subtract(2, "month").toDate();
070.        $scope.VM.filter.to_date = moment().toDate();
071. 
072.        /* ========================================== */
073.        /*            INITIALIZING THE GRID           */
074.        /* ========================================== */
075. 
076.        $scope.VM.dataSource = new kendo.data.DataSource({
077.            schema: {
078.                model: {
079.                    // TODO: change id to date_created:id
080.                    id: "date_created_id",
081.                    fields: {
082.                        date_created: {
083.                            editable: false,
084.                            type: "date"
085.                        },
086.                        project_name: {
087.                            editable: false
088.                        },
089.                        supplier_name: {
090.                            editable: true,
091.                            validation: {
092.                                required: true
093.                            }
094.                        },
095.                        info: {
096.                            editable: true
097.                        },
098.                        expense_cause: {
099.                            editable: true,
100.                            nullable: true
101.                        },
102.                        sub_expense_cause: {
103.                            editable: true,
104.                            nullable: true
105.                        },
106.                        cheque_number: {
107.                            editable: false
108.                        },
109.                        amount: {
110.                            editable: false
111.                        },
112.                        sig1: {
113.                            editable: false
114.                        },
115.                        sig2: {
116.                            editable: false
117.                        },
118.                        cheque_status: {
119.                            editable: false
120.                        }
121.                    }
122.                },
123.                data: "data"
124.            },
125.            //serverPaging: true,
126.            //serverSorting: true,
127.            autoSync: true,
128.            transport: {
129.                read: function(e) {
130.                    // Save the 'e' for future reference
131.                    $scope.VM.readOptions = e;
132. 
133.                    // Start the spinner
134.                    kendo.ui.progress($("div[kendo-grid]"), true);
135. 
136.                    var from_date = $scope.VM.filter.from_date;
137.                    var to_date = $scope.VM.filter.to_date;
138.                    MainService.getCheques(from_date, to_date).then(function(data) {
139.                        e.success(data);
140. 
141.                        // Stop the spinner
142.                        kendo.ui.progress($("div[kendo-grid]"), false);
143.                    });
144.                },
145.                update: function(e) {
146.                    // Save the 'e' for future reference
147.                    $scope.VM.updateOptions = e;
148. 
149.                    MainService.updateCheque(e.data).then(function(data) {
150.                        e.success(data);
151.                    });
152.                },
153.                create: function(e) {
154.                    // Save the 'e' for future reference
155.                    $scope.VM.insertOptions = e;
156. 
157.                    MainService.addCheque(e.data).then(function(data) {
158.                        e.success(data);
159.                    });
160.                },
161.                destroy: function(e) {
162. 
163.                }
164.            },
165.            aggregate: [
166.                { field: "amount", aggregate: "sum" }
167.            ],
168.        });
169.        $scope.VM.projectDS = new kendo.data.DataSource({
170.            data: []
171.        });
172.        $scope.VM.supplierDS = new kendo.data.DataSource({
173.            data: []
174.        });
175.        $scope.VM.infoDS = new kendo.data.DataSource({
176.            data: []
177.        });
178.        $scope.VM.expenseCauseDS = new kendo.data.DataSource({
179.            data: []
180.        });
181.        $scope.VM.subExpenseCauseDS = new kendo.data.DataSource({
182.            data: []
183.        });
184.        $scope.VM.chequeNumberDS = new kendo.data.DataSource({
185.            data: []
186.        });
187.        $scope.VM.amountDS = new kendo.data.DataSource({
188.            data: []
189.        });
190. 
191.        /* The grid definition */
192.        $scope.VM.gridOptions = {
193.            height: 630,
194.            filterable: {
195.                mode: "row",
196.                operators: {
197.                    string: {
198.                        eq: "שווה ",
199.                        neq: "לא שווה",
200.                        startswith: "מתחיל ב",
201.                        contains: "מכיל",
202.                        doesnotcontain: "לא מכיר",
203.                        endswith: "מסתיים ב"
204.                    }
205.                }
206.            },
207.            scrollable: true,
208.            sortable: true,
209.            selectable: "multiple, row",
210.            editable: true,
211.            resizable: true,
212.            reorderable: true,
213.            //groupable: true,
214.            toolbar: [
215.                { name: "excel", text: " ייצוא לאקסל" },
216.                //{ name: "save", text: "שמור"}
217.            ],
218.            excel: { fileName: "results.xlsx" },
219.            dataSource: $scope.VM.dataSource,
220.            columns: [
221.                //{ field: "cheque_id", title: "מזהה צ'ק" },
222.                //{
223.                //    headerTemplate: "<input type='checkbox' class='checkbox checkAll' ng-click='VM.selectAll($event)' />",
224.                //    template: "<input type='checkbox' class='checkbox checkRow' ng-click='VM.selectRow($event)' />",
225.                //    width: "26px"
226.                //},
227.                {
228.                    field: "date_created",
229.                    title: "תאריך",
230.                    template: "<div ng-bind='dataItem.date_created | date: \"dd/MM/yyyy\"' title='{{ dataItem.date_created | date: \"dd/MM/yyyy\" }}'></div>",
231.                    filterable: false
232.                },
233.                {
234.                    field: "project_name",
235.                    title: "פרויקט",
236.                    template: "<div ng-bind='dataItem.project_name' title='{{ dataItem.project_name }}'></div>",
237.                    filterable: {
238.                        cell: {
239.                            dataSource: $scope.VM.dataSource,
240.                            operator: "contains"
241.                        }
242.                    }
243.                },
244.                {
245.                    field: "supplier_name",
246.                    title: "מוטב",
247.                    template: "<div ng-bind='dataItem.supplier_name' title='{{ dataItem.supplier_name }}'></div>",
248.                    filterable: {
249.                        cell: {
250.                            dataSource: $scope.VM.dataSource,
251.                            operator: "contains"
252.                        }
253.                    },
254.                    editor: function(container, options) {
255.                        //container.append(
256.                        //    "<select kendo-combo-box "+
257.                        //        "k-data-text-field=\"'text'\" "+
258.                        //        "k-data-value-field=\"'value'\" "+
259.                        //        "k-filter='contains' "+
260.                        //        "k-auto-bind='false' "+
261.                        //        "k-data-source='VM.collections.suppliers' "+
262.                        //        "data-bind='value:"+options.field+"'>"+
263.                        //    "</select>");
264.                        container.append(
265.                            "<input kendo-auto-complete " +
266.                                "k-data-text-field=\"'text'\" "+
267.                                "k-data-value-field=\"'value'\" "+
268.                                "k-auto-bind='false' "+
269.                                "k-value-primitive='true' "+
270.                                "k-data-source='VM.collections.suppliers' "+
271.                                "data-bind='value:"+options.field+"'/>");
272.                    }
273.                },
274.                {
275.                    field: "info",
276.                    title: "פרטים" ,
277.                    template: "<div ng-bind='dataItem.info' title='{{ dataItem.info }}'></div>",
278.                    filterable: {
279.                        cell: {
280.                            dataSource: $scope.VM.dataSource,
281.                            operator: "contains"
282.                        }
283.                    },
284.                    width: "250px"
285.                },
286.                {
287.                    field: "expense_cause",
288.                    title: "סעיף הוצאה",
289.                    template: "<div ng-bind='dataItem.expense_cause' title='{{ dataItem.expense_cause }}'></div>",
290.                    filterable: {
291.                        cell: {
292.                            dataSource: $scope.VM.dataSource,
293.                            operator: "contains"
294.                        }
295.                    },
296.                    editor: function(container, options) {
297.                        //container.append(
298.                        //    "<select kendo-combo-box "+
299.                        //        "k-data-text-field=\"'text'\" "+
300.                        //        "k-data-value-field=\"'value'\" "+
301.                        //        "k-filter='contains' "+
302.                        //        "k-auto-bind='false' "+
303.                        //        "k-data-source='VM.collections.expensesCause' "+
304.                        //        "data-bind='value:"+options.field+"'>"+
305.                        //    "</select>");
306.                        container.append(
307.                            "<input kendo-auto-complete " +
308.                                "k-data-text-field=\"'text'\" "+
309.                                "k-data-value-field=\"'value'\" "+
310.                                "k-auto-bind='false' "+
311.                                "k-value-primitive='true' "+
312.                                "k-data-source='VM.collections.expensesCause' "+
313.                                "k-rebind='VM.collections.expensesCause' "+
314.                                "data-bind='value:"+options.field+"'/>");
315.                    }
316.                },
317.                {
318.                    field: "sub_expense_cause",
319.                    title: "תת סעיף הוצאה",
320.                    template: "<div ng-bind='dataItem.sub_expense_cause' title='{{ dataItem.sub_expense_cause }}'></div>",
321.                    filterable: {
322.                        cell: {
323.                            dataSource: $scope.VM.dataSource,
324.                            operator: "contains"
325.                        }
326.                    },
327.                    editor: function(container, options) {
328.                        //container.append(
329.                        //    "<select kendo-combo-box "+
330.                        //        "k-data-text-field=\"'text'\" "+
331.                        //        "k-data-value-field=\"'value'\" "+
332.                        //        "k-filter='contains' "+
333.                        //        "k-auto-bind='false' "+
334.                        //        "k-data-source='VM.collections.subExpensesCause' "+
335.                        //        "data-bind='value:"+options.field+"'>"+
336.                        //    "</select>");
337.                        container.append(
338.                            "<input kendo-auto-complete " +
339.                                "k-data-text-field=\"'text'\" "+
340.                                "k-data-value-field=\"'value'\" "+
341.                                "k-auto-bind='false' "+
342.                                "k-value-primitive='true' "+
343.                                "k-data-source='VM.collections.subExpensesCause' "+
344.                                "k-rebind='VM.collections.subExpensesCause' "+
345.                                "data-bind='value:"+options.field+"'/>");
346.                    }
347.                },
348.                {
349.                    field: "cheque_number",
350.                    title: "מספר צ'ק",
351.                    template: "<div ng-bind='dataItem.cheque_number' title='{{ dataItem.cheque_number }}'></div>",
352.                    filterable: {
353.                        cell: {
354.                            dataSource: $scope.VM.dataSource,
355.                            operator: "startswith"
356.                        }
357.                    }
358.                },
359.                {
360.                    field: "amount",
361.                    title: "סכום" ,
362.                    template: "<div ng-bind='dataItem.amount | currency: \"₪\"' title='{{ dataItem.amount | currency: \"₪\" }}'></div>",
363.                    format: "{0:c0}",
364.                    footerTemplate: "<div>{{ translation.TEXT_AMOUNT_SUM }}: #= kendo.toString(sum, 'c0') #</div>",
365.                    width: "180px"
366.                },
367.                {
368.                    field: "sig1",
369.                    title: "חתימה 1",
370.                    template: function(dataItem) {
371.                        var template;
372.                        var chequeId = dataItem.uid;
373. 
374.                        // TODO: need to convert date from string to actual date
375. 
376.                        template =  "<div ng-mouseover=\"(loggedInUser.signer == 1) && VM.showQuickSign(1, '"+chequeId+"')\" ng-mouseleave=\"(loggedInUser.signer == 1) && VM.hideQuickSign(1, '"+chequeId+"')\" title=\"{{ (dataItem.sig1_signAt) ? ((convertStringToDate(dataItem.sig1_signAt)) | date: 'HH:mm:ss dd/MM/yyyy') : '' }}\">" +
377.                                        "{{ dataItem.sig2_user || ' ' }}" +
378.                                        "<div class='quicksign bubble bubble-sig1-"+ chequeId +" bottom'>" +
379.                                            "<div class='quicksign-user'>{{ loggedInUser.full_name }}</div>" +
380.                                            "<div class='quicksign-amount'>{{ dataItem.amount | currency: '₪' }}</div>" +
381.                                            "<div class='quicksign-sign' ng-show='VM.isAllowSign() && !VM.isSigned(this.dataItem)'><button class='k-button' ng-click='VM.sign(this.dataItem)'>{{ translation.CMD_SIGN }}</button></div>" +
382.                                            "<div class='quicksign-unsign' ng-show='VM.isAllowSign() && VM.isSigned(this.dataItem)'><button class='k-button' ng-click='VM.unsign(this.dataItem)'>{{ translation.CMD_UNSIGN }}</button></div>" +
383.                                        "</div>" +
384.                                    "</div>";
385. 
386.                        return template;
387.                    },
388.                    filterable: false
389.                },
390.                {
391.                    field: "sig2",
392.                    title: "חתימה 2",
393.                    template: function(dataItem) {
394.                        var template;
395.                        var chequeId = dataItem.uid;
396. 
397.                        // TODO: need to convert date from string to actual date
398. 
399.                        template =  "<div ng-mouseover=\"(loggedInUser.signer == 2) && VM.showQuickSign(2, '"+chequeId+"')\" ng-mouseleave=\"(loggedInUser.signer == 2) && VM.hideQuickSign(2, '"+chequeId+"')\" title=\"{{ (dataItem.sig2_signAt) ? ((convertStringToDate(dataItem.sig2_signAt)) | date: 'HH:mm:ss dd/MM/yyyy') : '' }}\">" +
400.                                        "{{ dataItem.sig2_user || ' ' }}" +
401.                                        "<div class='quicksign bubble bubble-sig2-"+ chequeId +" bottom'>" +
402.                                            "<div class='quicksign-user'>{{ loggedInUser.full_name }}</div>" +
403.                                            "<div class='quicksign-amount'>{{ dataItem.amount | currency: '₪' }}</div>" +
404.                                            "<div class='quicksign-sign' ng-show='VM.isAllowSign() && !VM.isSigned(this.dataItem)'><button class='k-button' ng-click='VM.sign(this.dataItem)'>{{ translation.CMD_SIGN }}</button></div>" +
405.                                            "<div class='quicksign-unsign' ng-show='VM.isAllowSign() && VM.isSigned(this.dataItem)'><button class='k-button' ng-click='VM.unsign(this.dataItem)'>{{ translation.CMD_UNSIGN }}</button></div>" +
406.                                        "</div>" +
407.                                    "</div>";
408. 
409.                        return template;
410.                    },
411.                    filterable: false
412.                },
413.                {
414.                    field: "cheque_status",
415.                    title: "סטאטוס",
416.                    template: function(dataItem) {
417.                        var result = "";
418.                        var status = $scope.translationAPI.translateStatus(dataItem.cheque_status);
419.                        var css_class = dataItem.cheque_status;
420. 
421.                        result += "<div title='"+status+"' class='"+css_class+"'>";
422.                        result += status;
423.                        result += "</div>";
424. 
425.                        return result;
426.                    },
427.                    filterable: false
428.                }
429.            ],
430. 
431.            // Events
432.            dataBound: function(e) {
433.                $scope.VM.isInitialized = true;
434.            }
435.        };
436. 
437.        /* ========================================== */
438.        /*                  Methods                   */
439.        /* ========================================== */
440. 
441.        ///////////////////////////////////// Common
442.        // Gets a reference to the grid
443.        $scope.VM.getKendoGrid = function() {
444.            if (!$scope.VM.kendoGrid) {
445.                var grid = $("div[kendo-grid]");
446.                $scope.VM.kendoGrid = grid.getKendoGrid();
447.            }
448. 
449.            return $scope.VM.kendoGrid;
450.        };
451. 
452.        ///////////////////////////////////// Permissions
453.        // Check if the user is allowed to sign
454.        $scope.VM.isAllowSign = function() {
455.            return $scope.VM.permissions.allow_sign;
456.        };
457.        // Check if the user is allowed to unsign
458.        $scope.VM.isAllowUnSign = function() {
459.            return $scope.VM.permissions.allow_sign;
460.        };
461.        // Check if the user is allowed to edit existing items
462.        $scope.VM.isAllowEdit = function() {
463.            return $scope.VM.permissions.allow_edit;
464.        };
465.        // Check if the user is allowed to add new items
466.        $scope.VM.isAllowAdd = function() {
467.            return $scope.VM.permissions.allow_edit;
468.        };
469.        // Check if the user is allowed to cancel cheques
470.        $scope.VM.isAllowCancel = function() {
471.            return $scope.VM.permissions.allow_cancel;
472.        };
473. 
474.        ///////////////////////////////////// Helper methods
475.        // Check if at-least one row selected
476.        $scope.VM.isEntitySelected = function() {
477.            return ($scope.VM.getSelectedRowsCount() > 0);
478.        };
479.        // Check if exactly one row selected
480.        $scope.VM.isOneEntitySelected = function() {
481.            return ($scope.VM.getSelectedRowsCount() == 1);
482.        };
483.        // Check if the cheque is already signed
484.        $scope.VM.isSigned = function(dataItem) {
485.            if ($scope.loggedInUser.signer == 1)
486.                return dataItem.sig1_user && dataItem.sig1_signAt;
487.            else if ($scope.loggedInUser.signer == 2)
488.                return dataItem.sig2_user && dataItem.sig2_signAt;
489.            else
490.                return false;
491.        };
492.        // Get the number of rows in the grid
493.        $scope.VM.getRowsCount = function() {
494.            var isInitialized = $scope.VM.isInitialized;
495.            if (!isInitialized)
496.                return 0;
497. 
498.            var grid = $scope.VM.getKendoGrid();
499.            if (!grid)
500.                return 0;
501. 
502.            var results = grid.dataItems();
503.            if (!results)
504.                return 0;
505. 
506.            return results.length;
507.        };
508.        // Get the number of selected rows from the grid
509.        $scope.VM.getSelectedRowsCount = function() {
510.            var isInitialized = $scope.VM.isInitialized;
511.            if (!isInitialized)
512.                return 0;
513. 
514.            var grid = $scope.VM.getKendoGrid();
515.            if (!grid)
516.                return 0;
517. 
518.            var results = grid.select();
519.            if (!results)
520.                return 0;
521. 
522.            return results.length;
523.        };
524.        // Get the selected row
525.        $scope.VM.getSelectedRow = function() {
526.            var grid = $scope.VM.getKendoGrid();
527.            if (!grid)
528.                return;
529. 
530.            var results = grid.select();
531.            if (!results)
532.                return;
533. 
534.            return grid.dataItem(results[0]);
535.        };
536.        // Get the number of results by statys
537.        $scope.VM.getStatusCount = function(status) {
538.            var grid = $scope.VM.getKendoGrid();
539.            if (!grid)
540.                return;
541. 
542.            var results = grid.dataSource.data();
543.            if (!results)
544.                return;
545. 
546.            var count = 0;
547.            if (status === "all") {
548.              count = results.length;
549.            } else if (status === "active") {
550.                angular.forEach(results, function(value,key) {
551.                    if (value.is_canceled == "N")
552.                        count++;
553.                });
554.            } else {
555.                angular.forEach(results, function(value,key) {
556.                    if (value.cheque_status == status)
557.                        count++;
558.                });
559.            }
560. 
561.            return count;
562.        };
563. 
564.        ///////////////////////////////////// Commands
565.        // Opens the entity detail view
566.        $scope.VM.openEntityModal = function(state) {
567.            var instance = $modal.open({
568.                templateUrl: 'addNewModal.html',
569.                controller: 'AddNewCtrl',
570.                size: 'lg',
571.                resolve: {
572.                    entity: function(){
573.                        var grid = $scope.VM.getKendoGrid();
574.                        var results = grid.select();
575.                        return grid.dataItem(results[0]);
576.                    },
577.                    state: function() {
578.                        return state;
579.                    },
580.                    translation: function() {
581.                        return $scope.translation;
582.                    },
583.                    translationAPI: function() {
584.                        return $scope.translationAPI;
585.                    },
586.                    permissions: function() {
587.                        return $scope.VM.permissions;
588.                    },
589.                    loggedInUser: function() {
590.                        return $scope.loggedInUser;
591.                    }
592.                }
593.            });
594. 
595.            instance.result.then(function(entity) {
596.                if (entity) {
597.                    if (state) {
598.                        $scope.VM.addNew(entity);
599.                    }
600.                    else {
601.                        $scope.VM.update(entity);
602.                    }
603. 
604.                    // Save the changes
605.                    $scope.VM.dataSource.sync();
606.                }
607.            });
608.        };
609.        // Add a new entity
610.        $scope.VM.addNew = function(entity){
611.            $scope.VM.dataSource.add(entity)
612.        };
613.        // Update the entity
614.        $scope.VM.update = function(entity){
615. 
616.        };
617.        // Sign a cheque
618.        $scope.VM.sign = function(dataItem) {
619.            if ($scope.loggedInUser.signer == 1) {
620.                dataItem.sig1_user = $scope.loggedInUser.full_name;
621.                dataItem.sig1_signAt = moment().toDate();
622.            }
623.            else {
624.                dataItem.sig2_user = $scope.loggedInUser.full_name;
625.                dataItem.sig2_signAt = moment().toDate();
626.            }
627. 
628.            // Save the changes
629.            $scope.VM.dataSource.sync();
630.        };
631.        // Sign all selected cheques
632.        $scope.VM.signAll = function() {
633.            var isInitialized = $scope.VM.isInitialized;
634.            if (!isInitialized)
635.                return;
636. 
637.            var grid = $scope.VM.getKendoGrid();
638.            if (!grid)
639.                return;
640. 
641.            var results = grid.select();
642.            if (!results)
643.                return;
644. 
645.            for (i=0; i<results.length; i++) {
646.                $scope.VM.sign(grid.dataItem(results[i]));
647.            }
648.        };
649.        // Sign a cheque
650.        $scope.VM.unsign = function(dataItem) {
651.            if ($scope.loggedInUser.signer == 1) {
652.                dataItem.sig1_user = "";
653.                dataItem.sig1_signAt = "";
654.            }
655.            else {
656.                dataItem.sig2_user = "";
657.                dataItem.sig2_signAt = "";
658.            }
659. 
660.            // Save the changes
661.            $scope.VM.dataSource.sync();
662.        };
663.        // Sign all selected cheques
664.        $scope.VM.unsignAll = function() {
665.            var isInitialized = $scope.VM.isInitialized;
666.            if (!isInitialized)
667.                return;
668. 
669.            var grid = $scope.VM.getKendoGrid();
670.            if (!grid)
671.                return;
672. 
673.            var results = grid.select();
674.            if (!results)
675.                return;
676. 
677.            for (i=0; i<results.length; i++) {
678.                $scope.VM.unsign(grid.dataItem(results[i]));
679.            }
680.        };
681.        // Cancel the cheque
682.        $scope.VM.cancelAction = function(entity) {
683.            var entity = $scope.VM.getSelectedRow();
684. 
685.            entity.is_canceled = "Y";
686. 
687.            /* Send the new entity to the server for cancel */
688.            MainService.modifyCancellation(entity).then(function(data) {
689. 
690.            });
691.        };
692.        // Undo canceled cheque
693.        $scope.VM.undoCancelAction = function(entity) {
694.            var entity = $scope.VM.getSelectedRow();
695. 
696.            entity.is_canceled = "N";
697. 
698.            /* Send the new entity to the server for cancel */
699.            MainService.modifyCancellation(entity).then(function(data) {
700. 
701.            });
702.        };
703.        // Get all cheques from within the dates range
704.        $scope.VM.getCheques = function(e) {
705.            $scope.VM.dataSource.read($scope.VM.readOptions);
706.        };
707.        // Refresh the data, if any changes has been made
708.        var _chequeFlowInProgress = false;
709.        $scope.VM.getChequesFlow = function() {
710.            (function tick() {
711.                var project = $scope.VM.filter.project || "";
712.                var from_date = $scope.VM.filter.from_date;
713.                var to_date = $scope.VM.filter.to_date;
714. 
715.                if (_chequeFlowInProgress)
716.                    return;
717. 
718.                MainService.getChequesFlow(project, from_date, to_date).then(function(data) {
719.                    _chequeFlowInProgress = true;
720. 
721.                    var index, jndex;
722.                    for (index=0; index<data.length; index++) {
723.                        var action = data[index].type;
724.                        var flag = false;
725. 
726.                        var grid = $scope.VM.getKendoGrid();
727.                        if (!grid)
728.                            return;
729. 
730.                        var results = grid.dataItems();
731.                        if (!results)
732.                            return;
733. 
734.                        for (jndex=0; jndex<results.length && !flag; jndex++){
735.                            var currRow = results[jndex];
736.                            switch (action) {
737.                                case "update": {
738.                                    if (data[index]["date_created:id"] == currRow["date_created:id"]) {
739.                                        currRow.action = data[index].action;
740.                                        currRow.project_name = data[index].project_name;
741.                                        currRow.expense_cause = data[index].expense_cause;
742.                                        currRow.sub_expense_cause = data[index].sub_expense_cause;
743.                                        currRow.info = data[index].info;
744.                                        currRow.supplier_name = data[index].supplier_name;
745.                                        currRow.supplier_address = data[index].supplier_address;
746.                                        currRow.amount = data[index].amount;
747.                                        currRow.cheque_number = data[index].cheque_number;
748.                                        currRow.date_payment = data[index].date_payment;
749.                                        currRow.sig1_user = data[index].sig1_user;
750.                                        currRow.sig1_signAt = data[index].sig1_signAt;
751.                                        currRow.sig2_user = data[index].sig2_user;
752.                                        currRow.sig2_signAt = data[index].sig2_signAt;
753.                                        currRow.cheque_status = data[index].cheque_status;
754.                                        currRow.date_created = data[index].date_created;
755.                                        currRow.date_uploaded = data[index].date_uploaded;
756.                                        currRow.date_updated = data[index].date_updated;
757.                                        currRow.tax_deduction_percentage = data[index].tax_deduction_percentage;
758.                                        currRow.tax_deduction_sum = data[index].tax_deduction_sum;
759.                                        currRow.is_canceled = data[index].is_canceled;
760.                                        currRow.date_canceled = data[index].date_canceled;
761. 
762.                                        // Mark iteration as completed
763.                                        flag = true;
764.                                    }
765.                                    break;
766.                                }
767.                                case "insert": {
768.                                    if (data[index]["date_created:id"] == currRow["date_created:id"]) {
769.                                        // Mark iteration as completed
770.                                        flag = true;
771.                                    }
772.                                    break;
773.                                }
774.                                default: {
775.                                    if (action)
776.                                        console.error("Can't load entity from flow, unknown state '"+action+"'");
777.                                    else
778.                                        console.error("Can't load entity from flow, the 'type' is missing");
779.                                }
780.                            }
781.                        }
782. 
783.                        if (!flag) {
784.                            if (action == "insert") {
785.                                // TODO: insert the item to the grid
786.                                grid.dataSource.add(data[index]);
787.                            }
788.                        }
789.                    };
790. 
791.                    _chequeFlowInProgress = false;
792.                });
793. 
794.                var timer = $timeout(tick, 1000);
795.                $scope.$on("$destroy", function(event) {
796.                    $timeout.cancel(timer);
797.                });
798.            })();
799.        }();
800. 
801.        ///////////////////////////////////// Presentation
802.        // Responsible for filtering the grid by the status field
803.        $scope.VM.changeStatus = function(e, status) {
804.            e.preventDefault();
805. 
806.            $scope.VM.filter.status = status;
807.            switch (status) {
808.                case "all": {
809.                    $scope.VM.dataSource.filter({} );
810.                    break;
811.                }
812.                case "active": {
813.                    $scope.VM.dataSource.filter({ field: "is_canceled", operator: "eq", value: "N"} );
814.                    break;
815.                }
816.                case "unsigned":
817.                case "pending":
818.                case "signed": {
819.                    $scope.VM.dataSource.filter({ field: "cheque_status", operator: "eq", value: status} );
820.                    break;
821.                }
822.            }
823.        };
824.        // Save the position of the mouse
825.        $(document).mousemove(function(e) {
826.            window.x = e.pageX;
827.            window.y = e.pageY;
828.        });
829.        // Show the quicksign panel
830.        $scope.VM.showQuickSign = function(sig, chequeId) {
831.            var handle = $(".bubble-sig"+sig+"-"+chequeId);
832.            var isVisible = $(".bubble-sig"+sig+"-"+chequeId+":visible");
833. 
834.            if (isVisible.length > 0)
835.                return;
836. 
837.            var grid = $("div[kendo-grid]");
838.            var gridHalfPosition = grid.offset().top + (grid.height() / 2);
839. 
840.            if (window.y < gridHalfPosition) {
841.                handle.removeClass("bottom");
842.                handle.addClass("top");
843.            }
844.            else {
845.                handle.removeClass("top");
846.                handle.addClass("bottom");
847.            }
848. 
849.            handle.show();
850.        };
851.        // Hide the quicksign panel
852.        $scope.VM.hideQuickSign = function(sig, chequeId) {
853.            var handle = $(".bubble-sig"+sig+"-"+chequeId);
854.            handle.hide();
855.        };
856.    });
857. 
858.});

10 Answers, 1 is accepted

Sort by
0
T. Tsonev
Telerik team
answered on 20 Apr 2015, 07:23 AM
Hi,

Please accept my apologies for the delayed response.
I don't have all the dependencies to run the application, but the provided code still provides some clues on what's going on.

Initializing this many bindings in Angular will be costly, with our grid or not.
As you can see there's a significant delay and memory increase just from initializing ~20k bindings.
Add everything else on the page and you'll get the situation that you describe.

Our only recommendation at the moment is to enable paging.
Does it improve the performance in your actual application?

Regards,
T. Tsonev
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Lior
Top achievements
Rank 1
answered on 20 Apr 2015, 08:28 PM

Hey,

 

Thanks for your response.

Is using #: xxx # better than ng-bind, performance wise?

(I switch to #: xxx #where I currently could)

 

Regarding your suggestion to using pagination,

I tried to apply it but once the data loads, the pagination still contains 0 pages, even though there should be a-lot more.

I probably did something wrong, here's the code:

 The view:

001.<div ng-controller="DashboardCtrl" id="dashboard" class="k-rtl">
002.    <div ng-controller="MainCtrl" ng-init="init()">
003.        <nav id="navigation" class="navbar navbar-default" role="navigation">
004.            <div class="container-fluid">
005. 
006.                <div class="collapse navbar-collapse pull-right" id="bs-example-navbar-collapse-1">
007.                    <a class="navbar-brand" href="/#/main/all">
008.                        <img alt="SG" src="img/logo.png">
009.                    </a>
010.                    <ul class="nav navbar-nav">
011.                        <li><a href="/#/main/all">{{ translation.NAV_MAIN }}</a></li>
012.                        <li><a href="/#/import">{{ translation.NAV_IMPORT }}</a></li>
013.                    </ul>
014.                </div><!-- /.navbar-collapse -->
015. 
016.                <img alt="{{ loggedInUser.company }}" src="img/logos/{{ loggedInUser.company }}.png" ng-show="loggedInUser.company != ''" class="company-logo">
017. 
018.                <div class="navbar-header pull-left">
019.                    <div class="navbar-logged-in-user pull-right">
020.                        <img src="img/icon-user.png" width="36" height="36"/>
021.                    </div>
022.                    <div class="navbar-text pull-right">
023.                        {{ loggedInUser.full_name }}
024.                        <br/>
025.                        <a href="/api/logout"><strong>{{ translation.NAV_LOGOUT }}</strong></a>
026.                    </div>
027.                </div><!-- /.navbar-header -->
028.            </div>
029.        </nav>
030.        <div id="container">
031.            <aside id="filter-status">
032.                <ul class="nav nav-pills nav-stacked">
033.                    <li role="presentation" class="cheque_status clickable" ng-class="{'active': VM.filter.status == 'active'}"><a href="#" ng-click="VM.changeStatus($event, 'active')">{{ translation.STATUS_ACTIVE }} <span class="badge">{{ (VM.activeStatusCount || 0) | number:0 }}</span></a></li>
034.                    <li role="presentation" class="cheque_status clickable" ng-class="{'active': VM.filter.status == 'unsigned'}"><a href="#" ng-click="VM.changeStatus($event, 'unsigned')">{{ translation.STATUS_UNSIGNED }} <span class="badge">{{ (VM.unsignedStatusCount || 0) | number:0 }}</span></a></li>
035.                    <li role="presentation" class="cheque_status clickable" ng-class="{'active': VM.filter.status == 'pending'}"><a href="#" ng-click="VM.changeStatus($event, 'pending')">{{ translation.STATUS_PENDING }} <span class="badge">{{ (VM.pendingStatusCount || 0) | number:0 }}</span></a></li>
036.                    <li role="presentation" class="cheque_status clickable" ng-class="{'active': VM.filter.status == 'signed'}"><a href="#" ng-click="VM.changeStatus($event, 'signed')">{{ translation.STATUS_SIGNED }} <span class="badge">{{ (VM.signedStatusCount || 0) | number:0 }}</span></a></li>
037.                    <li role="presentation" class="cheque_status clickable" ng-class="{'active': VM.filter.status == 'all'}"><a href="#" ng-click="VM.changeStatus($event, 'all')">{{ translation.STATUS_ALL }} <span class="badge">{{ (VM.allStatusCount || 0) | number:0 }}</span></a></li>
038.                </ul>
039.            </aside>
040.            <section id="content">
041.                <div id="toolbar" class="row">
042.                    <div class="col-lg-12">
043.                        <div id="command-sign" class="btn-group" role="group" aria-label="Sign">
044.                            <button type="button" class="btn btn-lg btn-default" ng-disabled="!VM.isEntitySelected || !VM.isAllowSign" ng-click="VM.signAll()">{{ translation.CMD_SIGN }}</button>
045.                            <button type="button" class="btn btn-lg btn-default" ng-disabled="!VM.isEntitySelected || !VM.isAllowUnSign" ng-click="VM.unsignAll()">{{ translation.CMD_UNSIGN }}</button>
046.                        </div>
047.                        <div id="command-manage" class="btn-group" role="group" aria-label="Manage">
048.                            <button type="button" class="btn btn-lg btn-default" ng-click="VM.openEntityModal(true)" ng-disabled="!VM.isAllowAdd"><img src="img/icon-plus.png" width="20" height="20"/> {{ translation.CMD_ADD_NEW }}</button>
049.                            <button type="button" class="btn btn-lg btn-default" ng-click="VM.openEntityModal(false)" ng-disabled="!VM.isAllowEdit || !VM.isOneEntitySelected"><img src="img/icon-pencil.png" width="20" height="20"/> {{ translation.CMD_EDIT }}</button>
050.                        </div>
051.                        <div id="command-cancel" class="btn-group" role="group" aria-label="Cancel" ng-show="VM.isOneEntitySelected && !VM.isFromSAP() && !VM.isCanceled()">
052.                            <button type="button" class="btn btn-lg btn-default" ng-click="VM.cancelActionOnce()"><img src="img/icon-trash.png" width="20" height="20"/> {{ translation.CMD_CANCEL }}</button>
053.                        </div>
054.                        <div id="command-undo" class="btn-group" role="group" aria-label="Undo" ng-show="VM.isOneEntitySelected && !VM.isFromSAP() && VM.isCanceled()">
055.                            <button type="button" class="btn btn-lg btn-default" ng-click="VM.undoActionOnce()"><img src="img/icon-undo.png" width="20" height="20"/> {{ translation.CMD_UNDO_CANCEL }}</button>
056.                        </div>
057.                    </div>
058.                </div>
059. 
060.                <div class="row">
061.                    <div class="col col-lg-12">
062.                        <div id="filter" class="well">
063.                            <form role="form" action="" method="post" name="filterForm" class="form-inline" novalidate>
064.                                <!--<div class="form-group hide">
065.                                    <label for="filterProject">{{ translation.FILTER_PROJECT }}</label>
066.                                    <input type="text"
067.                                           class="form-control"
068.                                           id="filterProject"
069.                                           name="filter[project]"
070.                                           ng-model="VM.filter.project"
071.                                           typeahead="project for project in VM.collections.projects | filter:$viewValue"
072.                                           typeahead-focus-first>
073.                                </div>-->
074.                                <div class="form-group">
075.                                    <label for="filterFromDate">{{ translation.FILTER_FROM_DATE }}</label>
076.                                    <p class="input-group">
077.                                        <input id="filterFromDate" kendo-date-picker
078.                                               ng-model="VM.filter.from_date"
079.                                               data-k-format="'dd/MM/yyyy'"
080.                                               k-ng-model="VM.filter.from_date"
081.                                               k-rebind="VM.filter.to_date"
082.                                               k-max="VM.filter.to_date"
083.                                               k-on-change="VM.getCheques()" />
084.                                    </p>
085.                                </div>
086.                                <div class="form-group">
087.                                    <label for="filterToDate">{{ translation.FILTER_TO_DATE }}</label>
088.                                    <p class="input-group">
089.                                        <input id="filterToDate" kendo-date-picker
090.                                               ng-model="VM.filter.to_date"
091.                                               data-k-format="'dd/MM/yyyy'"
092.                                               k-ng-model="VM.filter.to_date"
093.                                               k-rebind="VM.filter.from_date"
094.                                               k-min="VM.filter.from_date"
095.                                               k-on-change="VM.getCheques()" />
096.                                    </p>
097.                                </div>
098.                            </form>
099.                        </div>
100.                    </div>
101.                </div>
102. 
103.                <div id="grid" class="row">
104.                    <div class="col-lg-12">
105.                        <div id="chequesGrid">
106.                            <kendo-grid options="VM.gridOptions">
107.                            </kendo-grid>
108.                        </div>
109.                    </div>
110.                </div>
111.                <div id="stats" class="row">
112.                    <div class="col-lg-12">
113.                        <span>{{ translation.TEXT_ALL_ROWS }}: </span><strong>{{ (VM.rowsCount || 0) }}</strong></span>
114.                        <br/>
115.                        <span>{{ translation.TEXT_SELECTED_ROWS }}: </span><strong>{{ (VM.selectedRowsCount || 0) }}</strong>
116.                    </div>
117.                </div>
118. 
119.            </section>
120.        </div>
121.        </div>
122.        <script type="text/ng-template" id="addNewModal.html">
123.            <div id="editor">
124.                <div class="modal-header">
125.                    <h3 class="modal-title text-center" ng-show="!VM.state">{{ translation.TITLE_EDIT_CHEQUE }}</h3>
126.                    <h3 class="modal-title text-center" ng-show="VM.state">{{ translation.TITLE_ADD_NEW }}</h3>
127.                </div>
128.                <div class="modal-body">
129.                    <form role="form" action="" method="post" id="editor" class="form-horizontal">
130.                        <div class="row">
131.                            <div class="col-lg-6">
132.                                <div class="form-group col-lg-12">
133.                                    <label for="editorAction" class="col-lg-4">{{ translation.EDITOR_ACTION }}</label>
134.                                    <div class="col-lg-8" style="padding-left: 0;">
135.                                        <select class="form-control"
136.                                                id="editorAction"
137.                                                name="editor[action]"
138.                                                title="{{VM.entity.action}}"
139.                                                ng-model="VM.entity.action"
140.                                                ng-options="action for action in VM.collections.actions">
141.                                        </select>
142.                                    </div>
143.                                </div>
144.                            </div>
145.                            <div class="col-lg-6 text-left">
146.                                <div id="editor-command-cancel" class="btn-group pull-left" role="group" aria-label="Cancel" ng-show="!VM.isFromSAP() && !VM.isCanceled()" style="margin-left: -15px;">
147.                                    <button type="button" class="btn btn-lg btn-default" ng-click="VM.cancelAction()"><img src="img/icon-trash.png" width="20" height="20"/> {{ translation.CMD_CANCEL }}</button>
148.                                </div>
149.                                <div id="editor-command-undo" class="btn-group pull-left" role="group" aria-label="Undo" ng-show="!VM.isFromSAP() && VM.isCanceled()" style="margin-left: -15px;">
150.                                    <button type="button" class="btn btn-lg btn-default" ng-click="VM.undoAction()"><img src="img/icon-undo.png" width="20" height="20"/> {{ translation.CMD_UNDO_CANCEL }}</button>
151.                                </div>
152.                            </div>
153.                        </div>
154.                        <div class="row">
155.                            <div class="col-lg-12">
156.                                <div class="form-group col-lg-6">
157.                                    <label for="editorProjectName" class="col-lg-4">{{ translation.EDITOR_PROJECT_NAME }}<span ng-show="true"> *</span></label>
158.                                    <div class="col-lg-8">
159.                                        <input type="text"
160.                                               class="form-control"
161.                                               id="editorProjectName"
162.                                               name="editor[project_name]"
163.                                               title="{{VM.entity.project_name}}"
164.                                               ng-model="VM.entity.project_name"
165.                                               ng-required="true"
166.                                               typeahead="project for project in VM.collections.projects | filter:$viewValue"
167.                                               typeahead-focus-first>
168.                                    </div>
169.                                </div>
170.                            </div>
171.                        </div>
172.                        <div class="row">
173.                            <div class="col-lg-12">
174.                                <div class="form-group col-lg-6">
175.                                    <label for="editorExpenseCause" class="col-lg-4">{{ translation.EDITOR_EXPENSE_CAUSE }}: </label>
176.                                    <div class="col-lg-8">
177.                                        <input type="text"
178.                                               class="form-control"
179.                                               id="editorExpenseCause"
180.                                               name="editor[editorExpenseCause]"
181.                                               title="{{VM.entity.expense_cause}}"
182.                                               ng-model="VM.entity.expense_cause"
183.                                               typeahead="cause for cause in VM.collections.expensesCause | filter:$viewValue"
184.                                               typeahead-focus-first>
185.                                    </div>
186.                                </div>
187.                                <div class="form-group col-lg-6">
188.                                    <label for="editorSubExpenseCause" class="col-lg-4">{{ translation.EDITOR_SUB_EXPENSE_CAUSE }} </label>
189.                                    <div class="col-lg-8">
190.                                        <input type="text"
191.                                               class="form-control"
192.                                               id="editorSubExpenseCause"
193.                                               name="editor[editorSubExpenseCause]"
194.                                               title="{{VM.entity.sub_expense_cause}}"
195.                                               ng-model="VM.entity.sub_expense_cause"
196.                                               typeahead="cause for cause in VM.collections.subExpensesCause | filter:$viewValue"
197.                                               typeahead-focus-first>
198.                                    </div>
199.                                </div>
200.                            </div>
201.                        </div>
202.                        <div class="row">
203.                            <div class="col-lg-12">
204.                                <div class="form-group col-lg-12">
205.                                    <label for="editorInfo" class="col-lg-2">{{ translation.EDITOR_INFO }}<span ng-show="true"> *</span></label>
206.                                    <div class="col-lg-10">
207.                                        <textarea type="text"
208.                                               class="form-control"
209.                                               id="editorInfo"
210.                                               name="editor[info]"
211.                                               title="{{VM.entity.info}}"
212.                                               ng-model="VM.entity.info"
213.                                               ng-required="true"></textarea>
214.                                    </div>
215.                                </div>
216.                            </div>
217.                        </div>
218.                        <div class="row">
219.                            <div class="col-lg-12">
220.                                <div class="form-group col-lg-6">
221.                                    <label for="editorSupplierName" class="col-lg-4">{{ translation.EDITOR_CREDIT }}<span ng-show="true"> *</span></label>
222.                                    <div class="col-lg-8">
223.                                        <input type="text"
224.                                               class="form-control"
225.                                               id="editorSupplierName"
226.                                               name="editor[supplier_name]"
227.                                               title="{{VM.entity.supplier_name}}"
228.                                               ng-model="VM.entity.supplier_name"
229.                                               ng-required="true"
230.                                               typeahead="company for company in VM.collections.companies | filter:$viewValue"
231.                                               typeahead-focus-first>
232.                                    </div>
233.                                </div>
234.                                <div class="form-group col-lg-6">
235.                                    <label for="editorSupplierAddress" class="col-lg-4">{{ translation.EDITOR_SUPPLIER_ADDRESS }} </label>
236.                                    <div class="col-lg-8">
237.                                        <input type="text"
238.                                               class="form-control"
239.                                               id="editorSupplierAddress"
240.                                               name="editor[supplier_address]"
241.                                               title="{{VM.entity.supplier_address}}"
242.                                               ng-model="VM.entity.supplier_address">
243.                                    </div>
244.                                </div>
245.                            </div>
246.                        </div>
247.                        <div class="row">
248.                            <div class="col-lg-12">
249.                                <div class="form-group col-lg-6">
250.                                    <label for="editorChequeAmount" class="col-lg-4">{{ translation.EDITOR_CHEQUE_SUM }}<span ng-show="true"> *</span></label>
251.                                    <div class="col-lg-8">
252.                                        <input type="number"
253.                                               class="form-control"
254.                                               id="editorChequeAmount"
255.                                               name="editor[amount]"
256.                                               title="{{VM.entity.amount}}"
257.                                               ng-model="VM.entity.amount"
258.                                               ng-required="true">
259.                                    </div>
260.                                </div>
261.                            </div>
262.                        </div>
263.                        <div class="row" ng-show="VM.isCheque()">
264.                            <div class="col-lg-12">
265.                                <div class="form-group col-lg-6">
266.                                    <label for="editorChequeNumber" class="col-lg-4">{{ translation.EDITOR_CHEQUE_NUMBER }}<span ng-show="true"> *</span></label>
267.                                    <div class="col-lg-8">
268.                                        <input type="text"
269.                                               class="form-control"
270.                                               id="editorChequeNumber"
271.                                               name="editor[cheque_number]"
272.                                               title="{{VM.entity.cheque_number}}"
273.                                               ng-model="VM.entity.cheque_number"
274.                                               ng-required="VM.isCheque()">
275.                                    </div>
276.                                </div>
277.                                <div class="form-group col-lg-6">
278.                                    <label for="editorChequeNumber" class="col-lg-4">{{ translation.EDITOR_DATE_PAYMENT }}<span ng-show="VM.isCheque()"> *</span></label>
279.                                    <div class="col-lg-8">
280.                                        <p class="input-group">
281.                                            <input id="editorDatePayment" kendo-date-picker
282.                                                   ng-model="VM.entity.date_payment"
283.                                                   data-k-format="'dd/MM/yyyy'"
284.                                                   k-ng-model="VM.entity.date_payment" />
285.                                        </p>
286.                                    </div>
287.                                </div>
288.                            </div>
289.                        </div>
290.                        <div class="row">
291.                            <div class="well col-lg-12" style="margin-top: 20px; margin-bottom: 20px; padding: 0; padding-top: 15px;">
292.                                <div class="row">
293.                                    <div class="col-lg-12">
294.                                        <div class="form-group col-lg-6">
295.                                            <label for="editorSig1User" class="col-lg-4">{{ translation.EDITOR_SIG1_SIGNED_BY }}</label>
296.                                            <div class="col-lg-8">
297.                                                <input type="text"
298.                                                       class="form-control"
299.                                                       id="editorSig1User"
300.                                                       name="editor[sig1_user]"
301.                                                       title="{{VM.entity.sig1_user}}"
302.                                                       ng-model="VM.entity.sig1_user"
303.                                                       readonly>
304.                                            </div>
305.                                        </div>
306.                                        <div class="form-group col-lg-6">
307.                                            <label for="editorSig1SignedAt" class="col-lg-4">{{ translation.EDITOR_SIG1_SIGNED_AT }}</label>
308.                                            <div class="col-lg-8">
309.                                                <input type="text"
310.                                                       class="form-control"
311.                                                       id="editorSig1SignedAt"
312.                                                       name="editor[sig1_signAt]"
313.                                                       title="{{VM.entity.sig1_signAt}}"
314.                                                       ng-model="VM.entity.sig1_signAt"
315.                                                       readonly
316.                                                       dateformat="dd/MM/yyyy">
317.                                            </div>
318.                                        </div>
319.                                    </div>
320.                                </div>
321.                                <div class="row">
322.                                    <div class="col-lg-12">
323.                                        <div class="form-group col-lg-6">
324.                                            <label for="editorSig2User" class="col-lg-4">{{ translation.EDITOR_SIG2_SIGNED_BY }}</label>
325.                                            <div class="col-lg-8">
326.                                                <input type="text"
327.                                                       class="form-control"
328.                                                       id="editorSig2User"
329.                                                       name="editor[sig2_user]"
330.                                                       title="{{VM.entity.sig2_user}}"
331.                                                       ng-model="VM.entity.sig2_user"
332.                                                       readonly>
333.                                            </div>
334.                                        </div>
335.                                        <div class="form-group col-lg-6">
336.                                            <label for="editorSig2SignAt" class="col-lg-4">{{ translation.EDITOR_SIG2_SIGNED_AT }}</label>
337.                                            <div class="col-lg-8">
338.                                                <input type="text"
339.                                                       class="form-control"
340.                                                       id="editorSig2SignAt"
341.                                                       name="editor[sig2_signAt]"
342.                                                       title="{{VM.entity.sig2_signAt}}"
343.                                                       ng-model="VM.entity.sig2_signAt"
344.                                                       readonly
345.                                                       dateformat="dd/MM/yyyy">
346.                                            </div>
347.                                        </div>
348.                                    </div>
349.                                </div>
350.                                <div class="row">
351.                                    <div class="col-lg-12">
352.                                        <div class="form-group col-lg-6">
353.                                            <label for="editorChequeStatus" class="col-lg-4">{{ translation.EDITOR_CHEQUE_STATUS }}</label>
354.                                            <div class="col-lg-8">
355.                                                <input type="text"
356.                                                       class="form-control text-center"
357.                                                       id="editorChequeStatus"
358.                                                       name="editor[cheque_status]"
359.                                                       title="{{VM.entity.cheque_status}}"
360.                                                       ng-model="VM.entity.cheque_status"
361.                                                       readonly
362.                                                       translate="cheque_status">
363.                                            </div>
364.                                        </div>
365.                                        <div class="form-group col-lg-6">
366.                                            <div id="editor-command-sign" class="btn-group pull-left" role="group" aria-label="Sign" style="padding-left: 15px;">
367.                                                <button type="button" class="btn btn-lg btn-primary" ng-disabled="!VM.isAllowSign || VM.isSigned()" ng-click="VM.sign()">{{ translation.CMD_SIGN }}</button>
368.                                                <button type="button" class="btn btn-lg btn-primary" ng-disabled="!VM.isAllowUnSign || !VM.isSigned()" ng-click="VM.unsign()">{{ translation.CMD_UNSIGN }}</button>
369.                                            </div>
370.                                        </div>
371.                                    </div>
372.                                </div>
373.                            </div>
374.                        </div>
375. 
376.                        <a class="btn btn-link" data-toggle="collapse" href="#cheque_more_info" aria-expanded="false" aria-controls="cheque_more_info">
377.                            {{ translation.TEXT_CHEQUE_MORE_INFO }}
378.                        </a>
379. 
380.                        <div class="collapse" id="cheque_more_info">
381.                            <div class="row">
382.                                <div class="col-lg-12">
383.                                    <div class="form-group text-center">
384.                                        <label>{{ translation.EDITOR_DATE_CREATED }}: </label>
385.                                        <span>{{ (VM.entity.date_created) ? (VM.convertStringToDate(VM.entity.date_created) | date: 'dd/MM/yyyy') : translation.TEXT_UNKNOWN }}</span>
386.                                          
387.                                        <label>{{ translation.EDITOR_DATE_UPLOADED }}: </label>
388.                                        <span>{{ (VM.entity.date_uploaded) ? (VM.convertStringToDate(VM.entity.date_uploaded) | date: 'dd/MM/yyyy') : translation.TEXT_UNKNOWN }}</span>
389.                                          
390.                                        <label>{{ translation.EDITOR_DATE_UPDATED }}: </label>
391.                                        <span>{{ (VM.entity.date_updated) ? (VM.convertStringToDate(VM.entity.date_updated) | date: 'dd/MM/yyyy') : translation.TEXT_UNKNOWN }} </span>
392.                                    </div>
393.                                </div>
394.                            </div>
395. 
396.                            <div class="row">
397.                                <div class="col-lg-12">
398.                                    <div class="form-group col-lg-6">
399.                                        <label for="editorTaxDeductionPercentage" class="col-lg-4">{{ translation.EDITOR_TAX_DEDUCTION_PERCENTAGE }}: </label>
400.                                        <div class="col-lg-8">
401.                                            <input type="number"
402.                                                   class="form-control"
403.                                                   id="editorTaxDeductionPercentage"
404.                                                   name="editor[tax_deduction_percentage]"
405.                                                   title="{{VM.entity.tax_deduction_percentage}}"
406.                                                   ng-model="VM.entity.tax_deduction_percentage">
407.                                        </div>
408.                                    </div>
409.                                    <div class="form-group col-lg-6">
410.                                        <label for="editorTaxDeductionSum" class="col-lg-4">{{ translation.EDITOR_TAX_DEDUCTION_SUM }}: </label>
411.                                        <div class="col-lg-8">
412.                                            <input type="number"
413.                                                   class="form-control"
414.                                                   id="editorTaxDeductionSum"
415.                                                   name="editor[tax_deduction_sum]"
416.                                                   title="{{VM.entity.tax_deduction_sum}}"
417.                                                   ng-model="VM.entity.tax_deduction_sum">
418.                                        </div>
419.                                    </div>
420.                                </div>
421.                            </div>
422. 
423.                            <div class="row" ng-show="VM.isCheque()">
424.                                <div class="col-lg-12">
425.                                    <div class="form-group col-lg-6">
426.                                        <label for="editorChequeCanceled" class="col-lg-4">{{ translation.EDITOR_CHEQUE_CANCELED }}: </label>
427.                                        <div class="col-lg-8 text-left">
428.                                            <input type="text"
429.                                                   class="form-control"
430.                                                   id="editorChequeCanceled"
431.                                                   name="editor[cheque_canceled]"
432.                                                   title="{{VM.entity.cheque_canceled}}"
433.                                                   ng-model="VM.entity.is_canceled"
434.                                                   readonly>
435.                                        </div>
436.                                    </div>
437.                                    <div class="form-group col-lg-6">
438.                                        <div class="form-group">
439.                                            <label for="editorDateChequeCanceled" class="col-lg-4">{{ translation.EDITOR_DATE_CHEQUE_CANCELED }}: </label>
440.                                            <div class="col-lg-8 text-left">
441.                                                <input type="datetime"
442.                                                       class="form-control"
443.                                                       id="editorDateChequeCanceled"
444.                                                       name="editor[date_cheque_canceled]"
445.                                                       title="{{VM.entity.date_cheque_canceled}}"
446.                                                       ng-model="VM.entity.date_canceled"
447.                                                       readonly>
448.                                            </div>
449.                                        </div>
450.                                    </div>
451.                                </div>
452.                            </div>
453. 
454.                            <div class="row" ng-show="!VM.state">
455.                                <div class="col-lg-12">
456.                                    <div class="form-group col-lg-6">
457.                                        <label for="editorChequeID" class="col-lg-4">{{ translation.EDITOR_CHEQUE_ID }}: </label>
458.                                        <div class="col-lg-8 text-left">
459.                                            <input type="text"
460.                                                   class="form-control"
461.                                                   id="editorChequeID"
462.                                                   name="editor[cheque_id]"
463.                                                   title="{{VM.entity['date_created:id']}}"
464.                                                   ng-model="VM.entity['date_created:id']"
465.                                                   readonly>
466.                                        </div>
467.                                    </div>
468.                                </div>
469.                            </div>
470. 
471.                        </div>
472.                    </form>
473.                </div>
474.                <div class="modal-footer">
475.                    <div id="toolbar">
476.                        <div id="command-editor-cancel" class="btn-group pull-right" role="group" aria-label="Cancel">
477.                            <button class="btn btn-lg btn-default pull-right" ng-click="VM.cancel()">{{ translation.CMD_CANCEL }}</button>
478.                        </div>
479.                        <button type="submit" class="btn btn-lg btn-primary pull-left" ng-click="VM.ok()" ng-disabled="!VM.isAllowEdit || !VM.isNewFilled()" ng-show="!VM.state">{{ translation.CMD_EDIT }}</button>
480.                        <button type="submit" class="btn btn-lg btn-primary pull-left" ng-click="VM.ok()" ng-disabled="!VM.isAllowAdd || !VM.isNewFilled()" ng-show="VM.state">{{ translation.CMD_ADD_NEW }}</button>
481.                    </div>
482.                </div>
483.            </div>
484.        </script>
485.    </div>
486.</div>
 

The controller:

001.define(['app'], function(app) {
002.    app.controller('MainCtrl', function ($scope, $modal, $timeout, MainService, LoginService) {
003. 
004.        /* ========================================== */
005.        /*           GENERAL INITIALIZATIONS          */
006.        /* ========================================== */
007. 
008.        // Set the culture
009.        kendo.culture("he-IL");
010. 
011.        // Define scope variables
012.        $scope.VM = {};
013.        $scope.VM.isInitialized = false;
014.        $scope.VM.filter = {};
015.        $scope.VM.editor = {};
016.        $scope.VM.collections = {};
017.        $scope.VM.kendoGrid = null;
018. 
019.        // Define the view permissions
020.        $scope.VM.permissions = {
021.            allow_edit: true,
022.            allow_sign: true,
023.            allow_cancel: true
024.        };
025. 
026.        /* ========================================== */
027.        /*         INITIALIZING DATA MEMBERS          */
028.        /* ========================================== */
029. 
030.        $scope.init = function() {
031.            ///////////////////////////////////// Permissions
032.            // Check if the user is allowed to sign
033.            $scope.VM.isAllowSign = $scope.VM.permissions.allow_sign;
034.            // Check if the user is allowed to unsign
035.            $scope.VM.isAllowUnSign = $scope.VM.permissions.allow_sign;
036.            // Check if the user is allowed to edit existing items
037.            $scope.VM.isAllowEdit = $scope.VM.permissions.allow_edit;
038.            // Check if the user is allowed to add new items
039.            $scope.VM.isAllowAdd = $scope.VM.permissions.allow_edit;
040.            // Check if the user is allowed to cancel cheques
041.            $scope.VM.isAllowCancel = $scope.VM.permissions.allow_cancel;
042.        };
043. 
044.        /* ========================================== */
045.        /*        INITIALIZING THE COLLECTIONS        */
046.        /* ========================================== */
047. 
048.        /* The list of all suppliers */
049.        $scope.VM.getSuppliers = function() {
050.            MainService.getSuppliers().then(function(data) {
051.                var suppliers = [];
052.                data.forEach(function (supplier) {
053.                    suppliers.push({ value: supplier.name, text: supplier.name});
054.                });
055.                $scope.VM.collections.suppliers = suppliers;
056.            });
057.        } ();
058.        $scope.VM.collections.suppliers = [];
059.        /* The list of all expensesCause */
060.        $scope.VM.getExpensesCause = function() {
061.            MainService.getExpensesCause().then(function(data) {
062.                var expenses_cause = [];
063.                data.forEach(function (cause) {
064.                    expenses_cause.push({ value: cause.expense_name, text: cause.expense_name});
065.                });
066.                $scope.VM.collections.expensesCause = expenses_cause;
067.            });
068.        } ();
069.        $scope.VM.collections.expensesCause = [];
070.        /* The list of all subExpensesCause */
071.        $scope.VM.getSubExpensesCause = function() {
072.            MainService.getSubExpensesCause().then(function(data) {
073.                var sub_expenses_cause = [];
074.                data.forEach(function (cause) {
075.                    sub_expenses_cause.push({ value: cause.sub_expense_name, text: cause.sub_expense_name});
076.                });
077.                $scope.VM.collections.subExpensesCause = sub_expenses_cause;
078.            });
079.        } ();
080.        $scope.VM.collections.subExpensesCause = [];
081. 
082.        /* ========================================== */
083.        /*      INITIALIZING THE DEFAULT FILTER       */
084.        /* ========================================== */
085. 
086.        $scope.VM.filter.status = "active";
087.        $scope.VM.filter.from_date = moment().subtract(2, "month").toDate();
088.        $scope.VM.filter.to_date = moment().toDate();
089. 
090.        /* ========================================== */
091.        /*            INITIALIZING THE GRID           */
092.        /* ========================================== */
093. 
094.        $scope.VM.dataSource = new kendo.data.DataSource({
095.            schema: {
096.                model: {
097.                    // TODO: change id to date_created:id
098.                    id: "date_created_id",
099.                    fields: {
100.                        date_created: {
101.                            editable: false,
102.                            type: "date"
103.                        },
104.                        project_name: {
105.                            editable: false
106.                        },
107.                        supplier_name: {
108.                            editable: true,
109.                            validation: {
110.                                required: true
111.                            }
112.                        },
113.                        info: {
114.                            editable: true
115.                        },
116.                        expense_cause: {
117.                            editable: true,
118.                            nullable: true
119.                        },
120.                        sub_expense_cause: {
121.                            editable: true,
122.                            nullable: true
123.                        },
124.                        cheque_number: {
125.                            editable: false
126.                        },
127.                        amount: {
128.                            editable: false
129.                        },
130.                        sig1: {
131.                            editable: false
132.                        },
133.                        sig2: {
134.                            editable: false
135.                        },
136.                        cheque_status: {
137.                            editable: false
138.                        }
139.                    }
140.                },
141.                data: "data"
142.            },
143.            serverPaging: true,
144.            serverSorting: true,
145.            pageSize: 100,
146.            serverPaging: false,
147.            serverSorting: false,
148.            serverFiltering: false,
149.            autoSync: true,
150.            transport: {
151.                read: function(e) {
152.                    // Save the 'e' for future reference
153.                    $scope.VM.readOptions = e;
154. 
155.                    // Start the spinner
156.                    kendo.ui.progress($("div[kendo-grid]"), true);
157. 
158.                    var from_date = $scope.VM.filter.from_date;
159.                    var to_date = $scope.VM.filter.to_date;
160.                    MainService.getCheques(from_date, to_date).then(function(data) {
161.                        e.success(data);
162. 
163.                        // Stop the spinner
164.                        kendo.ui.progress($("div[kendo-grid]"), false);
165.                    });
166.                },
167.                update: function(e) {
168.                    // Save the 'e' for future reference
169.                    $scope.VM.updateOptions = e;
170. 
171.                    MainService.updateCheque(e.data).then(function(data) {
172.                        e.success(data);
173.                    });
174.                },
175.                create: function(e) {
176.                    // Save the 'e' for future reference
177.                    $scope.VM.insertOptions = e;
178. 
179.                    MainService.addCheque(e.data).then(function(data) {
180.                        e.success(data);
181.                    });
182.                },
183.                destroy: function(e) {
184. 
185.                }
186.            },
187.            //pageSize: 20,
188.            aggregate: [
189.                { field: "amount", aggregate: "sum" }
190.            ],
191.        });
192.        $scope.VM.projectDS = new kendo.data.DataSource({
193.            data: []
194.        });
195.        $scope.VM.supplierDS = new kendo.data.DataSource({
196.            data: []
197.        });
198.        $scope.VM.infoDS = new kendo.data.DataSource({
199.            data: []
200.        });
201.        $scope.VM.expenseCauseDS = new kendo.data.DataSource({
202.            data: []
203.        });
204.        $scope.VM.subExpenseCauseDS = new kendo.data.DataSource({
205.            data: []
206.        });
207.        $scope.VM.chequeNumberDS = new kendo.data.DataSource({
208.            data: []
209.        });
210.        $scope.VM.amountDS = new kendo.data.DataSource({
211.            data: []
212.        });
213. 
214.        /* The grid definition */
215.        $scope.VM.gridOptions = {
216.            height: 630,
217.            filterable: {
218.                mode: "row",
219.                operators: {
220.                    string: {
221.                        eq: "שווה ",
222.                        neq: "לא שווה",
223.                        startswith: "מתחיל ב",
224.                        contains: "מכיל",
225.                        doesnotcontain: "לא מכיר",
226.                        endswith: "מסתיים ב"
227.                    }
228.                }
229.            },
230.            scrollable: true,
231.            sortable: true,
232.            selectable: "multiple, row",
233.            editable: true,
234.            resizable: false,
235.            reorderable: false,
236.            groupable: false,
237.            pageable: true,
238.            //pageable: {
239.            //    refresh: true,
240.            //    pageSizes: true,
241.            //    buttonCount: 5
242.            //},
243.            toolbar: [
244.                { name: "excel", text: " ייצוא לאקסל" },
245.                //{ name: "save", text: "שמור"}
246.            ],
247.            excel: { fileName: "results.xlsx" },
248.            dataSource: $scope.VM.dataSource,
249.            columns: [
250.                //{ field: "cheque_id", title: "מזהה צ'ק" },
251.                //{
252.                //    headerTemplate: "<input type='checkbox' class='checkbox checkAll' ng-click='VM.selectAll($event)' />",
253.                //    template: "<input type='checkbox' class='checkbox checkRow' ng-click='VM.selectRow($event)' />",
254.                //    width: "26px"
255.                //},
256.                {
257.                    field: "date_created",
258.                    title: "תאריך",
259.                    template: "<div ng-bind='dataItem.date_created | date: \"dd/MM/yyyy\"' title='{{ dataItem.date_created | date: \"dd/MM/yyyy\" }}'></div>",
260.                    filterable: false
261.                },
262.                {
263.                    field: "project_name",
264.                    title: "פרויקט",
265.                    template: "<div title='{{ dataItem.project_name }}'>#: data.project_name #</div>",
266.                    filterable: {
267.                        cell: {
268.                            dataSource: $scope.VM.dataSource,
269.                            operator: "contains"
270.                        }
271.                    }
272.                },
273.                {
274.                    field: "supplier_name",
275.                    title: "מוטב",
276.                    template: "<div title='{{ dataItem.supplier_name }}'>#: data.supplier_name #</div>",
277.                    filterable: {
278.                        cell: {
279.                            dataSource: $scope.VM.dataSource,
280.                            operator: "contains"
281.                        }
282.                    },
283.                    editor: function(container, options) {
284.                        //container.append(
285.                        //    "<select kendo-combo-box "+
286.                        //        "k-data-text-field=\"'text'\" "+
287.                        //        "k-data-value-field=\"'value'\" "+
288.                        //        "k-filter='contains' "+
289.                        //        "k-auto-bind='false' "+
290.                        //        "k-data-source='VM.collections.suppliers' "+
291.                        //        "data-bind='value:"+options.field+"'>"+
292.                        //    "</select>");
293.                        container.append(
294.                            "<input kendo-auto-complete " +
295.                                "k-data-text-field=\"'text'\" "+
296.                                "k-data-value-field=\"'value'\" "+
297.                                "k-auto-bind='false' "+
298.                                "k-value-primitive='true' "+
299.                                "k-data-source='VM.collections.suppliers' "+
300.                                "data-bind='value:"+options.field+"'/>");
301.                    }
302.                },
303.                {
304.                    field: "info",
305.                    title: "פרטים" ,
306.                    template: "<div title='{{ dataItem.info }}'>#: data.info #</div>",
307.                    filterable: {
308.                        cell: {
309.                            dataSource: $scope.VM.dataSource,
310.                            operator: "contains"
311.                        }
312.                    },
313.                    width: "250px"
314.                },
315.                {
316.                    field: "expense_cause",
317.                    title: "סעיף הוצאה",
318.                    template: "<div title='{{ dataItem.expense_cause }}'>#: data.expense_cause #</div>",
319.                    filterable: {
320.                        cell: {
321.                            dataSource: $scope.VM.dataSource,
322.                            operator: "contains"
323.                        }
324.                    },
325.                    editor: function(container, options) {
326.                        //container.append(
327.                        //    "<select kendo-combo-box "+
328.                        //        "k-data-text-field=\"'text'\" "+
329.                        //        "k-data-value-field=\"'value'\" "+
330.                        //        "k-filter='contains' "+
331.                        //        "k-auto-bind='false' "+
332.                        //        "k-data-source='VM.collections.expensesCause' "+
333.                        //        "data-bind='value:"+options.field+"'>"+
334.                        //    "</select>");
335.                        container.append(
336.                            "<input kendo-auto-complete " +
337.                                "k-data-text-field=\"'text'\" "+
338.                                "k-data-value-field=\"'value'\" "+
339.                                "k-auto-bind='false' "+
340.                                "k-value-primitive='true' "+
341.                                "k-data-source='VM.collections.expensesCause' "+
342.                                "k-rebind='VM.collections.expensesCause' "+
343.                                "data-bind='value:"+options.field+"'/>");
344.                    }
345.                },
346.                {
347.                    field: "sub_expense_cause",
348.                    title: "תת סעיף הוצאה",
349.                    template: "<div title='{{ dataItem.sub_expense_cause }}'>#: data.sub_expense_cause #</div>",
350.                    filterable: {
351.                        cell: {
352.                            dataSource: $scope.VM.dataSource,
353.                            operator: "contains"
354.                        }
355.                    },
356.                    editor: function(container, options) {
357.                        //container.append(
358.                        //    "<select kendo-combo-box "+
359.                        //        "k-data-text-field=\"'text'\" "+
360.                        //        "k-data-value-field=\"'value'\" "+
361.                        //        "k-filter='contains' "+
362.                        //        "k-auto-bind='false' "+
363.                        //        "k-data-source='VM.collections.subExpensesCause' "+
364.                        //        "data-bind='value:"+options.field+"'>"+
365.                        //    "</select>");
366.                        container.append(
367.                            "<input kendo-auto-complete " +
368.                                "k-data-text-field=\"'text'\" "+
369.                                "k-data-value-field=\"'value'\" "+
370.                                "k-auto-bind='false' "+
371.                                "k-value-primitive='true' "+
372.                                "k-data-source='VM.collections.subExpensesCause' "+
373.                                "k-rebind='VM.collections.subExpensesCause' "+
374.                                "data-bind='value:"+options.field+"'/>");
375.                    }
376.                },
377.                {
378.                    field: "cheque_number",
379.                    title: "מספר צ'ק",
380.                    template: "<div title='{{ dataItem.cheque_number }}'>#: data.cheque_number #</div>",
381.                    filterable: {
382.                        cell: {
383.                            dataSource: $scope.VM.dataSource,
384.                            operator: "startswith"
385.                        }
386.                    }
387.                },
388.                {
389.                    field: "amount",
390.                    title: "סכום" ,
391.                    template: "<div ng-bind='dataItem.amount | currency: \"₪\"' title='{{ dataItem.amount | currency: \"₪\" }}'></div>",
392.                    format: "{0:c0}",
393.                    footerTemplate: "<div>{{ translation.TEXT_AMOUNT_SUM }}: #= kendo.toString(sum, 'c0') #</div>",
394.                    width: "180px"
395.                },
396.                {
397.                    field: "sig1",
398.                    title: "חתימה 1",
399.                    template: function(dataItem) {
400.                        var template;
401.                        var chequeId = dataItem.uid;
402. 
403.                        // TODO: need to convert date from string to actual date
4