agGroupCellRenderer not found - ag-grid

I upgraded the ag-grid & ag-grid-react to 14.2.0, but I still get this warning:
ag-grid: Looking for component [agGroupCellRenderer] but it wasn't found.
My column definitions:
let columnDefs = [
{headerName: 'Name', field: 'userName', width:163, cellRenderer:'agGroupCellRenderer'},
{headerName: 'Job Title', field: 'jobTitle', width:143},
]
What am I missing here?

To those who have this message "ag-Grid: Looking for component" with Angular 8, check to see if you have it referenced on your HTML page. In my code, I was missing some reference code in the HTML file. Here is a rough example that could help someone:
EXAMPLE
Component File:
import { AgGroupCellRendererComponent } from
'../../global/components/agGroupCellRenderer.component';
columnDefs= [{headerName: 'Name', field: 'userName', width:163,
cellRenderer:'agGroupCellRenderer'}]
frameworkComponents: any;
constructor() {
this.frameworkComponents = {
agGroupCellRenderer: AgGroupCellRendererComponent,
};
}
HTML File:
<ag-grid-angular
style="width: 100%; height: 500px;"
class="ag-theme-balham"
[gridOptions]="gridOptions"
[rowData]="rowData"
[frameworkComponents]="frameworkComponents"
>

I think you should use components property for mapping string(agGroupCellRenderer) to angular component(AgGroupCellRendererComponent)
gridOptions = {
...
components: {
agGroupCellRenderer: AgGroupCellRendererComponent
}
}
More information here https://www.ag-grid.com/javascript-grid-cell-rendering/

make sure to have in your component file :
frameworkComponents = {agGroupCellRenderer: AGroupCellRenderer}
context = { componentParent: this }
.....
{headerName: 'Name', field: 'userName', width:163, cellRenderer:'agGroupCellRenderer'}
and this in your html file :
<ag-grid-angular
..
[frameworkComponents]="frameworkComponents"
[context] = "context"
..>

The warning went away when upgraded to 15.0.0

Related

Ag Grid headerClass overrides numericColumn type

I am trying to set AG Grid header style using headerClass:"ag-grid-total-header" but it overrides type: 'numericColumn' - it shows the header cell value left aligned.
columnDef:
{ headerName: 'Total', field: 'Total', width: 125, sortable: true, type: 'numericColumn', headerClass:"ag-grid-total-header"}
CSS:
.ag-grid-total-header {
text-align:right; /* It does not work */
background-color: red; /* This works */
}
Is there any way to apply header style while preserving numericColumn type?
stackblitz:
https://stackblitz.com/edit/angular-ag-grid-angular-huk1fp?file=app/my-grid-application/my-grid-application.component.ts
EDIT:
It works if I add headerComponentParams. However, the sorting does not work if I add headerComponentParams.
headerComponentParams : {
template:
'<div class="ag-cell-label-container" role="presentation">' +
'<span ref="eText" class="ag-header-cell-text" role="columnheader"></span>' +
'</div>'
}
The header class that is used for a numeric type header is ag-numeric-header. So, apply both your class ag-grid-total-header and ag-numeric-header classes to your column. Change your column definition to:
{
headerName: "Price",
field: "price",
type: "numericColumn",
headerClass: ["my-header-class", 'ag-numeric-header']
},
Demo.

Click event for group header on ag-grid angular

I am using ag-angular-grid in this I have group header and child columns also.
I want to know click event of that group header click.
below how I create header :
{
headerName: "<span id='performanceData'>Performance</span> <i class='fa fa-eye group-open-settings-button' aria-hidden='true' data-group='PerformanceData' ></i>",
groupId: "PerformanceData",
marryChildren: false,
onCellValueChanged:event=>{
console.log('trst');
},
children: [
{
headerName: "Talent Decision",
headerTooltip: "Talent Decision",
on click this i wants to open a popup.
any idea?
You will need to create a separate component for your custom group header. This can be done by implementing the IHeaderGroupAngularComp and using headerGroupComponentFramework, as stated on the documentation for the Header Group component.
Here is a rough sketch on how you can get it done.
First and foremost, on your main component that is using ag-grid, we will need to bind the input properties for frameworkComponents, and import your custom header group component.
On the component.html,
<ag-grid-angular style="width: 100%; height: 350px;" class="ag-theme-balham"
[columnDefs]="columnDefs"
[frameworkComponents]="frameworkComponents"
<!-- other properties -->
</ag-grid-angular>
On the component.ts, define the component which will be binded to your frameworkComponents, and for your custom header.
import { CustomHeaderGroupComponent } from '../custom-header-group-component/custom-header-group.component';
constructor() {
this.frameworkComponents = {
customHeaderGroupComponent: CustomHeaderGroupComponent,
};
this.columnDefs = [
{
headerGroupComponent: 'customHeaderGroupComponent',
// other properties for your group header
}
];
}
Do not forget to include your custom header group component on your module.ts too:
import { CustomHeaderGroupComponent } from "./custom-header-group-component/custom-header-group.component";
#NgModule({
imports: [
AgGridModule.withComponents(
[
CustomHeaderGroupComponent
]
),
// other imports
],
declarations: [
CustomHeaderGroupComponent,
// other components
],
// others
})
On your custom component.html template for the header group, you can then bind the (click) event to the header to it:
<div (click)="openPopup($event)"><span id='performanceData'>Performance</span> <i class='fa fa-eye group-open-settings-button' aria-hidden='true' data-group='PerformanceData' ></i></div>
And on your component.ts for the header group, you can define the openPopup() method:
import { Component } from '#angular/core';
import { IHeaderGroupAngularComp } from 'ag-grid-angular';
import { IHeaderGroupParams } from 'ag-grid-community';
.
.
.
export class CustomHeaderGroupComponent implements IHeaderGroupAngularComp {
params: IHeaderGroupParams;
agInit(params: IHeaderGroupParams) {
this.params = params;
}
.
.
openPopup() {
// handle the rest to enable to opening of the popup
}
}
The full working demo is actually available here, though it is a complete demo for all the features.

How to format data before displaying it on ag-grid

I've just discovered ag-grid. I'm using it on angular2+ and loading data from api. One of fields is date, but its in ISO format. I've been trying to format it, is there any way to do it, is it possible to add pipe or some other way? Usually i do it like this {{ someISODate | date: 'dd.MM.yyyy HH:mm'}}. Do i really have to format it manually in component before displaying it? Also I was wondering if its possible to add two fields under one column. Why? Well i have column author, and in data that im getting from api i have author.firstname and author.lastname, and now I wanna display both fields in same column. Any hints or examples are more than welcomed.
columnDefs = [
{headerName: 'Datum kreiranja', field: 'createdAt' }, //<-- wanna format it
{headerName: 'Vrsta predmeta', field: 'type.name' },
{headerName: 'Opis', field: 'description'},
{headerName: 'Kontakt', field: 'client.name'},
{headerName: 'Autor', field: 'author.firstname'}, //<-- wanna display author.lastname in same cell
{headerName: 'Status', field: 'status.name'}
];
You can do this by using cellRenderer (or valueFormatter as pointed in the UPDATE) and moment library.
{
headerName: 'Datuk kreiranja', field: 'createdAt',
cellRenderer: (data) => {
return moment(data.createdAt).format('MM/DD/YYYY HH:mm')
}
}
If you don't want to use moment, then below is how you can do it.
cellRenderer: (data) => {
return data.value ? (new Date(data.value)).toLocaleDateString() : '';
}
For Author field as well,
cellRenderer: (data) => {
return data.author.firstname + ' ' + data.author.lastname;
}
Reference: ag-grid: Cell Rendering
UPDATE
As suggested by #Mariusz, using valueFormatter makes more sense in this scenario. As per documentation, Value Formatter vs Cell Renderer
value formatter's are for text formatting and cell renderer's are for
when you want to include HTML markup and potentially functionality to
the cell. So for example, if you want to put punctuation into a value,
use a value formatter, but if you want to put buttons or HTML links
use a cell renderer.
You can use valueFormatter
{headerName: 'Datuk kreiranja', field: 'createdAt', valueFormatter: this.dateFormatter},
Create a small function:
dateFormatter(params) {
return moment(params.value).format('MM/DD/YYYY HH:mm');
}
First of all thanks to Paritosh.
The issue I was facing is the date field I was receiving from API is on the below format
"endDateUTC":"2020-04-29T12:00:00",
I have followed Paritosh solution using cellrenderer along with moment library but the value was always formatted to today's date for some reason.
The below solution is using valueFormatter with moment library.
This is for Angular2+ version. The job is really simple
In your .ts file:
import * as moment from 'moment';
{
headerName: 'End Date',
field: 'endDateUTC',
minWidth: 80,
maxWidth: 100,
valueFormatter: function (params) {
return moment(params.value).format('D MMM YYYY');
},
},
And the output you will get is:
End date:
29 APR 2020
Please feel free to change the date format you need.
Hope this will be helpful to some one.
I just want to expand on Vishwajeet's excellent answer from April 2019. Here's how I would use his code, and which import commands would be required:
import { Component, OnInit, ViewChild, LOCALE_ID, Inject } from '#angular/core';
constructor(#Inject(LOCALE_ID) private locale: string)
{
}
columnDefs = [
{ headerName: 'Last name', field: 'lastName' },
{ headerName: 'First name', field: 'firstName' },
{ headerName: 'DOB', field: 'dob', cellRenderer: (data) => { return formatDate(data.value, 'd MMM yyyy HH:mm', this.locale); }},
{ headerName: 'Policy start', field: 'policyStartDate', cellRenderer: (data) => { return formatDate(data.value, 'd MMM yyyy HH:mm', this.locale); } },
{ headerName: 'Policy end', field: 'policyEndDate', cellRenderer: (data) => { return formatDate(data.value, 'd MMM yyyy HH:mm', this.locale); } }
]
And your agGrid would contain something like this:
<ag-grid-angular
class="ag-theme-material"
[rowData]="rowData"
[columnDefs]="columnDefs"
</ag-grid-angular>
This works really nicely, but I decided to move the date formatting into it's own cell renderer for a few reasons:
The code above will display null values as "1 Jan 1970 01:00"
You would need to repeat this code, plus the imports and #Inject, into any control which uses it.
It repeats the logic each time, so if you wanted to change the date format throughout your application, it's harder to do. Also, if a future version of Angular broke that date formatting, you'd need to apply a fix for each occurrence.
So, let's move it into it's own cell renderer.
My DateTimeRenderer.ts file looks like this:
import { Component, LOCALE_ID, Inject } from '#angular/core';
import { ICellRendererAngularComp } from 'ag-grid-angular';
import { ICellRendererParams } from 'ag-grid-community';
import { formatDate } from '#angular/common';
#Component({
selector: 'datetime-cell',
template: `<span>{{ formatTheDate(params.value) }}</span>`
})
export class DateTimeRenderer implements ICellRendererAngularComp {
public params: ICellRendererParams;
constructor(#Inject(LOCALE_ID) public locale: string) { }
agInit(params: ICellRendererParams): void {
this.params = params;
}
formatTheDate(dateValue) {
// Convert a date like "2020-01-16T13:50:06.26" into a readable format
if (dateValue == null)
return "";
return formatDate(dateValue, 'd MMM yyyy HH:mm', this.locale);
}
public onChange(event) {
this.params.data[this.params.colDef.field] = event.currentTarget.checked;
}
refresh(params: ICellRendererParams): boolean {
return true;
}
}
In my app.module.ts file, I need to import this Component:
import { DateTimeRenderer } from './cellRenderers/DateTimeRenderer';
#NgModule({
declarations: [
AppComponent,
DateTimeRenderer
],
imports: [
BrowserModule,
AgGridModule.withComponents([DateTimeRenderer])
],
providers: [],
bootstrap: [AppComponent]
})
And now, back in my Component which uses the agGrid, I can remove LOCALE_ID, Inject from this line:
import { Component, OnInit, ViewChild, LOCALE_ID, Inject } from '#angular/core';
..remove it from our constructor...
constructor()
{
}
..import our new renderer...
import { DateTimeRenderer } from './cellRenderers/DateTimeRenderer';
..and change the columnDefs to use the new renderer:
columnDefs = [
{ headerName: 'Last name', field: 'lastName' },
{ headerName: 'First name', field: 'firstName' },
{ headerName: 'DOB', field: 'dob', cellRenderer: 'dateTimeRenderer' },
{ headerName: 'Policy start', field: 'policyStartDate', cellRenderer: 'dateTimeRenderer' },
{ headerName: 'Policy end', field: 'policyEndDate', cellRenderer: 'dateTimeRenderer' }
]
frameworkComponents = {
dateTimeRenderer: DateTimeRenderer
}
And I just need to make sure my agGrid knows about this new frameworkComponents section:
<ag-grid-angular
class="ag-theme-material"
[rowData]="rowData"
[columnDefs]="columnDefs"
[frameworkComponents]="frameworkComponents" >
</ag-grid-angular>
And that's it.
Again, the nice thing about this is I can use this date formatter anywhere throughout my code, and all the logic is in one place.
It's just shocking that, in 2020, we actually need to write our own date formatting function for an up-to-date grid like agGrid... this really should've been included in the agGrid libraries.
For Angular, if you want to do this without moment.js you can try something like below:
import { Component, OnInit, Inject, LOCALE_ID } from '#angular/core';
import { formatDate } from '#angular/common';
#Component({
selector: 'app-xyz'
})
export class xyzComponent implements OnInit {
constructor( #Inject(LOCALE_ID) private locale: string ) {
}
columnDefs = [
{headerName: 'Submitted Date', field: 'lastSubmittedDate', cellRenderer: (data) => {
return formatDate(data.value, 'dd MMM yyyy', this.locale);
}];
}
This component is using format date of angular/common
(Working & Optimized solution for date formatting is here!)
Tested on Angular 8 with dynamic data where date is coming like 2019-11-16T04:00:00.000Z.
In Ag-grid if you use valueFormatter, then no need of including "field:'Order Date'".
Also following Ragavan Rajan's answer. So you need to install moment.js in your angular CLI.
Working code and installation is below:
//Install moment.js in angular 8 cli.(no need of --save in latest versions
npm install moment
//In your component.ts
import * as moment from 'moment';
//Inside your colDef of ag-grid
{
headerName: "Effective Date",
field: "effectiveDate",
valueFormatter: function (params){
return moment (params.value).format ('DD MMM, YYYY');
}
/*
* This will display formatted date in ag-grid like 16 Nov, 2019.
* field name's value is from server side.(column name).
*/
If you are using AdapTable then you can do it via their Format Column function which can be applied either at design-time or run-time. And there you can choose pretty much any DateTime format that you want.
https://demo.adaptabletools.com/style/aggridformatcolumndemo
if you have two subfields like "Start Date" and "End Date" then you are supposed to do like this:
{
headerName: "Date Range",
children: [
{
field: 'StartDate',
cellRenderer: (data) => {
return data ? (new Date(data.value)).toLocaleDateString() : '';
}
},
{
field: 'EndDate',
cellRenderer: (data) => {
return data ? (new Date(data.value)).toLocaleDateString() : '';
}
}
],
}
I'm Using Angular 10, And I achieved the date formatting by using cellRenderer and DatePipe
{
field: "fieldName",
cellRenderer: (res) => {
const datepipe : DatePipe = new DatePipe("en-US");
var x = datepipe.transform(res.data.fieldName.split('T')[0],"yyyy-MM-dd");
return x;
}
},
Split('T') is used because when you call the Api, date comes in this format
"2021-07-31T00:00:00.000Z".
Try this:
{
headerName: 'Order Date',
field: 'OrderDate',
valueFormatter: function (params) {
var nowDate = new Date(parseInt(params.value.substr(6)));
return nowDate.format("yyyy-mm-dd");
}

ag-grid column headers stacked to the left

Only the first column header is being drawn correctly, all subsequent items in the columnDef array are drawn below it. If I populate the table with data, all column data is stacked to the left as well.
Here's what it looks like; the gray band in the row, below Country is actually moving down, as part of the framework.setTimeout() function in ag-grid.js.
The JS looks like:
gridOptions.columnDefs = [
{ headerName: 'Country', field: 'country' },
{ headerName: 'City', field: 'city' },
{ headerName: 'Jan', field: 'jan_act' },
{ headerName: 'Feb', field: 'feb_act' },
{ headerName: 'Mar', field: 'mar_act' },
{ headerName: 'Apr', field: 'apr_act' },
{ headerName: 'May', field: 'may_act' }];
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
I'm using ag-grid.noStyle.js v7.0.2 and think I have all dependencies loaded correctly for ag-grid. Am not using React or Angular.
Ah, found it! It's the <!DOCTYPE html> tag.
Problem was immediately resolved by changing the height of my div to pixel based, not %, e.g.
<div id="myGrid" style="height: 400px;" class="ag-fresh"></div>
Found it by going back to absolute basics after the JavaScript Datagrid example also failed to work.
Working with the CSS height property and percentage values.

Browser does not remember password during login

An earlier question mentioned a method using the el config in order to make the browser remember passwords. Howewer, the el config no longer exists in ExtJS 4.1.
Now, what should I do?
I believe it should be contentEl instead of el but I do this another way. You can build the entire thing with ExtJS directly. The only twist is that Ext fields will be created with the autocomplete=off attribute by default, so I use a derived class to override that.
Ext.define('ACField', {
extend: 'Ext.form.field.Text',
initComponent: function() {
Ext.each(this.fieldSubTpl, function(oneTpl, idx, allItems) {
if (Ext.isString(oneTpl)) {
allItems[idx] = oneTpl.replace('autocomplete="off"', 'autocomplete="on"');
}
});
this.callParent(arguments);
}
});
Ext.onReady(function() {
new Ext.panel.Panel({
renderTo: Ext.getBody(),
width: 300,
height: 100,
autoEl: {
tag: 'form',
action: 'login.php',
method: 'post'
},
items: [
new ACField({
xtype: 'textfield',
name: 'username',
fieldLabel: 'Username'
}),
new ACField({
xtype: 'textfield',
name: 'password',
fieldLabel: 'Password',
inputType: 'password'
}),
],
buttons: [{
xtype: 'button',
text: 'Log in',
type: 'submit',
preventDefault: false
}]
});
});
The answer from lagnat was mostly correct, to get this also working on Chrome and Firefox the following is required:
1) Override default ExtJS Textfield behavior for autocomplete (copied from lagnat):
Ext.define('ACField', {
extend: 'Ext.form.field.Text',
initComponent: function() {
Ext.each(this.fieldSubTpl, function(oneTpl, idx, allItems) {
if (Ext.isString(oneTpl)) {
allItems[idx] = oneTpl.replace('autocomplete="off"', 'autocomplete="on"');
}
});
this.callParent(arguments);
}
});
2) Make sure the textfields are within a <form> tag: (see answer from lagnat), since ExtJS 4 the <form> tag is no longer present in a FormPanel.
autoEl: {
tag: 'form',
action: '/j_spring_security_check',
method: 'post'
},
3) Make sure there is a <form> present in the HTML, with the same <input> names:
items:[
Ext.create('ACField',{
fieldLabel: 'Username',
name:'j_username',
inputId: 'username',
allowBlank:false,
selectOnFocus:true
}),
Ext.create('ACField',{
fieldLabel:'Password',
name:'j_password',
inputId: 'password',
xtype:'textfield',
allowBlank:false,
inputType:'password'
})
],
and within the HTML the regular form with same input names:
<body>
<div id="login-panel">
<form id="loginForm" action="<c:url value="/j_spring_security_check"/>" method="post">
<input class="x-hidden" type="text" id="username" name="j_username"/>
<input class="x-hidden" type="password" id="password" name="j_password"/>
</form>
</div>
<noscript>Please enable JavaScript</noscript>
</body>
With all these changes in place, saving username/password works in IE, Chrome and Firefox.
There is the autoRender property which will allow you to apply the Extjs field to an already existing element on the page. So if you set up your basic form in html, the browser should recognize the fields for the form as login info, and then Extjs will overlay itself onto that form if you use the autoRender with a reference to the correct fields (and also the button on the form to a submit type button in your basic html form) it should work correctly.
Also, keep in mind that the browser probably will not recognize an ajax call for logging in and you may need to use the basic form submission. I have a working example in my application, but I would have a hard time trying to pull out application specific code so have an example for here. Please comment if you need the example and I may be able to get back to you by monday.
Answer by #Lagnat does not work for ExtJS 4.2.1 and 4.2.2. It might be due to removal of type config from button. What we need is standard submit button <input type="submit"> for the button. So I added it on the button with opacity: 0. Below is my working code (Tested working on Firefox 27, Chrome 33, Safari 5.1.7, IE 11. Autofill/Autosave password should be enabled for browser):
Ext.create('Ext.FormPanel', {
width: 400,
height: 500,
padding: '45 0 0 25',
autoEl: {
tag: 'form',
action: 'login.php',
method: 'post'
},
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield',
fieldLabel: 'Username',
name: 'username',
listeners: {
afterrender: function() {
this.inputEl.set({
'autocomplete': 'on'
});
}
}
}, {
xtype: 'textfield',
fieldLabel: 'Password',
inputType: 'password',
name: 'username',
listeners: {
afterrender: function() {
this.inputEl.set({
'autocomplete': 'on'
});
}
}
}, {
xtype: 'button',
text: 'LOG IN',
width: 100,
height: 35,
preventDefault: false,
clickEvent: 'click',
listeners: {
afterrender: function() {
this.el.createChild({
tag: 'input',
type: 'submit',
value: 'LOG IN',
style: 'width: 100px; height: 35px; position: relative; top: -31px; left: -4px; opacity: 0;'
});
}
}
}]
});
I recommend using the built in Cookie functionality of ExtJS.
You can read a cookie using: readCookie('password);
You can create a cookie using: createCookie('password', "pass123", 30); // save for 30 days
Then you can use basic business logic to auto-populate your formField with the stored password.
Does that make sense?