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

Serious performance issues

10 Answers 345 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. 
647.            entity.is_canceled = "Y";
648. 
649.            /* Send the new entity to the server for cancel */
650.            MainService.modifyCancellation(entity).then(function(data) {
651. 
652.            });
653.        };
654.        // Undo canceled cheque
655.        $scope.VM.undoCancelAction = function(entity) {
656.            var entity = $scope.VM.selectedRows[0];
657. 
658.            entity.is_canceled = "N";
659. 
660.            /* Send the new entity to the server for cancel */
661.            MainService.modifyCancellation(entity).then(function(data) {
662. 
663.            });
664.        };
665.        // Get all cheques from within the dates range
666.        $scope.VM.getCheques = function(e) {
667.            $scope.VM.dataSource.read($scope.VM.readOptions);
668.        };
669.        // Refresh the data, if any changes has been made
670.        var _chequeFlowInProgress = false;
671.        $scope.VM.getChequesFlow = function() {
672.            (function tick() {
673.                var project = $scope.VM.filter.project || "";
674.                var from_date = $scope.VM.filter.from_date;
675.                var to_date = $scope.VM.filter.to_date;
676. 
677.                if (_chequeFlowInProgress)
678.                    return;
679. 
680.                MainService.getChequesFlow(project, from_date, to_date).then(function(data) {
681.                    _chequeFlowInProgress = true;
682. 
683.                    var index, jndex;
684.                    for (index=0; index<data.length; index++) {
685.                        var action = data[index].type;
686.                        var flag = false;
687. 
688.                        var grid = $scope.VM.kendoGrid;
689.                        if (!grid)
690.                            return;
691. 
692.                        var results = grid.dataItems();
693.                        if (!results)
694.                            return;
695. 
696.                        for (jndex=0; jndex<results.length && !flag; jndex++){
697.                            var currRow = results[jndex];
698.                            switch (action) {
699.                                case "update": {
700.                                    if (data[index]["date_created:id"] == currRow["date_created:id"]) {
701.                                        currRow.action = data[index].action;
702.                                        currRow.project_name = data[index].project_name;
703.                                        currRow.expense_cause = data[index].expense_cause;
704.                                        currRow.sub_expense_cause = data[index].sub_expense_cause;
705.                                        currRow.info = data[index].info;
706.                                        currRow.supplier_name = data[index].supplier_name;
707.                                        currRow.supplier_address = data[index].supplier_address;
708.                                        currRow.amount = data[index].amount;
709.                                        currRow.cheque_number = data[index].cheque_number;
710.                                        currRow.date_payment = data[index].date_payment;
711.                                        currRow.sig1_user = data[index].sig1_user;
712.                                        currRow.sig1_signAt = data[index].sig1_signAt;
713.                                        currRow.sig2_user = data[index].sig2_user;
714.                                        currRow.sig2_signAt = data[index].sig2_signAt;
715.                                        currRow.cheque_status = data[index].cheque_status;
716.                                        currRow.date_created = data[index].date_created;
717.                                        currRow.date_uploaded = data[index].date_uploaded;
718.                                        currRow.date_updated = data[index].date_updated;
719.                                        currRow.tax_deduction_percentage = data[index].tax_deduction_percentage;
720.                                        currRow.tax_deduction_sum = data[index].tax_deduction_sum;
721.                                        currRow.is_canceled = data[index].is_canceled;
722.                                        currRow.date_canceled = data[index].date_canceled;
723. 
724.                                        // Mark iteration as completed
725.                                        flag = true;
726.                                    }
727.                                    break;
728.                                }
729.                                case "insert": {
730.                                    if (data[index]["date_created:id"] == currRow["date_created:id"]) {
731.                                        // Mark iteration as completed
732.                                        flag = true;
733.                                    }
734.                                    break;
735.                                }
736.                                default: {
737.                                    if (action)
738.                                        console.error("Can't load entity from flow, unknown state '"+action+"'");
739.                                    else
740.                                        console.error("Can't load entity from flow, the 'type' is missing");
741.                                }
742.                            }
743.                        }
744. 
745.                        if (!flag) {
746.                            if (action == "insert") {
747.                                // TODO: insert the item to the grid
748.                                grid.dataSource.add(data[index]);
749.                            }
750.                        }
751.                    };
752. 
753.                    _chequeFlowInProgress = false;
754.                });
755. 
756.                var timer = $timeout(tick, 1000);
757.                $scope.$on("$destroy", function(event) {
758.                    $timeout.cancel(timer);
759.                });
760.            })();
761.        }();
762. 
763.        ///////////////////////////////////// Presentation
764.        // Responsible for filtering the grid by the status field
765.        $scope.VM.changeStatus = function(e, status) {
766.            e.preventDefault();
767. 
768.            $scope.VM.filter.status = status;
769.            switch (status) {
770.                case "all": {
771.                    $scope.VM.dataSource.filter({} );
772.                    break;
773.                }
774.                case "active": {
775.                    $scope.VM.dataSource.filter({ field: "is_canceled", operator: "eq", value: "N"} );
776.                    break;
777.                }
778.                case "unsigned":
779.                case "pending":
780.                case "signed": {
781.                    $scope.VM.dataSource.filter({ field: "cheque_status", operator: "eq", value: status} );
782.                    break;
783.                }
784.            }
785.        };
786.        // Save the position of the mouse
787.        $(document).mousemove(function(e) {
788.            window.x = e.pageX;
789.            window.y = e.pageY;
790.        });
791.        // Show the quicksign panel
792.        $scope.VM.showQuickSign = function(sig, chequeId) {
793.            var handle = $(".bubble-sig"+sig+"-"+chequeId);
794.            var isVisible = $(".bubble-sig"+sig+"-"+chequeId+":visible");
795. 
796.            if (isVisible.length > 0)
797.                return;
798. 
799.            var grid = $("div[kendo-grid]");
800.            var gridHalfPosition = grid.offset().top + (grid.height() / 2);
801. 
802.            if (window.y < gridHalfPosition) {
803.                handle.removeClass("bottom");
804.                handle.addClass("top");
805.            }
806.            else {
807.                handle.removeClass("top");
808.                handle.addClass("bottom");
809.            }
810. 
811.            handle.show();
812.        };
813.        // Hide the quicksign panel
814.        $scope.VM.hideQuickSign = function(sig, chequeId) {
815.            var handle = $(".bubble-sig"+sig+"-"+chequeId);
816.            handle.hide();
817.        };
818.    });
819. 
820.});

0
T. Tsonev
Telerik team
answered on 22 Apr 2015, 12:32 PM
Hello,

The difference between #: # and #= # is that the former will encode HTML entities. Performance shouldn't be an issue with either.

The reason that you don't see correct page count is likely to be a missing total count.
At least I couldn't find it in the code.

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 06 May 2015, 04:49 AM

Hey, 

So, I implemented a paging with 500 results per page, and it helped but it's still far from perfect.

I'm attaching both the HTML+JS+Data Mock this time, please advise.

Data Mock is attached.

HTML:

<div ng-controller="DashboardCtrl" id="dashboard" class="k-rtl">
    <div ng-controller="MainCtrl" ng-init="init()">
        <nav id="navigation" class="navbar navbar-default" role="navigation">
            <div class="container-fluid">
 
                <div class="collapse navbar-collapse pull-right" id="bs-example-navbar-collapse-1">
                    <a class="navbar-brand" href="/#/main/all">
                        <img alt="SG" src="img/logo.png">
                    </a>
                    <ul class="nav navbar-nav">
                        <li><a href="/#/main/all">{{ translation.NAV_MAIN }}</a></li>
                        <li><a href="/#/import">{{ translation.NAV_IMPORT }}</a></li>
                    </ul>
                </div><!-- /.navbar-collapse -->
 
                <img alt="{{ loggedInUser.company }}" src="img/logos/{{ loggedInUser.company }}.png" ng-show="loggedInUser.company != ''" class="company-logo">
 
                <div class="navbar-header pull-left">
                    <div class="navbar-logged-in-user pull-right">
                        <img src="img/icon-user.png" width="36" height="36"/>
                    </div>
                    <div class="navbar-text pull-right">
                        {{ loggedInUser.full_name }}
                        <br/>
                        <a href="/api/logout"><strong>{{ translation.NAV_LOGOUT }}</strong></a>
                    </div>
                </div><!-- /.navbar-header -->
            </div>
        </nav>
        <div id="container">
            <aside id="filter-status">
                <ul class="nav nav-pills nav-stacked">
                    <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>
                    <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>
                    <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>
                    <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>
                    <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>
                </ul>
            </aside>
            <section id="content">
                <div id="toolbar" class="row">
                    <div class="col-lg-12">
                        <div id="command-sign" class="btn-group" role="group" aria-label="Sign">
                            <button type="button" class="btn btn-lg btn-default" ng-disabled="!VM.isEntitySelected || !VM.isAllowSign" ng-click="VM.signAll()">{{ translation.CMD_SIGN }}</button>
                            <button type="button" class="btn btn-lg btn-default" ng-disabled="!VM.isEntitySelected || !VM.isAllowUnSign" ng-click="VM.unsignAll()">{{ translation.CMD_UNSIGN }}</button>
                        </div>
                        <div id="command-manage" class="btn-group" role="group" aria-label="Manage">
                            <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>
                            <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>
                        </div>
                        <div id="command-cancel" class="btn-group" role="group" aria-label="Cancel" ng-show="VM.isOneEntitySelected && !VM.isFromSAP && !VM.isCanceled">
                            <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>
                        </div>
                        <div id="command-undo" class="btn-group" role="group" aria-label="Undo" ng-show="VM.isOneEntitySelected && !VM.isFromSAP && VM.isCanceled">
                            <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>
                        </div>
                    </div>
                </div>
 
                <div class="row">
                    <div class="col col-lg-12">
                        <div id="filter" class="well">
                            <form role="form" action="" method="post" name="filterForm" class="form-inline" novalidate>
                                <!--<div class="form-group hide">
                                    <label for="filterProject">{{ translation.FILTER_PROJECT }}</label>
                                    <input type="text"
                                           class="form-control"
                                           id="filterProject"
                                           name="filter[project]"
                                           ng-model="VM.filter.project"
                                           typeahead="project for project in VM.collections.projects | filter:$viewValue"
                                           typeahead-focus-first>
                                </div>-->
                                <div class="form-group">
                                    <label for="filterFromDate">{{ translation.FILTER_FROM_DATE }}</label>
                                    <p class="input-group">
                                        <input id="filterFromDate" kendo-date-picker
                                               ng-model="VM.filter.from_date"
                                               data-k-format="'dd/MM/yyyy'"
                                               k-ng-model="VM.filter.from_date"
                                               k-rebind="VM.filter.to_date"
                                               k-max="VM.filter.to_date"
                                               k-on-change="VM.getCheques()" />
                                    </p>
                                </div>
                                <div class="form-group">
                                    <label for="filterToDate">{{ translation.FILTER_TO_DATE }}</label>
                                    <p class="input-group">
                                        <input id="filterToDate" kendo-date-picker
                                               ng-model="VM.filter.to_date"
                                               data-k-format="'dd/MM/yyyy'"
                                               k-ng-model="VM.filter.to_date"
                                               k-rebind="VM.filter.from_date"
                                               k-min="VM.filter.from_date"
                                               k-on-change="VM.getCheques()" />
                                    </p>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
 
                <div id="grid" class="row">
                    <div class="col-lg-12">
                        <div id="chequesGrid">
                            <kendo-grid options="VM.gridOptions">
                            </kendo-grid>
                        </div>
                    </div>
                </div>
                <div id="stats" class="row">
                    <div class="col-lg-12">
                        <span>{{ translation.TEXT_ALL_ROWS }}: </span><strong>{{ (VM.rowsCount || 0) }}</strong></span>
                        <br/>
                        <span>{{ translation.TEXT_SELECTED_ROWS }}: </span><strong>{{ (VM.selectedRowsCount || 0) }}</strong>
                    </div>
                </div>
 
            </section>
        </div>
        </div>
        <script type="text/ng-template" id="addNewModal.html">
            <div id="editor">
                <div class="modal-header">
                    <h3 class="modal-title text-center" ng-show="!VM.state">{{ translation.TITLE_EDIT_CHEQUE }}</h3>
                    <h3 class="modal-title text-center" ng-show="VM.state">{{ translation.TITLE_ADD_NEW }}</h3>
                </div>
                <div class="modal-body">
                    <form role="form" action="" method="post" id="editor" class="form-horizontal">
                        <div class="row">
                            <div class="col-lg-6">
                                <div class="form-group col-lg-12">
                                    <label for="editorAction" class="col-lg-4">{{ translation.EDITOR_ACTION }}</label>
                                    <div class="col-lg-8" style="padding-left: 0;">
                                        <select class="form-control"
                                                id="editorAction"
                                                name="editor[action]"
                                                title="{{VM.entity.action}}"
                                                ng-model="VM.entity.action"
                                                ng-options="action for action in VM.collections.actions">
                                        </select>
                                    </div>
                                </div>
                            </div>
                            <div class="col-lg-6 text-left">
                                <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;">
                                    <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>
                                </div>
                                <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;">
                                    <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>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-12">
                                <div class="form-group col-lg-6">
                                    <label for="editorProjectName" class="col-lg-4">{{ translation.EDITOR_PROJECT_NAME }}<span ng-show="true"> *</span></label>
                                    <div class="col-lg-8">
                                        <input type="text"
                                               class="form-control"
                                               id="editorProjectName"
                                               name="editor[project_name]"
                                               title="{{VM.entity.project_name}}"
                                               ng-model="VM.entity.project_name"
                                               ng-required="true"
                                               typeahead="project for project in VM.collections.projects | filter:$viewValue"
                                               typeahead-focus-first>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-12">
                                <div class="form-group col-lg-6">
                                    <label for="editorExpenseCause" class="col-lg-4">{{ translation.EDITOR_EXPENSE_CAUSE }}: </label>
                                    <div class="col-lg-8">
                                        <input type="text"
                                               class="form-control"
                                               id="editorExpenseCause"
                                               name="editor[editorExpenseCause]"
                                               title="{{VM.entity.expense_cause}}"
                                               ng-model="VM.entity.expense_cause"
                                               typeahead="cause for cause in VM.collections.expensesCause | filter:$viewValue"
                                               typeahead-focus-first>
                                    </div>
                                </div>
                                <div class="form-group col-lg-6">
                                    <label for="editorSubExpenseCause" class="col-lg-4">{{ translation.EDITOR_SUB_EXPENSE_CAUSE }} </label>
                                    <div class="col-lg-8">
                                        <input type="text"
                                               class="form-control"
                                               id="editorSubExpenseCause"
                                               name="editor[editorSubExpenseCause]"
                                               title="{{VM.entity.sub_expense_cause}}"
                                               ng-model="VM.entity.sub_expense_cause"
                                               typeahead="cause for cause in VM.collections.subExpensesCause | filter:$viewValue"
                                               typeahead-focus-first>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-12">
                                <div class="form-group col-lg-12">
                                    <label for="editorInfo" class="col-lg-2">{{ translation.EDITOR_INFO }}<span ng-show="true"> *</span></label>
                                    <div class="col-lg-10">
                                        <textarea type="text"
                                               class="form-control"
                                               id="editorInfo"
                                               name="editor[info]"
                                               title="{{VM.entity.info}}"
                                               ng-model="VM.entity.info"
                                               ng-required="true"></textarea>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-12">
                                <div class="form-group col-lg-6">
                                    <label for="editorSupplierName" class="col-lg-4">{{ translation.EDITOR_CREDIT }}<span ng-show="true"> *</span></label>
                                    <div class="col-lg-8">
                                        <input type="text"
                                               class="form-control"
                                               id="editorSupplierName"
                                               name="editor[supplier_name]"
                                               title="{{VM.entity.supplier_name}}"
                                               ng-model="VM.entity.supplier_name"
                                               ng-required="true"
                                               typeahead="company for company in VM.collections.companies | filter:$viewValue"
                                               typeahead-focus-first>
                                    </div>
                                </div>
                                <div class="form-group col-lg-6">
                                    <label for="editorSupplierAddress" class="col-lg-4">{{ translation.EDITOR_SUPPLIER_ADDRESS }} </label>
                                    <div class="col-lg-8">
                                        <input type="text"
                                               class="form-control"
                                               id="editorSupplierAddress"
                                               name="editor[supplier_address]"
                                               title="{{VM.entity.supplier_address}}"
                                               ng-model="VM.entity.supplier_address">
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-lg-12">
                                <div class="form-group col-lg-6">
                                    <label for="editorChequeAmount" class="col-lg-4">{{ translation.EDITOR_CHEQUE_SUM }}<span ng-show="true"> *</span></label>
                                    <div class="col-lg-8">
                                        <input type="number"
                                               class="form-control"
                                               id="editorChequeAmount"
                                               name="editor[amount]"
                                               title="{{VM.entity.amount}}"
                                               ng-model="VM.entity.amount"
                                               ng-required="true">
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row" ng-show="VM.isCheque()">
                            <div class="col-lg-12">
                                <div class="form-group col-lg-6">
                                    <label for="editorChequeNumber" class="col-lg-4">{{ translation.EDITOR_CHEQUE_NUMBER }}<span ng-show="true"> *</span></label>
                                    <div class="col-lg-8">
                                        <input type="text"
                                               class="form-control"
                                               id="editorChequeNumber"
                                               name="editor[cheque_number]"
                                               title="{{VM.entity.cheque_number}}"
                                               ng-model="VM.entity.cheque_number"
                                               ng-required="VM.isCheque()">
                                    </div>
                                </div>
                                <div class="form-group col-lg-6">
                                    <label for="editorChequeNumber" class="col-lg-4">{{ translation.EDITOR_DATE_PAYMENT }}<span ng-show="VM.isCheque()"> *</span></label>
                                    <div class="col-lg-8">
                                        <p class="input-group">
                                            <input id="editorDatePayment" kendo-date-picker
                                                   ng-model="VM.entity.date_payment"
                                                   data-k-format="'dd/MM/yyyy'"
                                                   k-ng-model="VM.entity.date_payment" />
                                        </p>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="well col-lg-12" style="margin-top: 20px; margin-bottom: 20px; padding: 0; padding-top: 15px;">
                                <div class="row">
                                    <div class="col-lg-12">
                                        <div class="form-group col-lg-6">
                                            <label for="editorSig1User" class="col-lg-4">{{ translation.EDITOR_SIG1_SIGNED_BY }}</label>
                                            <div class="col-lg-8">
                                                <input type="text"
                                                       class="form-control"
                                                       id="editorSig1User"
                                                       name="editor[sig1_user]"
                                                       title="{{VM.entity.sig1_user}}"
                                                       ng-model="VM.entity.sig1_user"
                                                       readonly>
                                            </div>
                                        </div>
                                        <div class="form-group col-lg-6">
                                            <label for="editorSig1SignedAt" class="col-lg-4">{{ translation.EDITOR_SIG1_SIGNED_AT }}</label>
                                            <div class="col-lg-8">
                                                <input type="text"
                                                       class="form-control"
                                                       id="editorSig1SignedAt"
                                                       name="editor[sig1_signAt]"
                                                       title="{{VM.entity.sig1_signAt}}"
                                                       ng-model="VM.entity.sig1_signAt"
                                                       readonly
                                                       dateformat="dd/MM/yyyy">
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row">
                                    <div class="col-lg-12">
                                        <div class="form-group col-lg-6">
                                            <label for="editorSig2User" class="col-lg-4">{{ translation.EDITOR_SIG2_SIGNED_BY }}</label>
                                            <div class="col-lg-8">
                                                <input type="text"
                                                       class="form-control"
                                                       id="editorSig2User"
                                                       name="editor[sig2_user]"
                                                       title="{{VM.entity.sig2_user}}"
                                                       ng-model="VM.entity.sig2_user"
                                                       readonly>
                                            </div>
                                        </div>
                                        <div class="form-group col-lg-6">
                                            <label for="editorSig2SignAt" class="col-lg-4">{{ translation.EDITOR_SIG2_SIGNED_AT }}</label>
                                            <div class="col-lg-8">
                                                <input type="text"
                                                       class="form-control"
                                                       id="editorSig2SignAt"
                                                       name="editor[sig2_signAt]"
                                                       title="{{VM.entity.sig2_signAt}}"
                                                       ng-model="VM.entity.sig2_signAt"
                                                       readonly
                                                       dateformat="dd/MM/yyyy">
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="row">
                                    <div class="col-lg-12">
                                        <div class="form-group col-lg-6">
                                            <label for="editorChequeStatus" class="col-lg-4">{{ translation.EDITOR_CHEQUE_STATUS }}</label>
                                            <div class="col-lg-8">
                                                <input type="text"
                                                       class="form-control text-center"
                                                       id="editorChequeStatus"
                                                       name="editor[cheque_status]"
                                                       title="{{VM.entity.cheque_status}}"
                                                       ng-model="VM.entity.cheque_status"
                                                       readonly
                                                       translate="cheque_status">
                                            </div>
                                        </div>
                                        <div class="form-group col-lg-6">
                                            <div id="editor-command-sign" class="btn-group pull-left" role="group" aria-label="Sign" style="padding-left: 15px;">
                                                <button type="button" class="btn btn-lg btn-primary" ng-disabled="!VM.isAllowSign || VM.isSigned" ng-click="VM.sign()">{{ translation.CMD_SIGN }}</button>
                                                <button type="button" class="btn btn-lg btn-primary" ng-disabled="!VM.isAllowUnSign || !VM.isSigned" ng-click="VM.unsign()">{{ translation.CMD_UNSIGN }}</button>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
 
                        <a class="btn btn-link" data-toggle="collapse" href="#cheque_more_info" aria-expanded="false" aria-controls="cheque_more_info">
                            {{ translation.TEXT_CHEQUE_MORE_INFO }}
                        </a>
 
                        <div class="collapse" id="cheque_more_info">
                            <div class="row">
                                <div class="col-lg-12">
                                    <div class="form-group text-center">
                                        <label>{{ translation.EDITOR_DATE_CREATED }}: </label>
                                        <span>{{ (VM.entity.date_created) ? (VM.convertStringToDate(VM.entity.date_created) | date: 'dd/MM/yyyy') : translation.TEXT_UNKNOWN }}</span>
                                          
                                        <label>{{ translation.EDITOR_DATE_UPLOADED }}: </label>
                                        <span>{{ (VM.entity.date_uploaded) ? (VM.convertStringToDate(VM.entity.date_uploaded) | date: 'dd/MM/yyyy') : translation.TEXT_UNKNOWN }}</span>
                                          
                                        <label>{{ translation.EDITOR_DATE_UPDATED }}: </label>
                                        <span>{{ (VM.entity.date_updated) ? (VM.convertStringToDate(VM.entity.date_updated) | date: 'dd/MM/yyyy') : translation.TEXT_UNKNOWN }} </span>
                                    </div>
                                </div>
                            </div>
 
                            <div class="row">
                                <div class="col-lg-12">
                                    <div class="form-group col-lg-6">
                                        <label for="editorTaxDeductionPercentage" class="col-lg-4">{{ translation.EDITOR_TAX_DEDUCTION_PERCENTAGE }}: </label>
                                        <div class="col-lg-8">
                                            <input type="number"
                                                   class="form-control"
                                                   id="editorTaxDeductionPercentage"
                                                   name="editor[tax_deduction_percentage]"
                                                   title="{{VM.entity.tax_deduction_percentage}}"
                                                   ng-model="VM.entity.tax_deduction_percentage">
                                        </div>
                                    </div>
                                    <div class="form-group col-lg-6">
                                        <label for="editorTaxDeductionSum" class="col-lg-4">{{ translation.EDITOR_TAX_DEDUCTION_SUM }}: </label>
                                        <div class="col-lg-8">
                                            <input type="number"
                                                   class="form-control"
                                                   id="editorTaxDeductionSum"
                                                   name="editor[tax_deduction_sum]"
                                                   title="{{VM.entity.tax_deduction_sum}}"
                                                   ng-model="VM.entity.tax_deduction_sum">
                                        </div>
                                    </div>
                                </div>
                            </div>
 
                            <div class="row" ng-show="VM.isCheque()">
                                <div class="col-lg-12">
                                    <div class="form-group col-lg-6">
                                        <label for="editorChequeCanceled" class="col-lg-4">{{ translation.EDITOR_CHEQUE_CANCELED }}: </label>
                                        <div class="col-lg-8 text-left">
                                            <input type="text"
                                                   class="form-control"
                                                   id="editorChequeCanceled"
                                                   name="editor[cheque_canceled]"
                                                   title="{{VM.entity.cheque_canceled}}"
                                                   ng-model="VM.entity.is_canceled"
                                                   readonly>
                                        </div>
                                    </div>
                                    <div class="form-group col-lg-6">
                                        <div class="form-group">
                                            <label for="editorDateChequeCanceled" class="col-lg-4">{{ translation.EDITOR_DATE_CHEQUE_CANCELED }}: </label>
                                            <div class="col-lg-8 text-left">
                                                <input type="datetime"
                                                       class="form-control"
                                                       id="editorDateChequeCanceled"
                                                       name="editor[date_cheque_canceled]"
                                                       title="{{VM.entity.date_cheque_canceled}}"
                                                       ng-model="VM.entity.date_canceled"
                                                       readonly>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
 
                            <div class="row" ng-show="!VM.state">
                                <div class="col-lg-12">
                                    <div class="form-group col-lg-6">
                                        <label for="editorChequeID" class="col-lg-4">{{ translation.EDITOR_CHEQUE_ID }}: </label>
                                        <div class="col-lg-8 text-left">
                                            <input type="text"
                                                   class="form-control"
                                                   id="editorChequeID"
                                                   name="editor[cheque_id]"
                                                   title="{{VM.entity['date_created:id']}}"
                                                   ng-model="VM.entity['date_created:id']"
                                                   readonly>
                                        </div>
                                    </div>
                                </div>
                            </div>
 
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <div id="toolbar">
                        <div id="command-editor-cancel" class="btn-group pull-right" role="group" aria-label="Cancel">
                            <button class="btn btn-lg btn-default pull-right" ng-click="VM.cancel()">{{ translation.CMD_CANCEL }}</button>
                        </div>
                        <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>
                        <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>
                    </div>
                </div>
            </div>
        </script>
    </div>
</div>

JS:

define(['app'], function(app) {
    app.controller('MainCtrl', function ($scope, $modal, $timeout, MainService, LoginService) {
 
        /* ========================================== */
        /*           GENERAL INITIALIZATIONS          */
        /* ========================================== */
 
        // Set the culture
        kendo.culture("he-IL");
 
        // Define scope variables
        $scope.VM = {};
        $scope.VM.isInitialized = false;
        $scope.VM.filter = {};
        $scope.VM.editor = {};
        $scope.VM.collections = {};
        $scope.VM.kendoGrid = null;
 
        // Define the view permissions
        $scope.VM.permissions = {
            allow_edit: true,
            allow_sign: true,
            allow_cancel: true
        };
 
        /* ========================================== */
        /*         INITIALIZING DATA MEMBERS          */
        /* ========================================== */
 
        $scope.init = function() {
            ///////////////////////////////////// Permissions
            // Check if the user is allowed to sign
            $scope.VM.isAllowSign = $scope.VM.permissions.allow_sign;
            // Check if the user is allowed to unsign
            $scope.VM.isAllowUnSign = $scope.VM.permissions.allow_sign;
            // Check if the user is allowed to edit existing items
            $scope.VM.isAllowEdit = $scope.VM.permissions.allow_edit;
            // Check if the user is allowed to add new items
            $scope.VM.isAllowAdd = $scope.VM.permissions.allow_edit;
            // Check if the user is allowed to cancel cheques
            $scope.VM.isAllowCancel = $scope.VM.permissions.allow_cancel;
        };
 
        /* ========================================== */
        /*        INITIALIZING THE COLLECTIONS        */
        /* ========================================== */
 
        /* The list of all suppliers */
        $scope.VM.getSuppliers = function() {
            MainService.getSuppliers().then(function(data) {
                var suppliers = [];
                data.forEach(function (supplier) {
                    suppliers.push({ value: supplier.name, text: supplier.name});
                });
                $scope.VM.collections.suppliers = suppliers;
            });
        } ();
        $scope.VM.collections.suppliers = [];
        /* The list of all expensesCause */
        $scope.VM.getExpensesCause = function() {
            MainService.getExpensesCause().then(function(data) {
                var expenses_cause = [];
                data.forEach(function (cause) {
                    expenses_cause.push({ value: cause.expense_name, text: cause.expense_name});
                });
                $scope.VM.collections.expensesCause = expenses_cause;
            });
        } ();
        $scope.VM.collections.expensesCause = [];
        /* The list of all subExpensesCause */
        $scope.VM.getSubExpensesCause = function() {
            MainService.getSubExpensesCause().then(function(data) {
                var sub_expenses_cause = [];
                data.forEach(function (cause) {
                    sub_expenses_cause.push({ value: cause.sub_expense_name, text: cause.sub_expense_name});
                });
                $scope.VM.collections.subExpensesCause = sub_expenses_cause;
            });
        } ();
        $scope.VM.collections.subExpensesCause = [];
 
        /* ========================================== */
        /*      INITIALIZING THE DEFAULT FILTER       */
        /* ========================================== */
 
        $scope.VM.filter.status = "active";
        $scope.VM.filter.from_date = moment().subtract(2, "month").toDate();
        $scope.VM.filter.to_date = moment().toDate();
 
        /* ========================================== */
        /*            INITIALIZING THE GRID           */
        /* ========================================== */
 
        $scope.VM.dataSource = new kendo.data.DataSource({
            schema: {
                model: {
                    // TODO: change id to date_created:id
                    id: "date_created_id",
                    fields: {
                        date_created: {
                            editable: false,
                            type: "date"
                        },
                        project_name: {
                            editable: false
                        },
                        supplier_name: {
                            editable: true,
                            validation: {
                                required: true
                            }
                        },
                        info: {
                            editable: true
                        },
                        expense_cause: {
                            editable: true,
                            nullable: true
                        },
                        sub_expense_cause: {
                            editable: true,
                            nullable: true
                        },
                        cheque_number: {
                            editable: false
                        },
                        amount: {
                            editable: false
                        },
                        sig1: {
                            editable: false
                        },
                        sig2: {
                            editable: false
                        },
                        cheque_status: {
                            editable: false
                        }
                    }
                },
                data: "data",
                total: function(response) {
                    return response.data.length;
                }
            },
            serverPaging: true,
            serverSorting: true,
            pageSize: 500,
            serverPaging: false,
            serverSorting: false,
            serverFiltering: false,
            autoSync: true,
            transport: {
                read: function(e) {
                    // Save the 'e' for future reference
                    $scope.VM.readOptions = e;
 
                    // Start the spinner
                    kendo.ui.progress($("div[kendo-grid]"), true);
 
                    var from_date = $scope.VM.filter.from_date;
                    var to_date = $scope.VM.filter.to_date;
                    MainService.getCheques(from_date, to_date).then(function(data) {
                        e.success(data);
 
                        // Stop the spinner
                        kendo.ui.progress($("div[kendo-grid]"), false);
                    });
                },
                update: function(e) {
                    // Save the 'e' for future reference
                    $scope.VM.updateOptions = e;
 
                    MainService.updateCheque(e.data).then(function(data) {
                        e.success(data);
                    });
                },
                create: function(e) {
                    // Save the 'e' for future reference
                    $scope.VM.insertOptions = e;
 
                    MainService.addCheque(e.data).then(function(data) {
                        e.success(data);
                    });
                },
                destroy: function(e) {
 
                }
            },
            aggregate: [
                { field: "amount", aggregate: "sum" }
            ]
        });
        $scope.VM.projectDS = new kendo.data.DataSource({
            data: []
        });
        $scope.VM.supplierDS = new kendo.data.DataSource({
            data: []
        });
        $scope.VM.infoDS = new kendo.data.DataSource({
            data: []
        });
        $scope.VM.expenseCauseDS = new kendo.data.DataSource({
            data: []
        });
        $scope.VM.subExpenseCauseDS = new kendo.data.DataSource({
            data: []
        });
        $scope.VM.chequeNumberDS = new kendo.data.DataSource({
            data: []
        });
        $scope.VM.amountDS = new kendo.data.DataSource({
            data: []
        });
 
        /* The grid definition */
        $scope.VM.gridOptions = {
            height: 630,
            filterable: {
                mode: "row",
                operators: {
                    string: {
                        eq: "שווה ",
                        neq: "לא שווה",
                        startswith: "מתחיל ב",
                        contains: "מכיל",
                        doesnotcontain: "לא מכיר",
                        endswith: "מסתיים ב"
                    }
                }
            },
            scrollable: true,
            sortable: true,
            selectable: "multiple, row",
            editable: true,
            resizable: false,
            reorderable: false,
            groupable: false,
            pageable: true,
            //pageable: {
            //    refresh: true,
            //    pageSizes: true,
            //    buttonCount: 5
            //},
            toolbar: [
                { name: "excel", text: " ייצוא לאקסל" },
                //{ name: "save", text: "שמור"}
            ],
            excel: { fileName: "results.xlsx" },
            dataSource: $scope.VM.dataSource,
            columns: [
                //{ field: "cheque_id", title: "מזהה צ'ק" },
                //{
                //    headerTemplate: "<input type='checkbox' class='checkbox checkAll' ng-click='VM.selectAll($event)' />",
                //    template: "<input type='checkbox' class='checkbox checkRow' ng-click='VM.selectRow($event)' />",
                //    width: "26px"
                //},
                {
                    field: "date_created",
                    title: "תאריך",
                    template: "<div ng-bind='dataItem.date_created | date: \"dd/MM/yyyy\"' title='{{ dataItem.date_created | date: \"dd/MM/yyyy\" }}'></div>",
                    filterable: false
                },
                {
                    field: "project_name",
                    title: "פרויקט",
                    template: "<div title='{{ dataItem.project_name }}'>#: data.project_name #</div>",
                    filterable: {
                        cell: {
                            dataSource: $scope.VM.dataSource,
                            operator: "contains"
                        }
                    }
                },
                {
                    field: "supplier_name",
                    title: "מוטב",
                    template: "<div title='{{ dataItem.supplier_name }}'>#: data.supplier_name #</div>",
                    filterable: {
                        cell: {
                            dataSource: $scope.VM.dataSource,
                            operator: "contains"
                        }
                    },
                    editor: function(container, options) {
                        //container.append(
                        //    "<select kendo-combo-box "+
                        //        "k-data-text-field=\"'text'\" "+
                        //        "k-data-value-field=\"'value'\" "+
                        //        "k-filter='contains' "+
                        //        "k-auto-bind='false' "+
                        //        "k-data-source='VM.collections.suppliers' "+
                        //        "data-bind='value:"+options.field+"'>"+
                        //    "</select>");
                        container.append(
                            "<input kendo-auto-complete " +
                                "k-data-text-field=\"'text'\" "+
                                "k-data-value-field=\"'value'\" "+
                                "k-auto-bind='false' "+
                                "k-value-primitive='true' "+
                                "k-data-source='VM.collections.suppliers' "+
                                "data-bind='value:"+options.field+"'/>");
                    }
                },
                {
                    field: "info",
                    title: "פרטים" ,
                    template: "<div title='{{ dataItem.info }}'>#: data.info #</div>",
                    filterable: {
                        cell: {
                            dataSource: $scope.VM.dataSource,
                            operator: "contains"
                        }
                    },
                    width: "250px"
                },
                {
                    field: "expense_cause",
                    title: "סעיף הוצאה",
                    template: "<div title='{{ dataItem.expense_cause }}'>#: data.expense_cause #</div>",
                    filterable: {
                        cell: {
                            dataSource: $scope.VM.dataSource,
                            operator: "contains"
                        }
                    },
                    editor: function(container, options) {
                        //container.append(
                        //    "<select kendo-combo-box "+
                        //        "k-data-text-field=\"'text'\" "+
                        //        "k-data-value-field=\"'value'\" "+
                        //        "k-filter='contains' "+
                        //        "k-auto-bind='false' "+
                        //        "k-data-source='VM.collections.expensesCause' "+
                        //        "data-bind='value:"+options.field+"'>"+
                        //    "</select>");
                        container.append(
                            "<input kendo-auto-complete " +
                                "k-data-text-field=\"'text'\" "+
                                "k-data-value-field=\"'value'\" "+
                                "k-auto-bind='false' "+
                                "k-value-primitive='true' "+
                                "k-data-source='VM.collections.expensesCause' "+
                                "k-rebind='VM.collections.expensesCause' "+
                                "data-bind='value:"+options.field+"'/>");
                    }
                },
                {
                    field: "sub_expense_cause",
                    title: "תת סעיף הוצאה",
                    template: "<div title='{{ dataItem.sub_expense_cause }}'>#: data.sub_expense_cause #</div>",
                    filterable: {
                        cell: {
                            dataSource: $scope.VM.dataSource,
                            operator: "contains"
                        }
                    },
                    editor: function(container, options) {
                        //container.append(
                        //    "<select kendo-combo-box "+
                        //        "k-data-text-field=\"'text'\" "+
                        //        "k-data-value-field=\"'value'\" "+
                        //        "k-filter='contains' "+
                        //        "k-auto-bind='false' "+
                        //        "k-data-source='VM.collections.subExpensesCause' "+
                        //        "data-bind='value:"+options.field+"'>"+
                        //    "</select>");
                        container.append(
                            "<input kendo-auto-complete " +
                                "k-data-text-field=\"'text'\" "+
                                "k-data-value-field=\"'value'\" "+
                                "k-auto-bind='false' "+
                                "k-value-primitive='true' "+
                                "k-data-source='VM.collections.subExpensesCause' "+
                                "k-rebind='VM.collections.subExpensesCause' "+
                                "data-bind='value:"+options.field+"'/>");
                    }
                },
                {
                    field: "cheque_number",
                    title: "מספר צ'ק",
                    template: "<div title='{{ dataItem.cheque_number }}'>#: data.cheque_number #</div>",
                    filterable: {
                        cell: {
                            dataSource: $scope.VM.dataSource,
                            operator: "startswith"
                        }
                    }
                },
                {
                    field: "amount",
                    title: "סכום" ,
                    template: "<div ng-bind='dataItem.amount | currency: \"₪\"' title='{{ dataItem.amount | currency: \"₪\" }}'></div>",
                    format: "{0:c0}",
                    footerTemplate: "<div>{{ translation.TEXT_AMOUNT_SUM }}: #= kendo.toString(sum, 'c0') #</div>",
                    width: "180px"
                },
                {
                    field: "sig1",
                    title: "חתימה 1",
                    template: function(dataItem) {
                        var template;
                        var chequeId = dataItem.uid;
 
                        // TODO: need to convert date from string to actual date
 
                        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') : '' }}\">" +
                                        "{{ dataItem.sig2_user || ' ' }}" +
                                        "<div class='quicksign bubble bubble-sig1-"+ chequeId +" bottom'>" +
                                            "<div class='quicksign-user'>{{ loggedInUser.full_name }}</div>" +
                                            "<div class='quicksign-amount'>{{ dataItem.amount | currency: '₪' }}</div>" +
                                            "<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>" +
                                            "<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>" +
                                        "</div>" +
                                    "</div>";
 
                        return template;
                    },
                    filterable: false
                },
                {
                    field: "sig2",
                    title: "חתימה 2",
                    template: function(dataItem) {
                        var template;
                        var chequeId = dataItem.uid;
 
                        // TODO: need to convert date from string to actual date
 
                        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') : '' }}\">" +
                                        "{{ dataItem.sig2_user || ' ' }}" +
                                        "<div class='quicksign bubble bubble-sig2-"+ chequeId +" bottom'>" +
                                            "<div class='quicksign-user'>{{ loggedInUser.full_name }}</div>" +
                                            "<div class='quicksign-amount'>{{ dataItem.amount | currency: '₪' }}</div>" +
                                            "<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>" +
                                            "<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>" +
                                        "</div>" +
                                    "</div>";
 
                        return template;
                    },
                    filterable: false
                },
                {
                    field: "cheque_status",
                    title: "סטאטוס",
                    template: function(dataItem) {
                        var result = "";
                        var status = $scope.translationAPI.translateStatus(dataItem.cheque_status);
                        var css_class = dataItem.cheque_status;
 
                        result += "<div title='"+status+"' class='"+css_class+"'>";
                        result += status;
                        result += "</div>";
 
                        return result;
                    },
                    filterable: false
                }
            ],
 
            // Events
            dataBound: function(e) {
                $scope.VM.isInitialized = true;
                $scope.VM.kendoGrid =  $("div[kendo-grid]").getKendoGrid();
                $scope.VM.rowsCount = $scope.VM.kendoGrid.dataItems().length;
 
                // Get the status count
                $scope.VM.activeStatusCount = $scope.VM.getStatusCount('active');
                $scope.VM.unsigned = $scope.VM.getStatusCount('unsigned');
                $scope.VM.pending = $scope.VM.getStatusCount('pending');
                $scope.VM.signedStatusCount = $scope.VM.getStatusCount('signed');
                $scope.VM.allStatusCount = $scope.VM.getStatusCount('all');
            },
 
            change: function(e) {
                var grid = $scope.VM.kendoGrid;
                var results = grid.select();
                $scope.VM.selectedRows = results;
                $scope.VM.selectedEntity = $scope.VM.kendoGrid.dataItem(results[0]);
                $scope.VM.selectedRowsCount = results.length;
                $scope.VM.isEntitySelected = results.length > 0;
                $scope.VM.isOneEntitySelected = results.length == 1;
                $scope.VM.isFromSAP = $scope.VM.selectedEntity.cheque_id;
                $scope.VM.isFromSAP = $scope.VM.selectedEntity.is_canceled == "Y";
                $scope.VM.isSigned = $scope.VM.isEntitySigned($scope.VM.selectedEntity);
            }
        };
 
        /* ========================================== */
        /*                  Methods                   */
        /* ========================================== */
 
        ///////////////////////////////////// Helper methods
        // Check if the cheque is already signed
        $scope.VM.isEntitySigned = function(dataItem) {
            if ($scope.loggedInUser.signer == 1)
                return (dataItem.sig1_user && dataItem.sig1_signAt) ? true : false;
            else if ($scope.loggedInUser.signer == 2)
                return (dataItem.sig2_user && dataItem.sig2_signAt) ? true : false;
            else
                return false;
        };
        // Get the number of results by statys
        $scope.VM.getStatusCount = function(status) {
            var grid = $scope.VM.kendoGrid;
            if (!grid)
                return;
 
            var results = grid.dataSource.data();
            if (!results)
                return;
 
            var count = 0;
            if (status === "all") {
              count = results.length;
            } else if (status === "active") {
                angular.forEach(results, function(value,key) {
                    if (value.is_canceled == "N")
                        count++;
                });
            } else {
                angular.forEach(results, function(value,key) {
                    if (value.cheque_status == status)
                        count++;
                });
            }
 
            return count;
        };
 
        ///////////////////////////////////// Commands
        // Opens the entity detail view
        $scope.VM.openEntityModal = function(state) {
            var instance = $modal.open({
                templateUrl: 'addNewModal.html',
                controller: 'AddNewCtrl',
                size: 'lg',
                resolve: {
                    entity: function(){
                        var grid = $scope.VM.kendoGrid;
                        var results = grid.select();
                        return grid.dataItem(results[0]);
                    },
                    state: function() {
                        return state;
                    },
                    translation: function() {
                        return $scope.translation;
                    },
                    translationAPI: function() {
                        return $scope.translationAPI;
                    },
                    permissions: function() {
                        return $scope.VM.permissions;
                    },
                    loggedInUser: function() {
                        return $scope.loggedInUser;
                    }
                }
            });
 
            instance.result.then(function(entity) {
                if (entity) {
                    if (state) {
                        $scope.VM.addNew(entity);
                    }
                    else {
                        $scope.VM.update(entity);
                    }
 
                    // Save the changes
                    $scope.VM.dataSource.sync();
                }
            });
        };
        // Add a new entity
        $scope.VM.addNew = function(entity){
            $scope.VM.dataSource.add(entity)
        };
        // Update the entity
        $scope.VM.update = function(entity){
 
        };
        // Sign a cheque
        $scope.VM.sign = function(dataItem) {
            if ($scope.loggedInUser.signer == 1) {
                dataItem.sig1_user = $scope.loggedInUser.full_name;
                dataItem.sig1_signAt = moment().toDate();
            }
            else {
                dataItem.sig2_user = $scope.loggedInUser.full_name;
                dataItem.sig2_signAt = moment().toDate();
            }
 
            // Save the changes
            $scope.VM.dataSource.sync();
        };
        // Sign all selected cheques
        $scope.VM.signAll = function() {
            var isInitialized = $scope.VM.isInitialized;
            if (!isInitialized)
                return;
 
            var grid = $scope.VM.kendoGrid;
            if (!grid)
                return;
 
            var results = grid.select();
            if (!results)
                return;
 
            for (i=0; i<results.length; i++) {
                $scope.VM.sign(grid.dataItem(results[i]));
            }
        };
        // Sign a cheque
        $scope.VM.unsign = function(dataItem) {
            if ($scope.loggedInUser.signer == 1) {
                dataItem.sig1_user = "";
                dataItem.sig1_signAt = "";
            }
            else {
                dataItem.sig2_user = "";
                dataItem.sig2_signAt = "";
            }
 
            // Save the changes
            $scope.VM.dataSource.sync();
        };
        // Sign all selected cheques
        $scope.VM.unsignAll = function() {
            var isInitialized = $scope.VM.isInitialized;
            if (!isInitialized)
                return;
 
            var grid = $scope.VM.kendoGrid;
            if (!grid)
                return;
 
            var results = grid.select();
            if (!results)
                return;
 
            for (i=0; i<results.length; i++) {
                $scope.VM.unsign(grid.dataItem(results[i]));
            }
        };
        // Cancel the cheque
        $scope.VM.cancelAction = function(entity) {
            var entity = $scope.VM.selectedRows[0];
 
            entity.is_canceled = "Y";
 
            /* Send the new entity to the server for cancel */
            MainService.modifyCancellation(entity).then(function(data) {
 
            });
        };
        // Undo canceled cheque
        $scope.VM.undoCancelAction = function(entity) {
            var entity = $scope.VM.selectedRows[0];
 
            entity.is_canceled = "N";
 
            /* Send the new entity to the server for cancel */
            MainService.modifyCancellation(entity).then(function(data) {
 
            });
        };
        // Get all cheques from within the dates range
        $scope.VM.getCheques = function(e) {
            $scope.VM.dataSource.read($scope.VM.readOptions);
        };
        // Refresh the data, if any changes has been made
        var _chequeFlowInProgress = false;
        $scope.VM.getChequesFlow = function() {
            (function tick() {
                var project = $scope.VM.filter.project || "";
                var from_date = $scope.VM.filter.from_date;
                var to_date = $scope.VM.filter.to_date;
 
                if (_chequeFlowInProgress)
                    return;
 
                MainService.getChequesFlow(project, from_date, to_date).then(function(data) {
                    _chequeFlowInProgress = true;
 
                    var index, jndex;
                    for (index=0; index<data.length; index++) {
                        var action = data[index].type;
                        var flag = false;
 
                        var grid = $scope.VM.kendoGrid;
                        if (!grid)
                            return;
 
                        var results = grid.dataItems();
                        if (!results)
                            return;
 
                        for (jndex=0; jndex<results.length && !flag; jndex++){
                            var currRow = results[jndex];
                            switch (action) {
                                case "update": {
                                    if (data[index]["date_created:id"] == currRow["date_created:id"]) {
                                        currRow.action = data[index].action;
                                        currRow.project_name = data[index].project_name;
                                        currRow.expense_cause = data[index].expense_cause;
                                        currRow.sub_expense_cause = data[index].sub_expense_cause;
                                        currRow.info = data[index].info;
                                        currRow.supplier_name = data[index].supplier_name;
                                        currRow.supplier_address = data[index].supplier_address;
                                        currRow.amount = data[index].amount;
                                        currRow.cheque_number = data[index].cheque_number;
                                        currRow.date_payment = data[index].date_payment;
                                        currRow.sig1_user = data[index].sig1_user;
                                        currRow.sig1_signAt = data[index].sig1_signAt;
                                        currRow.sig2_user = data[index].sig2_user;
                                        currRow.sig2_signAt = data[index].sig2_signAt;
                                        currRow.cheque_status = data[index].cheque_status;
                                        currRow.date_created = data[index].date_created;
                                        currRow.date_uploaded = data[index].date_uploaded;
                                        currRow.date_updated = data[index].date_updated;
                                        currRow.tax_deduction_percentage = data[index].tax_deduction_percentage;
                                        currRow.tax_deduction_sum = data[index].tax_deduction_sum;
                                        currRow.is_canceled = data[index].is_canceled;
                                        currRow.date_canceled = data[index].date_canceled;
 
                                        // Mark iteration as completed
                                        flag = true;
                                    }
                                    break;
                                }
                                case "insert": {
                                    if (data[index]["date_created:id"] == currRow["date_created:id"]) {
                                        // Mark iteration as completed
                                        flag = true;
                                    }
                                    break;
                                }
                                default: {
                                    if (action)
                                        console.error("Can't load entity from flow, unknown state '"+action+"'");
                                    else
                                        console.error("Can't load entity from flow, the 'type' is missing");
                                }
                            }
                        }
 
                        if (!flag) {
                            if (action == "insert") {
                                // TODO: insert the item to the grid
                                grid.dataSource.add(data[index]);
                            }
                        }
                    };
 
                    _chequeFlowInProgress = false;
                });
 
                var timer = $timeout(tick, 1000);
                $scope.$on("$destroy", function(event) {
                    $timeout.cancel(timer);
                });
            })();
        }();
 
        ///////////////////////////////////// Presentation
        // Responsible for filtering the grid by the status field
        $scope.VM.changeStatus = function(e, status) {
            e.preventDefault();
 
            $scope.VM.filter.status = status;
            switch (status) {
                case "all": {
                    $scope.VM.dataSource.filter({} );
                    break;
                }
                case "active": {
                    $scope.VM.dataSource.filter({ field: "is_canceled", operator: "eq", value: "N"} );
                    break;
                }
                case "unsigned":
                case "pending":
                case "signed": {
                    $scope.VM.dataSource.filter({ field: "cheque_status", operator: "eq", value: status} );
                    break;
                }
            }
        };
        // Save the position of the mouse
        $(document).mousemove(function(e) {
            window.x = e.pageX;
            window.y = e.pageY;
        });
        // Show the quicksign panel
        $scope.VM.showQuickSign = function(sig, chequeId) {
            var handle = $(".bubble-sig"+sig+"-"+chequeId);
            var isVisible = $(".bubble-sig"+sig+"-"+chequeId+":visible");
 
            if (isVisible.length > 0)
                return;
 
            var grid = $("div[kendo-grid]");
            var gridHalfPosition = grid.offset().top + (grid.height() / 2);
 
            if (window.y < gridHalfPosition) {
                handle.removeClass("bottom");
                handle.addClass("top");
            }
            else {
                handle.removeClass("top");
                handle.addClass("bottom");
            }
 
            handle.show();
        };
        // Hide the quicksign panel
        $scope.VM.hideQuickSign = function(sig, chequeId) {
            var handle = $(".bubble-sig"+sig+"-"+chequeId);
            handle.hide();
        };
    });
 
});

0
Lior
Top achievements
Rank 1
answered on 06 May 2015, 05:40 AM

BTW as I mentioned before, I also have a serious memory leak (just by sorting, filtering, paging etc')

0
Lior
Top achievements
Rank 1
answered on 06 May 2015, 07:05 AM
BTW, here's how it looks like, just as a reference.
0
T. Tsonev
Telerik team
answered on 07 May 2015, 03:45 PM

Hello Lior,

We'll need a running sample to verify any performance concerns.

A couple of hundred rows multiplied by the number of bindings you have in them can quickly mount up to a significant overhead.

Does the node count decrease when you force a manual garbage collection? See Memory Usage Leaks with Kendo UI and AngularJS for details.

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 10 May 2015, 04:28 AM

I'll work on a running example.

1. No, forcing manual garbage collection didn't make any effect (ran it multiple times).

2. I moved from angular's ui-grid to kendo grid, I didn't have any performance issues with it - the grid was far from perfect but it worked. I'd expect nothing less from telerik's components.

0
Lior
Top achievements
Rank 1
answered on 11 May 2015, 05:08 PM

OK, here's a running example - http://dojo.telerik.com/@kedmyster/UZOFU

you can see:

* Moving from one page to another/sorting takes too much time.

* The memory is rising on every operation you make, never dropping, even when you manually call the garbage collector.

 Please let me know ASAP if you need anything else from me.

 

Thanks!

Lior

0
T. Tsonev
Telerik team
answered on 13 May 2015, 07:25 AM

Hello Lior,

Thank you for taking the the time to compile the sample.

I did find a few small issues with it, for example AngularJS is included twice. This is the updated version of the snippet.

That said, the performance is without doubt abysmal. Most of the time seems to be spent in compiling the AngularJS templates. I'm not sure what we can do about this.

Memory usage is more interesting. In Chrome I see constant increase just by paging. In Firefox things seem to be much better. This discrepancy lead me to this issue.

Again, I'm not sure what we can do about Chrome in this case. If ui-grid works well for you then stick with it. I can't offer a better advice in this case.

Regards,
T. Tsonev
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
Tags
Grid
Asked by
Lior
Top achievements
Rank 1
Answers by
T. Tsonev
Telerik team
Lior
Top achievements
Rank 1
Share this question
or