Angular ui-grid auto height not working - angularjs

I am using angular ui-grid for show records. I have a product which has only 7 records, and another product which has 200 records. By default max row are selected to 20 when records are greater than 20, when records are less than 20 grid will auto resize depending on records count.
Problem is that; when I load 7 records in grid, the height of grid is according to 7 records, keeping there without refreshing page, when I type another records which has 200 entries in search box and submit, it assigns all records to grid but the size of grid remains the same as of 7 records.
I need to make grid auto resizeable according to records, keeping 20 records in page in records are more than or equal to 20.
here is some code from grid directive;
scope.configuration = {
data: scope.data,
exporterCsvFilename: 'testfile' + '.csv',
exporterMenuPdf: true,
enableSelectAll: true,
// enablePaging: true,
enablePaginationControls: true,
paginationPageSizes: [10, 20, 30, 40, 50, 100],
enableGridMenu: true,
enableFiltering: true,
paginationPageSize: (scope.largeGrid) ? 20 : 10,
enableHorizontalScrollbar: 1,
enableVerticalScrollbar: 0,
heights
scope.getGridHeight = function (data) {
var length = (scope.configuration.paginationPageSize > data.length) ? data.length : scope.configuration.paginationPageSize;
var rowHeight = (scope.autoHeightColumn) ? scope.getAutoHeight(data, scope.autoHeightColumn) : 30; // your row height
var headerHeight = 50; // your header height
var filterHeight = 62; // your filter height
if (scope.autoHeightColumn) {
return rowHeight + headerHeight + filterHeight + 'px';
}
return length * rowHeight + headerHeight + filterHeight + 'px';
};
scope.$watch('configuration.paginationPageSize', function () {
scope.gridHeight = scope.getGridHeight(scope.data);
//kind of hack: as minification does not setting interpolated variable.
$('#grid').css('height', scope.gridHeight);
});
scope.getAutoHeight = function (data, colum) {
//Todo: Get pagination and data height separately
var totalHeight = 0;
for (var i = 0; i < data.length; i++) {
var columnHeight = data[i][colum].length;
if (columnHeight) {
columnHeight = (columnHeight / 12) * 23;
} else {
columnHeight = 23;
}
totalHeight += columnHeight;
}
return totalHeight;
};

we can achieve ui-grid auto height using some css overrides.
.ui-grid-viewport
{
overflow-x: hidden !important;
overflow-y: auto !important;
}
.ui-grid, .ui-grid-viewport
{
height: auto !important;
}
.ui-grid-viewport, .ui-grid-canvas
{
height: auto !important;
}
.ui-grid-row, .ui-grid-cell
{
height: auto !important;
}
.ui-grid-row div[role=row]
{
display: flex;
align-content: stretch;
}
.ui-grid, .ui-grid-viewport
{
height: auto !important;
}
.ui-grid-viewport, .ui-grid-canvas
{
height: auto !important;
}

Related

Filling a chart with a color above a specific Y value

I've been trying to fill a chart with color above certain Y value and I can't do it.
What I tried to do is writing a condition that if the value is >3.5 that area of the chart will be filled with a different color.
splineSeries.fill(function() {
if (this.value > 3.5)
return '#d3f335 0.4'
else
return '#cdf0a7 0.6'})
However, this doesn't work for me as it fills the whole area of the chart which values are >3.5 and not only the area that it's above the line.
This is how that chunk of code is working in my chart.1
If you know how this can be solved I would really appreciate if you help me :)
Thanks!!
It does not seem AnyChart supports this kind of filling out of the box. You can however get creative with gradients:
var cmin = chart.getStat("yScalesMin");
var cmax = splineSeries.getStat('seriesMax');
var cutoff = 3.5;
splineSeries.fill({
angle: 90,
keys: [{
color: '#cdf0a7',
opacity: 0.6,
offset: (cutoff-cmin) / (cmax-cmin)
}, {
color: '#d3f335',
opacity: 0.4,
offset: (cutoff-cmin) / (cmax-cmin)
}],
thickness: 3
});
Here's a working example:
chart = anychart.area();
var series = chart.area(generateRandomData());
var cmin = chart.getStat("yScalesMin");
var cmax = series.getStat('seriesMax');
var cutoff = 3.5;
series.fill({
angle: 90,
keys: [{
color: '#cdf0a7',
opacity: 0.6,
offset: (cutoff-cmin) / (cmax-cmin)
}, {
color: '#d3f335',
opacity: 0.4,
offset: (cutoff-cmin) / (cmax-cmin)
}],
thickness: 3
});
chart.container("chart");
chart.draw();
function generateRandomData() {
var data = [];
for (let i=0; i<16; i++) {
data.push({x:i, value:Math.random() * 7.5});
}
return data;
}
#chart {
height: 400px;
}
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-cartesian.min.js"></script>
<div id="chart"></div>
This should also work with charts with yScalesMin > 0

Prevent JointJS elements from generating off paper

I'm using JointJS in a React environment to create a Directed graph from some Neo4j data. My problem is that elements are being generated off the paper As pictured here, "test6" is generated mostly off the page and "test10" isn't even shown. I would like all elements to be displayed on the paper, without overlapping each other or links if possible.
My paper is defined with only a width dimension set equal to the div width and the div is styled to be 100% width
...
width: $('#paper').width(),
...
and
render(){
return(
<React.Fragment>
<div id="paper" style={{width:'100%'}}></div>
</React.Fragment>
)
}
The code for generating an element is as follows:
function makeElement(node) {
var maxLineLength = _.max(node.name.split('\n'), function(l) { return l.length; }).length;
var letterSize = 12;
var width = 2 * (letterSize * (0.6 * maxLineLength + 1));
var height = 2 * ((node.name.split('\n').length + 1) * letterSize);
return new joint.shapes.basic.Rect({
id: node.id,
size: { width: 100, height: height },
attrs: {
type:'node',
text: {
text: node.name,
'font-size': letterSize,
'font-family': 'monospace' },
rect: {
width: width, height: height,
rx: 5, ry: 5,
stroke: '#555'
}
}
});
}
Thanks in advance :)
EDIT: I don't have the exact solution yet, but in the meantime I used this to make the paper draggable to view all nodes

How to group rows on range when cell value is number in ag-grid-react?

I am trying to group rows according to range of numbers like < 50, 50-69,70-100, 100+ but rows are grouped according to value in a cell in each row.
I have a custom filter for the column on which i am enabling rowGroup, I have a cell renderer which renders the cell style according to the value. the cell renderer return a value or html string which is rendered in grid cell. when you enable row group the same cell renderer return the the ranges i.e <50 , 50-69, 70-100, 100+, But group count is not according to these range instead it is grouped on value of the cell.
field Definition:
{
headerName:"PH Index",
field: "botScore",
width: 120,
cellStyle: { textAlign : 'center', padding: '0px 0px' },
cellRenderer: this.scoreRenderer,
filter: "scoreFilter",
enableRowGroup: true,
enablePivot: true,
menuTabs : ['filterMenuTab', 'generalMenuTab', 'columnsMenuTab'],
},`
CellRenderer:
scoreRenderer(params){
let progress;
if(typeof(params.value)== "number"){
progress = Math.round((params.value/150)*100);
if(params.value===0){
return '<div style="font-size: 13px; font-weight: 600; background: #ffffff color: #000000">'+params.value+'</div>';
}
else if(params.value<50 && params.value>0){
return '<div style="font-size: 13px; font-weight: 600; background: linear-gradient(to right, red '+progress+'%, transparent '+progress+'%); color: #000">'+params.value+'</div>';
}
else if(params.value>49 && params.value<70){
return '<div style="font-size: 13px; font-weight: 600; background: linear-gradient(to right, orange '+progress+'%, transparent '+progress+'%); color: '+(progress > 55? "#fff":"#000")+'">'+params.value+'</div>';
}
else if(params.value>69 && params.value<100){
return '<div style="font-size: 13px; font-weight: 600; background: linear-gradient(to right, #53bd9c '+progress+'%, transparent '+progress+'%); color: '+(progress > 55? "#fff":"#000")+'">'+params.value+'</div>';
}
else if(params.value>100){
return '<div style="background: linear-gradient(to right, green '+progress+'%, transparent '+progress+'%); font-size: 13px; font-weight: 600; color: #fff">'+params.value+'</div>';
}
}
else{
let value = parseFloat(params.value);
if(value<50){
return '< 50';
}
else if(value>49 && value<70){
return '50 - 69';
}
else if(value>69 && value<101){
return '70 - 100';
}
else if(value>100)
return '100+';
}
}
AgGridReact component use:
`
<AgGridReact
enableFilter = { true }
enableSorting = { true }
enableColResize = { true }
onGridReady = { this.onGridReady.bind(this) }
onFilterChanged = {this.filterChanged.bind(this)}
frameworkComponents = {this.state.frameworkComponents}
columnDefs = { this.state.columnDefs }
rowData = { this.state.rowDefs }
/>`
Ex.
row1 - value 11
row2 - value 30
row3 - value 55
row4 - value 110
row5 - value 135
So if you group these rows according to range mentioned above you should get this
<50(2),
50-100(1),
100+(2)
but i am getting this output
<50(1)
<50(1)
50-100(1)
100+(1)
100+(1)

Angular ui-grid dynamically calculate height of the grid

I am using : https://github.com/angular-ui/ui-grid.info/tree/gh-pages/release/3.0.0-RC.18
<div ui-grid="gridOptions" style="height:765px"></div>
When I hard code the value, as shown above, the grid spreads out and everything works as expected.
However, if I do the following...
$scope.gridStyle = 'height:'+numRows*rowHeight+'px' //(765px);
<div ui-grid="gridOptions" style="{{gridStyle}}"></div>
The height is printed in the div and div widens but the content itself widens to only around 340px. The space that is left is blank, so instead of 25 rows I see only 8. I have to scroll down, while there is a whole 400px free in the grid. The ui-grid-viewport and ui-grid-canvas are both not using this space...
Why can't the ui-grid-viewport use that space?
I use ui-grid - v3.0.0-rc.20 because a scrolling issue is fixed when you go full height of container. Use the ui.grid.autoResize module will dynamically auto resize the grid to fit your data. To calculate the height of your grid use the function below. The ui-if is optional to wait until your data is set before rendering.
angular.module('app',['ui.grid','ui.grid.autoResize']).controller('AppController', ['uiGridConstants', function(uiGridConstants) {
...
$scope.gridData = {
rowHeight: 30, // set row height, this is default size
...
};
...
$scope.getTableHeight = function() {
var rowHeight = 30; // your row height
var headerHeight = 30; // your header height
return {
height: ($scope.gridData.data.length * rowHeight + headerHeight) + "px"
};
};
...
<div ui-if="gridData.data.length>0" id="grid1" ui-grid="gridData" class="grid" ui-grid-auto-resize ng-style="getTableHeight()"></div>
A simpler approach is set use css combined with setting the minRowsToShow and virtualizationThreshold value dynamically.
In stylesheet:
.ui-grid, .ui-grid-viewport {
height: auto !important;
}
In code, call the below function every time you change your data in gridOptions. maxRowToShow is the value you pre-defined, for my use case, I set it to 25.
ES5:
setMinRowsToShow(){
//if data length is smaller, we shrink. otherwise we can do pagination.
$scope.gridOptions.minRowsToShow = Math.min($scope.gridOptions.data.length, $scope.maxRowToShow);
$scope.gridOptions.virtualizationThreshold = $scope.gridOptions.minRowsToShow ;
}
.ui-grid, .ui-grid-viewport,.ui-grid-contents-wrapper, .ui-grid-canvas {
height: auto !important;
}
UPDATE:
The HTML was requested so I've pasted it below.
<div ui-grid="gridOptions" class="my-grid"></div>
ORIGINAL:
We were able to adequately solve this problem by using responsive CSS (#media) that sets the height and width based on screen real estate. Something like (and clearly you can add more based on your needs):
#media (min-width: 1024px) {
.my-grid {
width: 772px;
}
}
#media (min-width: 1280px) {
.my-grid {
width: 972px;
}
}
#media (min-height: 768px) {
.my-grid {
height: 480px;
}
}
#media (min-height: 900px) {
.my-grid {
height: 615px;
}
}
The best part about this solution is that we need no resize event handling to monitor for grid size changes. It just works.
I like Tony approach. It works, but I decided to implement in different way. Here my comments:
1) I did some tests and when using ng-style, Angular evaluates ng-style content, I mean getTableHeight() function more than once. I put a breakpoint into getTableHeight() function to analyze this.
By the way, ui-if was removed. Now you have ng-if build-in.
2) I prefer to write a service like this:
angular.module('angularStart.services').factory('uiGridService', function ($http, $rootScope) {
var factory = {};
factory.getGridHeight = function(gridOptions) {
var length = gridOptions.data.length;
var rowHeight = 30; // your row height
var headerHeight = 40; // your header height
var filterHeight = 40; // your filter height
return length * rowHeight + headerHeight + filterHeight + "px";
}
factory.removeUnit = function(value, unit) {
return value.replace(unit, '');
}
return factory;
});
And then in the controller write the following:
angular.module('app',['ui.grid']).controller('AppController', ['uiGridConstants', function(uiGridConstants) {
...
// Execute this when you have $scope.gridData loaded...
$scope.gridHeight = uiGridService.getGridHeight($scope.gridData);
And at the HTML file:
<div id="grid1" ui-grid="gridData" class="grid" ui-grid-auto-resize style="height: {{gridHeight}}"></div>
When angular applies the style, it only has to look in the $scope.gridHeight variable and not to evaluate a complete function.
3) If you want to calculate dynamically the height of an expandable grid, it is more complicated. In this case, you can set expandableRowHeight property. This fixes the reserved height for each subgrid.
$scope.gridData = {
enableSorting: true,
multiSelect: false,
enableRowSelection: true,
showFooter: false,
enableFiltering: true,
enableSelectAll: false,
enableRowHeaderSelection: false,
enableGridMenu: true,
noUnselect: true,
expandableRowTemplate: 'subGrid.html',
expandableRowHeight: 380, // 10 rows * 30px + 40px (header) + 40px (filters)
onRegisterApi: function(gridApi) {
gridApi.expandable.on.rowExpandedStateChanged($scope, function(row){
var height = parseInt(uiGridService.removeUnit($scope.jdeNewUserConflictsGridHeight,'px'));
var changedRowHeight = parseInt(uiGridService.getGridHeight(row.entity.subGridNewUserConflictsGrid, true));
if (row.isExpanded)
{
height += changedRowHeight;
}
else
{
height -= changedRowHeight;
}
$scope.jdeNewUserConflictsGridHeight = height + 'px';
});
},
columnDefs : [
{ field: 'GridField1', name: 'GridField1', enableFiltering: true }
]
}
tony's approach does work for me but when do a console.log, the function getTableHeight get called too many time(sort, menu click...)
I modify it so the height is recalculated only when i add/remove rows. Note: tableData is the array of rows
$scope.getTableHeight = function() {
var rowHeight = 30; // your row height
var headerHeight = 30; // your header height
return {
height: ($scope.gridData.data.length * rowHeight + headerHeight) + "px"
};
};
$scope.$watchCollection('tableData', function (newValue, oldValue) {
angular.element(element[0].querySelector('.grid')).css($scope.getTableHeight());
});
Html
<div id="grid1" ui-grid="gridData" class="grid" ui-grid-auto-resize"></div>
I am late to the game but I found a nice solution. I created a custom attribute directive all you need to do is pass in the gridApi and it will automatically calculate the height. It also subscribes to the pagination change event so if the user changes page size it will resize.
class UIGridAutoResize implements ng.IDirective {
link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => void;
scope: { gridApi: "=" };
restrict = "A";
private previousValue: string;
private isValid: boolean = true;
private watch: any;
constructor($timeout: ng.ITimeoutService) {
UIGridAutoResize.prototype.link = (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
const gridOptions = scope.$eval(attrs.uiGrid) as any;
const gridApi = scope.$eval(attrs.gridResize) as any;
gridApi.core.on.rowsRendered(scope, () => {
$timeout(() => {
this.autoSizeGrid(element, attrs, gridOptions, gridApi, false);
}, 100);
});
gridApi.core.on.filterChanged(scope, () => {
this.autoSizeGrid(element, attrs, gridOptions, gridApi, false);
});
if (attrs.uiGridPagination === "") {
gridApi.pagination.on.paginationChanged(null, () => {
this.autoSizeGrid(element, attrs, gridOptions, gridApi, true);
});
}
angular.element(window).resize(() => {
$timeout(() => {
this.autoSizeGrid(element, attrs, gridOptions, gridApi, false);
}, 100);
});
};
}
static Factory(): ng.IDirectiveFactory {
const directive = ($timeout: ng.ITimeoutService) => {
return new UIGridAutoResize($timeout);
};
directive["$inject"] = ["$timeout"];
return directive;
}
private autoSizeGrid(element: ng.IAugmentedJQuery, attrs: ng.IAttributes, gridOptions: any, gridApi: any, isPaginationChanged: boolean) {
gridApi.core.handleWindowResize();
// Clear empty grid message
angular.element(element.parent()).find("#emptyGridMessage").remove();
element.find(".ui-grid-viewport").css("display", "");
if (attrs.hidePageSize === "") {
element.find(".ui-grid-pager-row-count-picker").css("display", "none");
}
let rowCount = gridApi.core.getVisibleRows().length;
const headerElements = element.find(".ui-grid-header");
let headerHeight = 2;
if (headerElements.length > 1) { // If we have more than one header element the grid is using grouping
const headerElement = angular.element(headerElements[1]);
headerHeight += headerElement.height();
} else {
headerHeight += headerElements.height();
}
if (attrs.uiGridPagination === "") {
if (rowCount < 1) {
gridOptions.enablePagination = false;
gridOptions.enablePaginationControls = false;
element.css("height", (rowCount * 30) + headerHeight - 2);
element.find(".ui-grid-viewport").css("display", "none");
angular.element("<div id='emptyGridMessage' style='font-size: 1em; width: 100%; background-color: white; border: 1px solid #d4d4d4; padding: 7px 12px; color: #707070;'><span style='opacity: 0.95;'>There are no records.</span></div>").insertAfter(element);
} else if (gridApi.core.getVisibleRows().length < gridOptions.paginationPageSize && !isPaginationChanged) {
gridOptions.enablePagination = false;
gridOptions.enablePaginationControls = false;
element.css("height", (rowCount * 30) + headerHeight);
} else {
gridOptions.enablePagination = true;
gridOptions.enablePaginationControls = true;
element.css("height", (rowCount * 30) + headerHeight);
}
} else {
if (rowCount < 1) {
element.css("height", (rowCount * 30) + headerHeight - 2);
element.find(".ui-grid-viewport").css("display", "none");
angular.element("<div id='emptyGridMessage' style='font-size: 1em; width: 100%; background-color: white; border: 1px solid #d4d4d4; padding: 7px 12px; color: #707070;'><span style='opacity: 0.95;'>There are no records.</span></div>").insertAfter(element);
} else {
element.css("height", (rowCount * 30) + headerHeight);
}
}
// Add extra margin to prevent scroll bar and pager from overlapping content underneath
const pagerHeight = element.find(".ui-grid-pager-panel").height();
if (rowCount > 0) {
if (pagerHeight > 0)
element.css("margin-bottom", pagerHeight);
else
element.css("margin-bottom", 10);
} else {
if (pagerHeight > 0)
angular.element(element.parent()).find("#emptyGridMessage").css("margin-bottom", pagerHeight);
else
angular.element(element.parent()).find("#emptyGridMessage").css("margin-bottom", 10);
}
if (rowCount > gridOptions.paginationPageSize) // Sometimes paging shows all rows this fixes that
gridApi.core.refresh();
}
}
<div ui-grid="vm.gridOptions" grid-resize="vm.gridApi" ui-grid-resize-columns ui-grid-pagination></div>
following #tony's approach, changed the getTableHeight() function to
<div id="grid1" ui-grid="$ctrl.gridOptions" class="grid" ui-grid-auto-resize style="{{$ctrl.getTableHeight()}}"></div>
getTableHeight() {
var offsetValue = 365;
return "height: " + parseInt(window.innerHeight - offsetValue ) + "px!important";
}
the grid would have a dynamic height with regards to window height as well.

how to Fit row height to content in ng-grid?

Any idea how to Fit row height to content in ng-grid? See that the text is wrapped but not shown as the row has a fix height.
I saw several posts claiming there is no support for that... thought maybe someone can enlighten me...
Thanks.
Here is my code:
this.gridOptions = { data: 'tenants.elements',
selectedItems: this.mySelections,
enableColumnResize: true,
enableColumnReordering: true,
enableRowReordering: true,
showSelectionCheckbox: true,
selectWithCheckboxOnly: true,
showFooter: true,
//sortInfo: {fields: ['percent'], directions: ['desc']},
columnDefs:
[
{width: '7%', field:'apartment', displayName:'דירה'},
{width: '2%', field:'tenant_type', displayName:'-', cellTemplate: '<i ng-class="tenants.getTenantType(row.entity.tenant_type)" class="fa fa-child"></i>'},
{width: '2%', field:'defecto', displayName:'-', cellTemplate: '<i ng-show="row.entity.defecto" class="fa fa-child defecto"></i>'},
{width: '30%', field:'tenant_name', displayName:'שם דייר', cellTemplate: '{{row.entity.tenant_name}}'},
{width: '25%' ,field:'phones', displayName:'טלפונים'},
{width: '25%' ,field:'mails', displayName:'מיילים'}
],
filterOptions: this.filterOptions,
plugins: [new ngGridAutoRowHeightPlugin()]
//plugins: [new ngGridFlexibleHeightPlugin()]
}
Here is a plugin, some code taken from the ngGridFlexibleHeightPlugin
Be warned, that height will be changed on all rows by maximum height
ngGridAutoRowHeightPlugin = function () {
var self = this;
self.grid = null;
self.scope = null;
self.init = function (scope, grid, services) {
self.domUtilityService = services.DomUtilityService;
self.grid = grid;
self.scope = scope;
var recalcHeightForData = function () { setTimeout(innerRecalcForData, 1); };
var innerRecalcForData = function () {
var gridId = self.grid.gridId,
rowHeight = self.grid.config.rowHeight;
$('.' + gridId + ' .ngRow [ng-cell-text]').each(function (index, cellText) {
//+10 for default top and bottom padding of .ngCellText class
rowHeight = Math.max(rowHeight, $(cellText).outerHeight() + 10);
});
if (self.grid.config.rowHeight < rowHeight) {
self.grid.config.rowHeight = rowHeight;
//update grid's scope.rowHeight as vertical bars height depends on it
if (scope.$$phase == '$apply' || scope.$$phase == '$digest') {
updateGridScopeHeight();
} else {
scope.$apply(updateGridScopeHeight);
}
self.domUtilityService.RebuildGrid(self.scope, self.grid);
function updateGridScopeHeight() {
self.grid.$root.scope().rowHeight = rowHeight;
}
}
};
self.scope.catHashKeys = function () {
var hash = '',
idx;
for (idx in self.scope.renderedRows) {
hash += self.scope.renderedRows[idx].$$hashKey;
}
return hash;
};
self.scope.$watch('catHashKeys()', innerRecalcForData);
self.scope.$watch(self.grid.config.data, recalcHeightForData);
};
};
And also, add this style rule to your css (after ng-grid css)
.ngCellText {
white-space: normal !important;
}
Plunker here

Resources