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.
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
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With