Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Angular Bootstrap datepicker inside of ui-grid (unstable 3.0.0)

I am using AngularJS Bootstrap datepicker inside of the ui-grid (3.0.0 - unstable)

I have implemented the datepicker through the custom cellTemplate (ui-grid):

{
      field: 'Wiedervorl',
      displayName: 'Wiedervorl.',
      enableCellEdit: true,
      enableCellEditOnFocus: true,
      enableHiding: false,
      cellTemplate: '<div><input ng-model="row.entity.Wiedervorl" ng-change="grid.appScope.addModifyFlag(row.entity)" ng-click="opened = true;" datepicker-popup="dd.MM.yyyy" is-open="opened" datepicker-options="grid.appScope.dateOptions" datepicker-append-to-body="true" type="text" /></div>',
      cellFilter: 'date',
      cellClass: function () {
        return 'text-left';
      },
      filter: {
        placeholder: 'date',
        condition: uiGridConstants.filter.CONTAINS
      },
      width: '7%'
    },

There is a flag 'opened' inside of the datepicker input which is isolated exactly for one instance of this input element (datepicker instance).

The problem is, that after opening one more datepicker in another row, the previous one doesn't close and multiple datepickers can be opened at the same time.

enter image description here

I tried to add "ng-blur" to the input, but it executed at the moment, when datepicker is opening.

Any ideas, how to accomplish the closing of previous datepicker instance inside of the UI-Grid ? To make only one active at a time.

Thanks

like image 353
Anton Honcharuk Avatar asked May 18 '15 06:05

Anton Honcharuk


3 Answers

Maybe it will be helpful for someone. I did a detailed investigation into how to integrate a Bootstrap detepicker to a UI Grid.

At first, you need to use a custom editableCellTemplate and a custom directive which correctly processes END_EDIT and CANCEL_EDIT events.

Below you can find the code of my directive:

var app = angular.module('ui.grid.edit');

app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants', 'uiGridEditConstants', function($timeout, $document, uiGridConstants, uiGridEditConstants) {
    return {
        template: function(element, attrs) {
            var html = '<div class="datepicker-wrapper" ><input uib-datepicker-popup is-open="isOpen" ng-model="' + attrs.rowField + '" ng-change="changeDate($event)" on-open-focus="false" disabled/></div>';
            return html;
        },
        require: ['?^uiGrid', '?^uiGridRenderContainer'],
        scope: true,
        compile: function() {
            return {
                pre: function($scope, $elm, $attrs) {

                },
                post: function($scope, $elm, $attrs, controllers) {
                    var setCorrectPosition = function() {
                        var gridElement = $('.ui-grid-viewport');
                        var gridPosition = {
                            width: gridElement.outerWidth(),
                            height: gridElement.outerHeight(),
                            offset: gridElement.offset()
                        };

                        var cellElement = $($elm);
                        var cellPosition = {
                            width: cellElement.outerWidth(),
                            height: cellElement.outerHeight(),
                            offset: cellElement.offset()
                        };

                        var datepickerElement = $('ul', cellElement);
                        var datepickerPosition = {
                            width: datepickerElement.outerWidth(),
                            height: datepickerElement.outerHeight()
                        };
                        var newOffsetValues = {};

                        var isFreeOnRight = (gridPosition.width - (cellPosition.offset.left - gridPosition.offset.left) - cellPosition.width) > datepickerPosition.width;
                        if (isFreeOnRight) {
                            newOffsetValues.left = cellPosition.offset.left + cellPosition.width;
                        } else {
                            newOffsetValues.left = cellPosition.offset.left - datepickerPosition.width;
                        }

                        var freePixelsOnBottom = gridPosition.height - (cellPosition.offset.top - gridPosition.offset.top) - cellPosition.height;
                        var freePixelsOnTop = gridPosition.height - freePixelsOnBottom - cellPosition.height;
                        var requiredPixels = (datepickerPosition.height - cellPosition.height) / 2;
                        if (freePixelsOnBottom >= requiredPixels && freePixelsOnTop >= requiredPixels) {
                            newOffsetValues.top = cellPosition.offset.top - requiredPixels + 10;
                        } else if (freePixelsOnBottom >= requiredPixels && freePixelsOnTop < requiredPixels) {
                            newOffsetValues.top = cellPosition.offset.top - freePixelsOnTop + 10;
                        } else {
                            newOffsetValues.top = gridPosition.height - datepickerPosition.height + gridPosition.offset.top - 20;
                        }

                        datepickerElement.offset(newOffsetValues);
                        datepickerElement.css("visibility", "visible");
                    };

                    $timeout(function() {
                        setCorrectPosition();
                    }, 0);

                    $scope.isOpen = true;

                    var uiGridCtrl = controllers[0];
                    var renderContainerCtrl = controllers[1];

                    var onWindowClick = function (evt) {
                        var classNamed = angular.element(evt.target).attr('class');
                        var inDatepicker = (classNamed.indexOf('datepicker-calendar') > -1);
                        if (!inDatepicker && evt.target.nodeName !== "INPUT") {
                            $scope.stopEdit(evt);
                        }
                    };

                    var onCellClick = function (evt) {
                        console.log('click')
                        angular.element(document.querySelectorAll('.ui-grid-cell-contents')).off('click', onCellClick);
                        $scope.stopEdit(evt);
                    };

                    $scope.changeDate = function (evt) {
                        $scope.stopEdit(evt);
                    };

                    $scope.$on(uiGridEditConstants.events.BEGIN_CELL_EDIT, function () {
                        if (uiGridCtrl.grid.api.cellNav) {
                            uiGridCtrl.grid.api.cellNav.on.navigate($scope, function (newRowCol, oldRowCol) {
                                $scope.stopEdit();
                            });
                        } else {
                            angular.element(document.querySelectorAll('.ui-grid-cell-contents')).on('click', onCellClick);
                        }
                        angular.element(window).on('click', onWindowClick);
                    });

                    $scope.$on('$destroy', function () {
                        angular.element(window).off('click', onWindowClick);
                    });

                    $scope.stopEdit = function(evt) {
                        $scope.$emit(uiGridEditConstants.events.END_CELL_EDIT);
                    };

                    $elm.on('keydown', function(evt) {
                        switch (evt.keyCode) {
                            case uiGridConstants.keymap.ESC:
                                evt.stopPropagation();
                                $scope.$emit(uiGridEditConstants.events.CANCEL_CELL_EDIT);
                                break;
                        }
                        if (uiGridCtrl && uiGridCtrl.grid.api.cellNav) {
                            evt.uiGridTargetRenderContainerId = renderContainerCtrl.containerId;
                            if (uiGridCtrl.cellNav.handleKeyDown(evt) !== null) {
                                $scope.stopEdit(evt);
                            }
                        } else {
                            switch (evt.keyCode) {
                                case uiGridConstants.keymap.ENTER:
                                case uiGridConstants.keymap.TAB:
                                    evt.stopPropagation();
                                    evt.preventDefault();
                                    $scope.stopEdit(evt);
                                    break;
                            }
                        }
                        return true;
                    });
                }
            };
        }
    };
}]);

Note! My directive requires jQuery for setting the correct position of the datepicker inside the grid. If you don't use jQuery, you should comment calling the setCorrectPosition() in the directive. In this case datepicker will be placed in the grid according to the standard behavior.

You can find some extra information on how use it and examples code at Github: https://github.com/Joiler/ui-grid-edit-datepicker

Plunker: http://plnkr.co/edit/4mNr86cN6wFOLYQ02QND

like image 62
Alexey Patsovsky Avatar answered Oct 05 '22 13:10

Alexey Patsovsky


Just in-case if somebody comes across like I did, ui-grid also already have it's own built-in date picker option.

 $scope.gridOptions.columnDefs = [
    { name: 'registered', 
      displayName: 'Registered', 
      type: 'date', 
      cellFilter:'date:"yyyy-MM-dd"'}
];

Unless a specific type of external date-picker is required like OP, this seems pretty handy and easy to use.

like image 32
Rajush Avatar answered Oct 05 '22 15:10

Rajush


You need to implement a directive with the date picker in it, and that directive needs to provide END_EDIT and CANCEL_EDIT events to the grid. Refer the edit tutorial: http://ui-grid.info/docs/#/tutorial/201_editable (just above ColumnDef options).

You can see the code associated with the dropdown directive in https://github.com/angular-ui/ng-grid/blob/master/src/features/edit/js/gridEdit.js, second block from the bottom

like image 36
PaulL Avatar answered Oct 05 '22 14:10

PaulL