I'm trying to add a nested grid inside an existing grid. I'm very new at this, and although I'm able to display the grid, I cannot seem to display the data. When I do console.log(params), I have all my data, but no matter what I try, I cannot display the data. I think the problem is with my getDetailRowData, and what I'm passing to the successCallback function. Any ideas?
this is my component ts file
import { Component, OnInit } from '#angular/core';
import { ReportService } from 'src/app/services/report.service';
import { Report } from 'src/app/models/report';
import { agThemeSgBootstrap } from "#sg-bootstrap/ag-grid";
import {GridOptions, Module} from 'ag-grid-community';
import { DatePipe } from '#angular/common';
#Component({
selector: 'app-report',
templateUrl: './report.component.html',
styleUrls: ['./report.component.scss']
})
export class ReportComponent implements OnInit {
apiUrl: any;
reports : Report[];
gridOptions: GridOptions;
dateValue = new Date();
maxDate = new Date();
private detailCellRendererParams;
private columnDefs;
// public modules: Module[] = AllModules;
constructor(private reportService : ReportService, private datePipe: DatePipe) {
this.columnDefs = [
{headerName: 'Report Name', field: 'reportName', cellRenderer: 'agGroupCellRenderer',
resizable: true,
valueGetter: params => { return `${params.data.reportName}.dat` }},
{headerName: 'Sent to FIS', field: 'sentToFis',resizable: false,
valueGetter: params => { return params.data.sentToFis === true ? "Yes" : "No" } },
{headerName: 'File Size', field: 'contentLength', resizable: true,
valueGetter: params => { return `${this.formatBytes(params.data.contentLength)}` }},
{headerName: 'Last Modified', field: 'lastModified', resizable: true,
valueGetter: params => { return this.datePipe.transform(params.data.lastModified, "yyyy-MM-dd HH:mm:ss") }},
];
this.detailCellRendererParams = {
detailGridOptions: {
columnDefs: [
{headerName: 'ADSL Path', field: 'adslPath', resizable: true,
valueGetter: params => { return params.data.adlsFullPath}},
{headerName: 'Version', field: 'version', resizable: true,
valueGetter: params => { return params.data.sentToFis}},
{headerName: 'Source', field: 'source', resizable: true,
valueGetter: params => { return params.data.adlsFullPath}},
],
},
getDetailRowData: function(params) {
params.successCallback(params.data.children);
console.log(params.data);
}
}
}
ngOnInit() {
this.callReportService(new Date())
}
reportDateChange(value: Date) {
let currentValue = this.datePipe.transform(this.dateValue, "yyyyMMdd")
let newSelectedValue = this.datePipe.transform(value, "yyyyMMdd")
if (currentValue != newSelectedValue) {
if (this.gridOptions.api) {
this.gridOptions.api.showLoadingOverlay();
}
this.callReportService(value)
this.dateValue = value;
}
}
callReportService(value: Date) {
this.reportService.getReportsForDate(value).subscribe(x=> {
this.reports = x;
this.gridOptions.api.sizeColumnsToFit();
})
}
ngAfterContentInit() {
let agOpt = { ...{
animateRows: true,
enableRangeSelection: true,
defaultColDef: {
editable: false,
enableValue: false,
enableRowGroup: false,
enablePivot: true,
filter: true,
sortable: true
},
statusBar: {
statusPanels: [{
statusPanel: 'agTotalRowCountComponent',
align: 'left'
},
{
statusPanel: 'agFilteredRowCountComponent'
},
{
statusPanel: 'agSelectedRowCountComponent'
},
{
statusPanel: 'agAggregationComponent'
},
],
}
}, ...agThemeSgBootstrap }
this.gridOptions = { ...this.gridOptions, ...agOpt }
}
onGridReady(params) {
params.api.setDomLayout("autoHeight");
params.api.sizeColumnsToFit();
}
onGridSizeChanged(params) {
params.api.sizeColumnsToFit();
}
ngAfterViewInit() {
this.dateValue = new Date()
}
formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
my html file
<div class="container-fluid">
<div class="col-xs-12 col-12 col-md-3 form-group row">
<label for="reportDate" class="col-form-label">Report Date:</label>
<div class="col-sm-4 col-md-7">
<input id="reportDate"
class="form-control"
#dp="bsDatepicker"
(bsValueChange)="reportDateChange($event)"
[maxDate]="maxDate"
bsDatepicker
[bsValue]="dateValue" [bsConfig]="{ isAnimated: true, adaptivePosition: true, dateInputFormat: 'YYYY-MM-DD', containerClass: 'theme-red' }"/>
</div>
</div>
<div class="row">
<div class="col">
<ag-grid-angular
style="width: 100%; height: 300px;"
class="ag-theme-sg-bootstrap"
[gridOptions]="gridOptions"
[masterDetail]="true"
[detailCellRendererParams]="detailCellRendererParams"
[rowData]="reports"
[columnDefs]="columnDefs"
(gridReady)="onGridReady($event)"
(gridSizeChanged)="onGridSizeChanged($event)">
</ag-grid-angular>
</div>
</div>
</div>
Related
I have created two custom components, i.e custom tooltip component and custom datepicker component. when im trying to declare in framework it is not working.
Its taking only datepicker custom component not tooltip component. If datepicker component is removed then its taking tooltip component Its considering only one custom component in frameworkComponent.
Please find the below code:
CustomTooltip :
import {Component, ViewEncapsulation} from '#angular/core';
import {ITooltipAngularComp} from "ag-grid-angular";
#Component({
selector: 'tooltip-component',
template: `
<div class="custom-tooltip" [style.background-color]="data.color">
<p>{{tooltipData}}</p>
</div>`,
styles: [
`
:host {
position: absolute;
width: 250px;
height: 60px;
border: 1px solid cornflowerblue;
overflow: hidden;
pointer-events: none;
transition: opacity 1s;
}
:host.ag-tooltip-hiding {
opacity: 0;
}
.custom-tooltip p {
margin: 5px;
white-space: nowrap;
}
`
],
styleUrls: ['./custom-tooltip.component.scss']
})
export class CustomTooltip implements ITooltipAngularComp {
params: any;
data: any;
tooltipData: any;
agInit(params): void {
console.log("params",params.value);
this.params = params;
this.tooltipData=params.value;
this.data = params.api.getRowNode(params.rowIndex).data;
this.data.color = this.params.color || 'white';
}
}
CustomDateComponent:
import {Component, ElementRef, ViewChild} from '#angular/core';
import flatpickr from 'flatpickr'
#Component({
selector: 'app-loading-overlay',
template: `
<div #flatpickrEl class="ag-input-text-wrapper custom-date-filter fa">
<input type='text' data-input />
<a class='input-button' title='clear' data-clear>
<i class='fa fa-times'></i>
</a>
<a class="input-button" title="toggle" data-toggle>
<i class="fa fa-calendar"></i>
</a>
</div>
`,
styles: [
`
.custom-date-filter a {
position: relative;
right: 34px;
color: rgba(0, 0, 0, 0.54);
cursor: pointer;
}
.custom-date-filter:after {
content: '\f073';
display: block;
font-weight: 400;
font-family: 'Font Awesome 5 Free';
position: relative;
right: 25px;
pointer-events: none;
color: rgba(0, 0, 0, 0.54);
}
`
]
})
export class CustomDateComponent {
#ViewChild("flatpickrEl", {read: ElementRef}) flatpickrEl: ElementRef;
private date: Date;
private params: any;
private picker: any;
agInit(params: any): void {
this.params = params;
}
ngAfterViewInit(): void {
// outputs `I am span`
this.picker = flatpickr(this.flatpickrEl.nativeElement, {
onChange: this.onDateChanged.bind(this),
wrap: true
});
this.picker.calendarContainer.classList.add('ag-custom-component-popup');
}
ngOnDestroy() {
console.log(`Destroying DateComponent`);
}
onDateChanged(selectedDates) {
this.date = selectedDates[0] || null;
this.params.onDateChanged();
}
getDate(): Date {
return this.date;
}
setDate(date: Date): void {
this.date = date || null;
this.picker.setDate(date);
}
}
Im trying to use both custom component in one grid i.e:
this.columnDefs = [
{
headerName: 'Request Number', field: 'request_no', sortable: true, filter: 'agNumberColumnFilter'
},
{
headerName: 'Request Date', field: 'created_at', sortable: true, width: 300,
filter: "agDateColumnFilter",
filterParams: {
comparator: function (filterLocalDateAtMidnight, cellValue) {
var dateAsString = cellValue;
var dateParts = dateAsString.split("/");
var cellDate = new Date(Number(dateParts[2]), Number(dateParts[1]) - 1, Number(dateParts[0]));
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
return 0;
}
if (cellDate < filterLocalDateAtMidnight) {
return -1;
}
if (cellDate > filterLocalDateAtMidnight) {
return 1;
}
}
}
},
{ headerName: 'Requested By', field: 'user_name', sortable: true, filter: 'agTextColumnFilter', tooltipField: 'user_name' },
{
headerName: 'Client', field: 'borrower_name', sortable: true, filter: 'agTextColumnFilter',
tooltipField: 'borrower_name', tooltipComponentParams: { color: "#ececec" }, width: 200
},
{
headerName: 'Debtor', field: 'customer_name', sortable: true, filter: 'agTextColumnFilter',
cellStyle: { color: 'blue', cursor: 'pointer' }, tooltipField: 'customer_name', width: 200
},
{
headerName: 'Current Limit', field: 'current_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{
headerName: 'Requested Limit', field: 'requested_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{
headerName: 'Approved Limit', field: 'approved_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{ headerName: 'Status', field: 'status', sortable: true, filter: 'agTextColumnFilter', width: 120, },
{ headerName: 'Comment', field: 'comment', sortable: true, filter: 'agTextColumnFilter', tooltipField: 'comment', width: 200 },
{
headerName: "",
suppressMenu: true,
suppressSorting: false,
cellClass: 'action-class',
width: 120,
template:
`<i class="fa fa-pencil-square-o" aria-hidden="true" data-action-type="view" pTooltip="Edit Queue" tooltipPosition="top"></i>
<i class="fa fa-info-circle" aria-hidden="true" data-action-type="history" pTooltip="View Comment History" tooltipPosition="top"></i>`
}
];
this.defaultColDef = {
enableValue: true,
sortable: true,
tooltipComponent: "customTooltip",
resizable: true
};
this.frameworkComponents = {
customTooltip: CustomTooltip,
agDateInput: CustomDateComponent };
Seems it's a custom tooltip issue (somewhere inside ag-grid), will try to inform the team and describe the bug.
In your case, you can specify your component (CustomDateComponent) directly via cellEditorFramework rather than cellEditor, without internal declaration in frameworkComponents block.
{
headerName: "Date",
field: "date",
width: 190,
editable:true,
cellEditorFramework:CustomDateComponent
}
Just for info: your CustomDateComponent not yet ready to be used as cellEditor
Here is my scenario,
I am making a onFetchEvents Redux Action call and getting all events and passing it as follows. rowData={this.props.events.data}
Everything works fine. Now I need to apply a filter, and this requires another call to the server.
I set enableServerSideFilter: true, so that when a filter applied, datagrid does not make a call on its own.
I monitor filter changes and call handleFilterChanged. I get the const filterModel = this.gridApi.getFilterModel(); and send it to rest endpoint.
Everything works fine but the datagrid does not remember the filter state.
I tried this.gridApi.setFilterModel(filterModel); but it gets me into infinite loop.
To make the long story short, how can I use filters with Redux so that I have full control also where should I use this.gridApi.setFilterModel()
Let me know if you need more clarification.
Thanks
/* eslint-disable */
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';
import { AgGridReact } from 'ag-grid-react';
import { fetchEvents } from '#mc/duck/actions/AppActions';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
// import './customTheme.scss';
// col resizing pipe is not visibles
class DatagridPage extends Component {
constructor(props) {
super(props);
this.state = {
params: {
filterModel: {},
page: 1,
limit: 10,
},
gridOptions: {
enableServerSideFilter: true,
enableSorting: true,
enableColResize: true,
suppressMenuHide: true,
pagination: true,
animateRows: true,
onFilterChanged: this.handleFilterChanged,
onSortChanged: () => console.log('onSortChanged'),
columnDefs: [
{
headerName: 'id',
field: 'id',
headerClass: 'test',
checkboxSelection: true,
filter: 'agTextColumnFilter',
},
{
headerName: 'name',
field: 'name',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'risk score',
field: 'risk_score',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'urgency',
field: 'urgency',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{ headerName: 'type', field: 'type', headerClass: 'test', filter: 'agTextColumnFilter' },
{
headerName: 'artifacts',
field: 'artifacts',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'created',
field: 'created',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'updated',
field: 'updated',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{ headerName: 'due', field: 'due', headerClass: 'test', filter: 'agTextColumnFilter' },
{
headerName: 'owner',
field: 'owner',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'status',
field: 'status',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'description',
field: 'description',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
],
},
};
}
componentDidMount() {
console.log('componentDidMount');
const { params } = this.state;
this.props.onFetchEvents(params);
}
onGridReady = params => {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
console.log('onGridReady', this.gridApi.getFilterModel());
};
onPageSizeChanged(newPageSize) {
var value = document.getElementById('page-size').value;
const paramsCopy = { ...this.state.params };
paramsCopy.limit = Number(value);
paramsCopy.page = 1;
this.setState({ params: paramsCopy }, () => this.props.onFetchEvents(paramsCopy));
this.gridApi.paginationSetPageSize(Number(value));
}
handleFilterChanged = () => {
console.log('handleFilterChanged');
const filterModel = this.gridApi.getFilterModel();
const paramsCopy = { ...this.state.params, filterModel };
console.group('handleFilterChanged');
console.log('filterModel', filterModel);
console.log('state filterModel', this.state.params.filterModel);
if (!areEqualShallow(filterModel, this.state.params.filterModel)) {
this.gridApi.setFilterModel(filterModel);
this.setState({ params: paramsCopy }, () => this.props.onFetchEvents(paramsCopy));
}
console.groupEnd();
function areEqualShallow(a, b) {
for (var key in a) {
if (!(key in b) || a[key] !== b[key]) {
return false;
}
}
for (var key in b) {
if (!(key in a) || a[key] !== b[key]) {
return false;
}
}
return true;
}
// this.gridApi.setFilterModel(filterModel);
};
render() {
return (
<Fragment>
<div>
Page Size:
<select onChange={this.onPageSizeChanged.bind(this)} id="page-size">
<option value="10">10</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select>
</div>
<div
className="ag-theme-balham-dark"
style={{
height: '500px',
width: '100%',
}}
>
<AgGridReact
rowSelection="multiple"
gridOptions={this.state.gridOptions}
columnDefs={this.state.columnDefs}
rowData={this.props.events.data}
defaultColDef={this.state.defaultColDef}
onGridReady={this.onGridReady}
rowHeight={49}
/>
</div>
</Fragment>
);
}
}
const mapStateToProps = state => ({
events: state.app.events,
});
const mapDispatchToProps = {
onFetchEvents: params => fetchEvents(params),
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(DatagridPage);
In your column definition try setting the newRowsAction to "keep", e.g.
{
headerName: 'name',
field: 'name',
headerClass: 'test',
filter: 'agTextColumnFilter',
filterParams: {
newRowsAction: 'keep'
}
}
Or set it for all via defaultColDef, e.g.
gridOptions: {
defaultColDef: {
filterParams: {
newRowsAction: 'keep'
}
}
}
The "No Row To Show" message is showing in grid, but the grid one row of data.
I have a demoComponent.ts and html file.
please refer my below code
demoComponent.ts
import { GridOptions } from "ag-grid/main";
import { Service } from '../Services/service';
#Component({
.
templateUrl: 'demoComponent.html',
.
})
#Injectable()
export class demoComponent implements OnInit {
GridOptions: GridOptions;
GridColumnDefs: any[];
GridData: any[];
data1 : any[]
constructor(private Service: Servicee) {
}
ngOnInit() {
this.GridOptions = <GridOptions>{
enableFilter: false,
rowSelection: 'multiple',
enableSorting: true,
enableColResize: true,
singleClickEdit: true,
animateRows: true,
headerHeight: 50,
rowClass: 'rowClass'
};
Observable.forkJoin(
this.Service.methed1(this.id),
this.Service.methed1(this.id),
this.Service.methed1(this.id),
this.Service.methed1()
).subscribe(response => {
this.data1 = <any>response[0];
this.data2 = <any>response[1];
this.data3 = <any>response[2];
this.data4 = <any>response[3];
this.GridBind()
});
}
GridBind()
{
this.GridColumnDefs = [
{ headerName: "", field: "id", hide: true },
{ headerName: "Column1", field: "Column1", width: 70, editable: true, tooltipField: "release", headerTooltip: "Release" },
{ headerName: "Column2", field: "Column2", width: 80, editable: true, tooltipField: "Column2", headerTooltip: "Column2",
//cellEditorFramework: EstimatingSourceComponent
cellEditorFramework: EstimatingSourceComponent,
cellEditorParams: {
dropDownValue: this.data1,
}
},
{ headerName: "Column3", field: "Column3", width: 70, editable: true },
{ headerName: "Column4", field: "Column4", width: 70, editable: true },
];
this.GridData = [
{
id: 1, Column1: this.data1.value1, Column2: this.data1.value2, Column3: this.data1.value3, Column4: this.data1.value4
}
];
}
}
Then the demoComponent.html
<ag-grid-angular style="width: 100%; height: 350px;"
id="grid1"
class="ag-fresh"
[rowData]="GridData"
[columnDefs]="GridColumnDefs"
[gridOptions]="GridOptions">
</ag-grid-angular>
on rendering the one row is binded with the grid. but "No Row To Show" is showing.
Please correct my implementation , if it is wrong.
thanks in advance
can I do a conditional template on the first column below?
for example:
If my row has score property and I want to hide the input when my score is above 70?
let columns = [
{ width: 30, suppressSorting: true, suppressMenu: true, template: '<input type="checkbox">' },
{ headerName: "Score", filter: 'number', valueGetter: (params : any) =>
params.data.traces ? (<Alert> params.data.traces[0]).severity : params.data.severity, width:70},
{ headerName: "Behaviour tags" },
{ headerName: "Host", field: "host_name" },
{ headerName: "Group Id", cellRenderer: 'group', width:140 },
{ headerName: "Comments",width:290 }
];
Use cellRenderer property in your column object
let columns = [{ width: 30, suppressSorting: true, suppressMenu: true,
cellRenderer: function (params) {
var display = 'block';
if (params.data.score > 70) {
display = 'none';
}
var html = '<input type="checkbox" style="display: ' + display + '">';
return html;
}
}]
In params.data you have all row data
Ext.define('SApp.view.appCrd.InCarousel', {
extend: 'Ext.view.View',
xtype: 'InCarousel',
id:'InCarouselContent',
requires: ['Ext.data.Store',
'SApp.model.appCrd.InDetailModel',
'SApp.model.appCrd.InMeasureModel',
],
tpl: [
'<tpl for=".">',
'<div class="item thumb-wrap">',
'<div class="thumb" style="width:180px; height:180px;">',
'<div ><span class="thumb-title-home-page {class}"><span>{InName}</span></span></div>',
'<tpl for="measureData">',
'<div class="thumb-description" ><span class="thumb-description-name">{key}</span> <span class="thumb-description-value">{value}</span></div>',
'</tpl>',
'</div>',
'</div>',
'</tpl>'
],
itemSelector: 'div.thumb-wrap',
multiSelect: true,
listeners: {
click: {
element: 'el',
fn: function(){var createappCrdInDetailView = new Ext.create('SApp.view.appCrd.appCrdInDetailView');
var vport = Ext.getCmp('appCrdMainContent');
vport.removeAll(true, true);
vport.add(createappCrdInDetailView.show());}
},
dblclick: {
element: 'body',
fn: function(){ console.log('dblclick body'); }
}
},
singleSelect: true,
cls: 'x-image-view',
initComponent: function() {
this.store = Ext.create('Ext.data.Store', {
storeId: 'CustomerDataStore',
autoLoad: true,
model: 'SApp.model.appCrd.InDetailModel',
proxy: {
type: 'ajax',
url : '../SApp/resources/data/appCrd/InList.json',
reader: {
type: 'json'
}
}
});
this.callParent();
}
});
code is working after changing listener
swipe: {
element: 'el', //bind to the underlying body property on the panel
event: 'swipe',
fn: function(event){
if(event.direction == 'left'){
slideRight()
}else{
slideLeft()
}
}
}