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

Serious performance issues

10 Answers 156 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
404. 
405.                        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') : '' }}\">" +
406.                                        "{{ dataItem.sig2_user || ' ' }}" +
407.                                        "<div class='quicksign bubble bubble-sig1-"+ chequeId +" bottom'>" +
408.                                            "<div class='quicksign-user'>{{ loggedInUser.full_name }}</div>" +
409.                                            "<div class='quicksign-amount'>{{ dataItem.amount | currency: '₪' }}</div>" +
410.                                            "<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>" +
411.                                            "<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>" +
412.                                        "</div>" +
413.                                    "</div>";
414. 
415.                        return template;
416.                    },
417.                    filterable: false
418.                },
419.                {
420.                    field: "sig2",
421.                    title: "חתימה 2",
422.                    template: function(dataItem) {
423.                        var template;
424.                        var chequeId = dataItem.uid;
425. 
426.                        // TODO: need to convert date from string to actual date
427. 
428.                        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') : '' }}\">" +
429.                                        "{{ dataItem.sig2_user || ' ' }}" +
430.                                        "<div class='quicksign bubble bubble-sig2-"+ chequeId +" bottom'>" +
431.                                            "<div class='quicksign-user'>{{ loggedInUser.full_name }}</div>" +
432.                                            "<div class='quicksign-amount'>{{ dataItem.amount | currency: '₪' }}</div>" +
433.                                            "<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>" +
434.                                            "<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>" +
435.                                        "</div>" +
436.                                    "</div>";
437. 
438.                        return template;
439.                    },
440.                    filterable: false
441.                },
442.                {
443.                    field: "cheque_status",
444.                    title: "סטאטוס",
445.                    template: function(dataItem) {
446.                        var result = "";
447.                        var status = $scope.translationAPI.translateStatus(dataItem.cheque_status);
448.                        var css_class = dataItem.cheque_status;
449. 
450.                        result += "<div title='"+status+"' class='"+css_class+"'>";
451.                        result += status;
452.                        result += "</div>";
453. 
454.                        return result;
455.                    },
456.                    filterable: false
457.                }
458.            ],
459. 
460.            // Events
461.            dataBound: function(e) {
462.                $scope.VM.isInitialized = true;
463.                $scope.VM.kendoGrid =  $("div[kendo-grid]").getKendoGrid();
464.                $scope.rowsCount = $scope.VM.kendoGrid.dataItems().length;
465. 
466.                // Get the status count
467.                $scope.VM.activeStatusCount = $scope.VM.getStatusCount('active');
468.                $scope.VM.unsigned= $scope.VM.getStatusCount('unsigned');
469.                $scope.VM.pending= $scope.VM.getStatusCount('pending');
470.                $scope.VM.signedStatusCount = $scope.VM.getStatusCount('signed');
471.                $scope.VM.allStatusCount = $scope.VM.getStatusCount('all');
472.            },
473. 
474.            change: function(e) {
475.                var grid = $scope.VM.kendoGrid;
476.                var results = grid.select();
477.                $scope.selectedRows = results;
478.                $scope.selectedRowsCount = results.length;
479.                $scope.isEntitySelected = results.length > 0;
480.                $scope.isOneEntitySelected = results.length == 1;
481.            }
482.        };
483. 
484.        /* ========================================== */
485.        /*                  Methods                   */
486.        /* ========================================== */
487. 
488.        ///////////////////////////////////// Helper methods
489.        // Check if the cheque is already signed
490.        $scope.VM.isSigned = function(dataItem) {
491.            if ($scope.loggedInUser.signer == 1)
492.                return dataItem.sig1_user && dataItem.sig1_signAt;
493.            else if ($scope.loggedInUser.signer == 2)
494.                return dataItem.sig2_user && dataItem.sig2_signAt;
495.            else
496.                return false;
497.        };
498.        // Get the number of results by statys
499.        $scope.VM.getStatusCount = function(status) {
500.            var grid = $scope.VM.kendoGrid;
501.            if (!grid)
502.                return;
503. 
504.            var results = grid.dataSource.data();
505.            if (!results)
506.                return;
507. 
508.            var count = 0;
509.            if (status === "all") {
510.              count = results.length;
511.            } else if (status === "active") {
512.                angular.forEach(results, function(value,key) {
513.                    if (value.is_canceled == "N")
514.                        count++;
515.                });
516.            } else {
517.                angular.forEach(results, function(value,key) {
518.                    if (value.cheque_status == status)
519.                        count++;
520.                });
521.            }
522. 
523.            return count;
524.        };
525. 
526.        ///////////////////////////////////// Commands
527.        // Opens the entity detail view
528.        $scope.VM.openEntityModal = function(state) {
529.            var instance = $modal.open({
530.                templateUrl: 'addNewModal.html',
531.                controller: 'AddNewCtrl',
532.                size: 'lg',
533.                resolve: {
534.                    entity: function(){
535.                        var grid = $scope.VM.kendoGrid;
536.                        var results = grid.select();
537.                        return grid.dataItem(results[0]);
538.                    },
539.                    state: function() {
540.                        return state;
541.                    },
542.                    translation: function() {
543.                        return $scope.translation;
544.                    },
545.                    translationAPI: function() {
546.                        return $scope.translationAPI;
547.                    },
548.                    permissions: function() {
549.                        return $scope.VM.permissions;
550.                    },
551.                    loggedInUser: function() {
552.                        return $scope.loggedInUser;
553.                    }
554.                }
555.            });
556. 
557.            instance.result.then(function(entity) {
558.                if (entity) {
559.                    if (state) {
560.                        $scope.VM.addNew(entity);
561.                    }
562.                    else {
563.                        $scope.VM.update(entity);
564.                    }
565. 
566.                    // Save the changes
567.                    $scope.VM.dataSource.sync();
568.                }
569.            });
570.        };
571.        // Add a new entity
572.        $scope.VM.addNew = function(entity){
573.            $scope.VM.dataSource.add(entity)
574.        };
575.        // Update the entity
576.        $scope.VM.update = function(entity){
577. 
578.        };
579.        // Sign a cheque
580.        $scope.VM.sign = function(dataItem) {
581.            if ($scope.loggedInUser.signer == 1) {
582.                dataItem.sig1_user = $scope.loggedInUser.full_name;
583.                dataItem.sig1_signAt = moment().toDate();
584.            }
585.            else {
586.                dataItem.sig2_user = $scope.loggedInUser.full_name;
587.                dataItem.sig2_signAt = moment().toDate();
588.            }
589. 
590.            // Save the changes
591.            $scope.VM.dataSource.sync();
592.        };
593.        // Sign all selected cheques
594.        $scope.VM.signAll = function() {
595.            var isInitialized = $scope.VM.isInitialized;
596.            if (!isInitialized)
597.                return;
598. 
599.            var grid = $scope.VM.kendoGrid;
600.            if (!grid)
601.                return;
602. 
603.            var results = grid.select();
604.            if (!results)
605.                return;
606. 
607.            for (i=0; i<results.length; i++) {
608.                $scope.VM.sign(grid.dataItem(results[i]));
609.            }
610.        };
611.        // Sign a cheque
612.        $scope.VM.unsign = function(dataItem) {
613.            if ($scope.loggedInUser.signer == 1) {
614.                dataItem.sig1_user = "";
615.                dataItem.sig1_signAt = "";
616.            }
617.            else {
618.                dataItem.sig2_user = "";
619.                dataItem.sig2_signAt = "";
620.            }
621. 
622.            // Save the changes
623.            $scope.VM.dataSource.sync();
624.        };
625.        // Sign all selected cheques
626.        $scope.VM.unsignAll = function() {
627.            var isInitialized = $scope.VM.isInitialized;
628.            if (!isInitialized)
629.                return;
630. 
631.            var grid = $scope.VM.kendoGrid;
632.            if (!grid)
633.                return;
634. 
635.            var results = grid.select();
636.            if (!results)
637.                return;
638. 
639.            for (i=0; i<results.length; i++) {
640.                $scope.VM.unsign(grid.dataItem(results[i]));
641.            }
642.        };
643.        // Cancel the cheque
644.        $scope.VM.cancelAction = function(entity) {
645.            var entity = $scope.VM.selectedRows[0];
646.