I've been trying to use the ng-grid 3.0 (ui-grid). I have managed to populate my grid and it's been very responsive and it's features are really amazing. But I'm trying to customize my column headers, as I need more info there.
I can create a custom header cell template, as indicated in the docs, but I don't seem able to use a Bootstrap Dropdown there, it gets cropped and I can't use it at all. Some googling got me thinking it is probably some issue with the overflow attributes, but still I can't solve it. My grid options is as follows:
$scope.columnDefs = [
{ name:'name', displayName: 'Vdd', headerCellTemplate: 'headerTemplate.html' },
{ name:'gender', headerCellTemplate: 'headerTemplate.html' },
{ name:'company' }
]
$scope.gridOptions = {
columnDefs: $scope.columnDefs,
rowTemplate: 'rowTemplate.html',
data: 'data'
};
I have forked an example in plunkr and managed to reproduce my issue:
http://plnkr.co/edit/qdrFiifiz18fxB8w6Aja?p=preview
I want to replace the built-in dropdown menu (since it doesn't seem to allow nesting and sub-menus) and add another one (so in the end, I'd have two dropdown menus in each header cell)
Any help is appreciated =)
I am proud to say I think I've figured it out. I've been digging through ui-grid's source code and narrowed it down to this block (lines: 2847 - 2852).
function syncHorizontalHeader(scrollEvent){
var newScrollLeft = scrollEvent.getNewScrollLeft(colContainer, containerCtrl.viewport);
if (containerCtrl.headerViewport) {
containerCtrl.headerViewport.scrollLeft = gridUtil.denormalizeScrollLeft(containerCtrl.viewport,newScrollLeft, grid);
}
}
I noticed that containerCtrl.headerViewport.scrollLeft was never getting set to newScrollLeft. A quick google search led me to this StackOverflow thread which says that you can't set the scrollLeft property of an element if it's overflow is set to visible.
My solution was to replace containerCtrl.headerViewport.scrollLeft = gridUtil.denormalizeScrollLeft(containerCtrl.viewport,newScrollLeft, grid); with containerCtrl.headerViewport.style.marginLeft = -gridUtil.denormalizeScrollLeft(containerCtrl.viewport,newScrollLeft, grid) + 'px'; which just sets a negative margin on the header. Then add an overflow:hidden; style to .ui-grid-render-container-body to hide headers that extend beyond the main grid viewport.
Doing this messed up the placement of column menus, but there is an easy fix. On line 514 replace var containerScrollLeft = renderContainerElm.querySelectorAll('.ui-grid-viewport')[0].scrollLeft; with var containerScrollLeft = renderContainerElm.querySelectorAll('.ui-grid-viewport')[0].style.marginLeft; to use the margin instead of the scroll value in the menu placement calculation.
Related
I use kirjs/react-highcharts plugin for react but I have no idea how to get Highchart component in order to call reflow like: $('#container1').highcharts().reflow()
From docs its tells:
For access to methods & properties from the Highcharts library you can use ReactHighcharts.Highcharts. For example, the Highcharts options are available via ReactHighcharts.Highcharts.getOptions().
but I don't see there any reflow() method.
Any ideas?
Edit
its located under ReactHighcharts.Highcharts.charts so I can call
ReactHighcharts.Highcharts.charts[0].reflow()
but it has list of all highchart instances so I have no information what index is mine
I am using highcharts-react-official and highcharts. They are available via npm . Although I am using different packages, the working principle should be the same.
The way of using reflow() function in react-highchart is also Highcharts.charts[i].reflow(). I have prepared 2 code sandboxes for the demonstration. The first code sandbox shows the way of re-flowing every chart, and the second code sandbox shows the way of re-flowing a particular chart.
The first code sandbox link is here.
There is a button on App.js to hide and show highchart divs.
The child component is React.memo (for performance optimization), therefore the hidden highchart will not be auto resized after it becomes visible.
To let every chart window resize with respect to the window size. The following code is placed in App.js. It looks for all existing charts and re-flow them:
for (var i = 0; i < Highcharts.charts.length; i++) {
if (Highcharts.charts[i] !== undefined) {
Highcharts.charts[i].reflow();
}
}
The second code sandbox link is here.
Almost the same as the first code sandbox, but there is a ref array of referencing every chart.
The chart which needs re-flowing is placed with an id in its chart option.
Iterate every chart and get the index of the chart with that id e.g. "secondChart" for this example.
for (var i = 0; i < chartRef.current.length; i++) {
if (
chartRef.current[i].chart.userOptions.id !== undefined &&
chartRef.current[i].chart.userOptions.id === "secondChart"
)
Highcharts.charts[chartRef.current[i].chart.index].reflow();
}
Re-flow that chart.
The Reflow can be set with event load in options.
Workaround: You have to set reflow() using setTimeout() this will trigger the resizing. Time is set to 0 to avoid delay.
Solution
const options = {
chart: {
type: 'column',
style: {},
events: {
load() {
setTimeout(this.reflow.bind(this), 0);
},
},
},
title: 'Column Chart',
}
Usage
<HighchartsReact highcharts={Highcharts} options={options} />
ui-grid's expandable row feature works great if every parent row has the same number of subrows in its subGrid, and therefore the same height. Not so great if the number of subrows in the subGrid varies from row-to-row of the parent.
I've been able to get the subGrid ITSELF to have a dynamic height by passing in the array length and using that to generate a subGrid-height:
lengthOfList: data[i].friends.length
style="height:{{row.entity.subGridOptions.lengthOfList * 30}}px;
But since that subGrid has to live WITHIN the parent grid, the parent grid needs to push its rows down to make room. And it needs to do so by a different amonut for each row. Unfortunately the parent grid has a single, fixed expandableRowHeight for ALL rows. I can't specify the expandableRowHeight for EACH row.
Best I can hope for is to alter the expandableRowHeight on-the-fly, as I click on a row to open it.
Trying to figure out how to listen for the open expandable row event.
ui-grid is a shrink-wrapped plugin that writes it own DOM elements, so I have no way of adding anything to the elements that might help me capture events on them.
How can I configure the expandableRowHeight on-the-fly?
Plunker:
http://plnkr.co/edit/1IeEsXZAf9pRgKmy5jfO?p=preview
(The plunker is misbehaving. 1] A font that ui-grid is using is now blocked due to cross-domain issues, 2] sometimes the html template goes AWOL for no discernible reason.)
Ah. I found the gridApi. So this solution works!
$scope.gridOptions = {
onRegisterApi: function(gridApi) {
gridApi.expandable.on.rowExpandedStateChanged($scope,function(row){
$scope.gridOptions.expandableRowHeight = row.entity.mySubRowItems.length * 30;
});
}
}
I'm sure there's a way of referring to the row's parent grid directly. Instead of $scope.gridOptions, there's surely some row $parent reference, I just haven't found it yet.
As an update for 3.0.0 stable there is a rowExpandedBeforeStateChanged event. If you use rowExpandedStateChanged then your grid won't update w/ the new expandableRowHeight you're trying to set.
$scope.gridOptions.onRegisterApi = function(gridApi) {
// dynamic expandable rows
gridApi.expandable.on.rowExpandedBeforeStateChanged($scope, function(row) {
$scope.gridOptions.expandableRowHeight = 30 + row.entity.valueArray.length * 30;
});
};
Finally got this sorted, the important thing to know is that the expandable row has a row height, and a sub (expandable) grid size height inside that row, so if you need to display the grid to fit exactly inside the row, you need to set both heights to the same dynamic size.
Inside your top level grid api you need to specify :
$scope.gridOptions = {
onRegisterApi: function(gridApi) {
gridApi.expandable.on.rowExpandedStateChanged($scope, function (row) {
row.expandedRowHeight = (row.entity.subGridOptions.data.length * rowHeight) + headerHeight;
});
});
}
}
but now u use the expandedRowHeight variable from ui-grid version 3.0.0+, not available in 2+, the expandableRowHeight does not seem to have any affect, but might be used to set the global value for expandedRowHeight. (Note the different naming of these 2 variables on the grid)
Now this will size your top Grid row to display your sub grid correctly.
In the expandableRowTemplate.html for the sub grid u need to specify the height dynamically of the sub grid using the css style:
<div ui-grid="row.entity.subGridOptions" style="height:{{(row.entity.subGridOptions.data.length * rowHeight) + headerHeight}}px" ui-grid-auto-resize></div>
this works very well for me currently, and might help someone out there.
Note that it could also help to investigate the ui-grid-autoresize directive now in ui-grid 3+, apparently this helps in accomplishing this, not 100% sure if I used it correctly, but it works
This is how it works, I checked other people's solution, they all not working
Let's assume that the headerRowHeight is 39px.
The template should be something like this :
'<div ui-grid="row.entity.subGridOptions" ng-style="{height: (row.entity.youList.length * row.entity.subGridOptions.rowHeight) + 39 + \'px\'}" ui-grid-selection ui-grid-edit ui-grid-cellnav ui-grid-validate ui-grid-auto-resize ui-grid-resize-columns></div>'
The rowExpandedStateChanged should be this:
gridApi.expandable.on.rowExpandedStateChanged($scope, function (row) {
$timeout(function() {
row.expandedRowHeight = row.entity.youList.length * row.entity.subGridOptions.rowHeight + 39;
}, 150);
});
I tried a different approach to the solution using CSS alone instead of leveraging the "expandableRowHeight" property. And this worked for me.
The parent grid data is rendered inside a div with class name "ui-grid-canvas". Add the following style -
.ui-grid-canvas{
height: auto !important;
overflow: auto !important;
}
For expanded child table, add a style to div with class name "expandableRow"
.expandableRow{
height: auto !important;
}
Also, do not set the "expandableRowHeight" property in gridOptions.
I'm trying to create a DatePicker that slides in over a TableView in order to edit a date-field. The problem is that the DatePicker appears behind the TableView - you can see a part of it because the TableView is transparent.
I tried giving one a higher zIndex value than the other, both ways, but that didn't help. The datepicker is a picker control in a view, which I slide up with an animation.
How does one overlay a TableView with a different view?
Edit: the table is quite complicated because of the various types of data in it, so copy&paste would be overkill. But here's the relevant part:
var win = Ti.UI.createWindow();
var table = Ti.UI.createTableView({zIndex: 1});
// some table sections are added here
// create picker layer
var row = Ti.UI.createTableViewRow({zIndex: 2});
var picker_view = Titanium.UI.createView({
height: 251,
bottom: -251,
zIndex: 3,
visible: false
});
var picker = Ti.UI.createPicker({
type: Ti.UI.PICKER_TYPE_DATE,
selectionIndicator: true
});
picker_view.add(picker);
row.addEventListener('click', function()
{
picker_view.visible = true;
var slide_in = Titanium.UI.createAnimation({bottom:0});
picker_view.animate(slide_in);
});
row.add(picker_view);
some_section.add(row);
win.add(table);
Thanks!
Try to Add the picker in the window not in table.
dont add the view to the row, add it to the window.
dont set visibility on the view, it is irrelevant since you are sliding it up and down.
also you really are confusing things with the zIndexes, they are not really needed in this use case.
Suppose I know which row index to target (with this.rowToBeDeleted having a value of 2, say), how can I hide this row only from the grid but not the store (I have a flag in the store, which signifies what rows should be deleted from the db later in my PHP webservice code).
You can either use one of the store.filter() methods or you can hide the row element.
grid.getView().getRow(rowIndex).style.display = 'none';
I think it's much better though to just remove the record from the store and let the store update the view since you are deleting the record and not just hiding it. With the store in batch mode (the default: batch: true, restful: false), it will remember which rows you've removed and won't fire a request to the server until you call store.save().
I suggest using store.FilterBy() and pass a function to test the value of the value in rowToBedeleted:
store.filterBy(function(record) {
return record.get("rowToBeDeleted") != 2;
});
I wrote a basic blogpost about gridfiltering a while ago, you can read it here: http://aboutfrontend.com/extjs/extjs-grid-filter/
In ExtJS 4.1, there is no view.getRow(..). Instead you can use:
this.view.addRowCls(index, 'hidden');
to hide the row at the specified index, and
this.view.removeRowCls(index, 'hidden');
to show it (where 'this' is the grid).
CSS class hidden is defined as
.hidden,
{
display: none;
}
This is useful for peculiar scenarious where store.filterBy() is not appropriate.
In the grid js file write following code to apply a CSS to those rows which you want to hide.
<pre><code>
Ext.define('MyGrid',{
extend : 'Ext.grid.Panel',
xtype : ''mygrid',
viewConfig : {
getRowClass : function(record,id){
if(record.get('rowToBeDeleted') == 2){
return 'hide-row';
}
}
},
.................
.................
});
</code></pre>
Now define a custom CSS in custom.css file:
.hide-row{display:none}
This will hide rows in grid without removing or filtering from store.
You can use the store.filter() or store.filterBy() methods for that.
Set a "hidden" property on your records and the filter all records that have hidden set to true for example. This way they'll still be present in the store but not visible in the grid.
I have a combobox, and based on the current selection, I would like dynamic text to appear beside the combobox.
My current solution works, but seems kludgy and fragile. It may not work at all depending on where the combobox appears in the DOM.
Here is the crux of my current solution (called when dropdown changes):
var child = owner.el.first().next().first().first().first().next().first();
if (child.dom.childNodes.length == 3) {
child.createChild({
tag: 'span',
html: c + Ext.id()
});
} else {
child.last().replaceWith({
tag: 'span',
html: c + Ext.id()
});
}
I'm mostly concerned about the first line...this can't be a good way of finding an insertion point.
Here is a pic of the combo box with the dynamic text appearing beside:
And here is what I looked at to find where I wanted to insert the text:
Can someone suggest a better way of achieving this effect? Thanks.
Have the span in the page already and grab it by its id.
Ext.get('combo-suffix').insertHTML(theText);
Or, grab the combo box element by its id and get/add its sibling.
Will it always be in a div called "ext-gen82"?
If so, why not use the jquery find method?
$("#ext-gen82").find("ext-gen107");
Alternatively you could give them their own class to help you find them as well:
$(".parentClass").find(".spanElement");
You could also use the children method:
$("#ext-gen82").children("span");
#OrangeDog pointed me the right direction, but I'll post a more detailed answer here for completeness/future reference.
var inserted = false; // need to set this up initially
...
var pos = Ext.getCmp('combo-element-id').el.next('.x-form-trigger');
if (inserted) {
pos.next().update(c + Ext.id());
} else {
inserted = true;
pos.insertSibling({
tag: 'span',
html: c + Ext.id()
}, 'after');
}