Ag-Grid onGridReady not getting parameters? - ag-grid

I've been working with ag-grid, and on looking at other samples I've seen the following snippet of code that people use to save the ag-grid api as a variable for later use.
HTML:
template: `
<ag-grid-angular
id="grid"
style="width: 100vw;"
[style.height] = 'height'
class="ag-theme-balham ag-grid"
[columnDefs]="columnDefs"
[gridOptions]="gridOptions"
(gridReady)="onGridReady($event)"
></ag-grid-angular>`
Typescript:
onGridReady(params){
this.resizeGrid();
this.gridApi = params.api;
this.columnsApi = params.columnApi;}
I've been trying to do something similar, however when I try the same, params is showing up as "undefined". I've tried all manner of methods to try to get the api, but it's just not working for me. My grid loads correctly with the data I've passed to it, but it keeps telling me the params.api is undefined. What am I missing here?

Managed to figure it out after a bit more trial & error, posting the solution just in case anyone is having the same problem.
I took out the (gridReady)="onGridReady($event)" from my HTML code, and added the following to my GridOptions declaration:
onGridReady: this.onGridReady.bind(this),
After that I created a new method to set the api, which seems to work just fine.
onGridReady(event: any){
this.gridApi = event.api;
this.gridApi.sizeColumnsToFit();
}

Can you try removing the gridOptions from your HTML template
<ag-grid-angular
style="width: 600px; height: 500px;"
class="ag-theme-balham"
[columnDefs]="columnDefs"
[animateRows]="true"
[pagination]="true"
[sortingOrder]="sortingOrder"
(gridReady)="onGridReady($event)"
></ag-grid-angular>
Note: Not using gridoptions in the above HTML template.
And if you want to load data from the API. In .ts file
onGridReady(params) {
this.gridApi = params.api;
console.log('grid api', params.api);
this.gridColumnApi = params.columnApi;
// const dataValue = [{firstName: 'Ragavan', age: 31}, {firstName: 'Mike', age: 50}];
this.http.get('https://your-mock-url/params')
.subscribe(data => {
params.api.setRowData(data);
}
);
}
Try the above code. Hope it should work.

Related

"jQuery for React AST"?

Is there such a thing as a "jQuery for React AST"? That like jQuery allows for elegant search, traversal, creation, mutation of an AST that contains things like JSXNode, etc? I saw that acorn has some basic traversal stuff, but it isn't super usable for repeatedly doing reorders, insertions, wrapping a component in {flag && } to conditionally render, etc, etc. I'm not even sure how to google for this except "jquery for AST" which, uh yeah, didn't work.
Subsecond
You can use Subsecond for this purpose, here is how it will looks like:
Input:
<h1>hello world</h1>
Output:
<h2>hello world</h2>
Transformation:
S('JSXIdentifier').each((node) => {
node.text('h2');
})
πŸ“œ
Here is playground.
🐊Putout
Also there is a way to do similar things using declarative approach with help of 🐊Putout I'm working on:
Change tag
Input:
<h1>hello world</h1>
Output:
<h2>hello world</h2>
Transformation:
export const replace = () => ({
'<h1>__a</h1>': '<h2>__a</h2>',
});
πŸŠπŸ“œ
Here is playground.
Change Attribute
You can also change an attribute className to class:
Input:
const div = <div className="abc">{x}</div>
Output:
const div = <div class="abc">{x}</div>
Transformation:
export const replace = () => ({
'<div className="__a">__jsx_children</div>': '<div class="__a">__jsx_children</div>',
});
πŸŠπŸ“œ
Here is playground

AGGrid - Vue Data Grid: Vuex Documentation

On this page of documentation:
https://www.ag-grid.com/archive/27.1.0/vue-data-grid/framework-data-flow/
The Vue3 code Snippet:
<ag-grid-vue
style="width: 600px; height: 150px;"
class="ag-theme-alpine"
v-model="rowData"
v-on:update:modelValue="modelChanges"
//..other bindings/attributes
methods: {
dataModelChanged(rowData) {
this.$store.dispatch('applyTransaction', rowData);
}
}
is the line:
v-on:update:modelValue="modelChanges"
meant to be?
v-on:update:modelValue="dataModelChanged"
Unfortunately the example repo is old Vue2 and not really useful for helping understand this in action as it doesn't use this binding.

How to display column dynamically according to API responce using ag-grid

Below is code for ag grid and its getting error on " this.gridApi.setColumnDefs(this.columnDefs)" can somebody guide how to resolve this issue:
<ag-grid-angular "
class="ag-theme-balham"
#agGrid
[columnDefs]="columnDefs"
id="newGrid"
[enableSorting]="true"
[enableFilter]="true"
[modules]="modules"
[paginationAutoPageSize]="true"
[rowData]="rowData"
[defaultColDef]="defaultColDef"
(gridReady)="onGridReady($event)"
[pagination]="true">>
</ag-grid-angular>
Am calling this method on GreadReady() as well as ngOnit but showing error on "this.gridApi.setColumnDefs(this.columnDefs)". Actually this.gridAPI getting undefine. and showing error in console ="Cannot read property 'setColumnDefs' of undefined"
public getcolumnData()
{
let columnDefs=[];
this.GetGridData();
let header:any =[];
Object.keys(this.JSONDATA).forEach(function(key) {
header.push(key)
});
console.log("header.........",header)
columnDefs=[...columnDefs,{headerName:header,field: header }];
});
this.gridApi.setColumnDefs(this.columnDefs)
}
gridApi for the ag-grid is only available after onGridReady event. More details regarding how you could have instance of gridApi can be found on almost every ag-grid example.
Reference: Column Definition Example
Now, what to be done.
In this case, you don't need to explicitly call gridApi.setColumnDefs. While iterating the column details received from server, simply push them inside columnDefs of the component. In your template, you have already passed the columnDefs array to the ag-grid.
this.columnDefs = [];
this.columnDefs.push({
headerName: header, field: header
// ... other details
});

How to use javascript libraries that require binding to DOM nodes

I have been trying to use Ag-Grid with Svelte. I understand that the main problem with using this grid library is that it needs to bind to a dom element that may not exist at the time of the code executing. For example:
// lookup the container we want the Grid to use
var eGridDiv = document.querySelector('#myGrid');
In this case, the #myGrid element does not exist yet.
I have tried creating an element and then placing it on the HTML part of the Svelte component, like this.
let eGridDiv = document.createElement("DIV");
let gridOptions = { columnDefs: columnDefs, rowData: $orders };
new Grid(eGridDiv, gridOptions);
And then down on the HTML section
<eGridDiv />
However, the new element does not seem to be initialized by Ag-Grid.
So what is the recommended way to use these types of libraries in Svelte?
If you want to use a DOM node in the script part of your component you can use the bind:this={domNode} element binding to get a reference to it, and then use it after the component has been rendered in onMount.
<script>
import { onMount } from 'svelte';
let domNode;
// ...
onMount(() => {
const gridOptions = { columnDefs: columnDefs, rowData: $orders };
new Grid(domNode, gridOptions);
});
</script>
<div bind:this={domNode} />

What causes the "control.registerOnChange is not a function" error

I have a form using the reactive form approach. The form is created as follow in my pug:
form([formGroup]='form', novalidate='', (ngSubmit)='postSurvey(form.value, form.valid)')
Everything works fine except when I try to change the form (which is a FormArray) in the javascript part. I get the following error:
EXCEPTION: Error in http://localhost:8080/app/components/fillForm.template.html:0:326 caused by: control.registerOnChange is not a function
core.umd.js:3497 TypeError: control.registerOnChange is not a function
at setUpControl (http://localhost:8080/node_modules/#angular/forms/bundles/forms.umd.js:1634:17)
at eval (http://localhost:8080/node_modules/#angular/forms/bundles/forms.umd.js:4752:25)
at Array.forEach (native)
at FormGroupDirective._updateDomValue (http://localhost:8080/node_modules/#angular/forms/bundles/forms.umd.js:4747:29)
at FormGroupDirective.ngOnChanges (http://localhost:8080/node_modules/#angular/forms/bundles/forms.umd.js:4616:22)
at Wrapper_FormGroupDirective.ngDoCheck (/ReactiveFormsModule/FormGroupDirective/wrapper.ngfactory.js:30:18)
at View_FillFormComponent2.detectChangesInternal (/AppModule/FillFormComponent/component.ngfactory.js:275:32)
at View_FillFormComponent2.AppView.detectChanges (http://localhost:8080/node_modules/#angular/core/bundles/core.umd.js:12592:18)
at View_FillFormComponent2.DebugAppView.detectChanges (http://localhost:8080/node_modules/#angular/core/bundles/core.umd.js:12739:48)
at ViewContainer.detectChangesInNestedViews (http://localhost:8080/node_modules/#angular/core/bundles/core.umd.js:12850:41)
at CompiledTemplate.proxyViewClass.View_FillFormComponent0.detectChangesInternal (/AppModule/FillFormComponent/component.ngfactory.js:64:14)
at CompiledTemplate.proxyViewClass.AppView.detectChanges (http://localhost:8080/node_modules/#angular/core/bundles/core.umd.js:12592:18)
at CompiledTemplate.proxyViewClass.DebugAppView.detectChanges (http://localhost:8080/node_modules/#angular/core/bundles/core.umd.js:12739:48)
at CompiledTemplate.proxyViewClass.AppView.internalDetectChanges (http://localhost:8080/node_modules/#angular/core/bundles/core.umd.js:12577:22)
at CompiledTemplate.proxyViewClass.View_FillFormComponent_Host0.detectChangesInternal (/AppModule/FillFormComponent/host.ngfactory.js:29:19)
My code to change the form is quite complex and I can't simplify it or reproduce it in a plunker. More than finding directly the solution (it's too difficult with so little details), I would like to understand what this error means? And what might cause this error.
I have figured out that the error occurs at [formGroup]='form' in my HTML.
Any suggestion will help.
Update I have filed an issue on angular github here and have proposed a fix here The plunker to reproduce the issue is here
Yes, that error message is a bit cryptic, but if you use FormBuilder, you would see this when you added a control to FormGroup in your component and named it "A", but then either forgot to add input with formControlName="A" to your template, or formControlName for the intended input is not A, or empty, or not present.
Basically, it says: "I cannot match the control I have in FormGroup to the control in the template".
I came across looking for a solution to the similar issue and then found a solution myself.
My issue was the following. I had a form like this
form: FormGroup = new FormGroup({
status: new FormArray([])
});
Initially it was represented by the list of checkboxes for each status on the template. And then I created a custom component to represent status selector and used it in template like so
<status-selector [formControlName]="'status'"></status-selector>
The problem is that formControlName must point to FormControl instance, but actually it was pointing to a FormArray instance. So, changing to status: new FormControl([]) fixed this issue for me.
In my case this error was thrown because I was using FormControlName instead of FormArrayName to bind to a FormArray in my template.
My component code:
public form = new FormGroup({
...
checkboxes: new FormArray([....])
})
My template code that threw error:
<input type="checkbox" formControlName="checkboxes" />
Fix:
<input type="checkbox" formArrayName="checkboxes" />
I have also encountered this error when mixing template driven with reactive driven approaches (by mistake):
<input #inputCtrl
[formControl]="inputCtrl"
/>
inputCtrl was properly defined in the component. Of course, #inputCtrl must be scrapped in order to work (it was hard to see when input had about 10 attributes).
In my case the error occurred when the formControl name was same as a template variable on the page. For example
<select id="status" [formControl]="userStatus">...</select>
<form-status #userStatus ></form-status> //matching template variable name
this.extensionForm = this._fb.group({
id: [''],
category: [12],
extensions: new FormArray([]),
priority: ['', []],
});
formArray.push(this.extensionForm);
Note :- error occurs because of you have used formControlName
where you declare formArray, you must use formArrayName instead
<input type="text" formControlName="extensions" />
simple solution:
<input type="checkbox" formArrayName="extensions" />
If have defined a FormArray field in your form, note that you do NOT need to label it with formControlName="". You need to handle the input and validation in other ways (setters, getters, functions), but will definitely get an error if you try to assign formControlName to a FormArray!
This error also appears when we use a reactive form inside ng-template in conjunction with *ngIf.
To avoid this use ng-container and do not use ngElse.
In my case I got the error when I used formControlName in the template when actual form model was a FormGroup instance. Changing to formControlGroup helped.
Maybe you have moved a control element outside the group in the template.
OK:
<div formGroupName="passwordForm">
Password: <input type="password" formControlName="password">
Confirm: <input type="password" formControlName="confirmPassword">
</div>
Not OK:
Password: <input type="password" formControlName="password">
<div formGroupName="passwordForm">
Confirm: <input type="password" formControlName="confirmPassword">
</div>
In my case the issue was that I was referring to something as a FormGroup instead of a FormControl with an object as the value.
This was my initial, faulty code:
formArray.push(
new FormGroup({
value: new FormControl('', Validators.required),
description: new FormControl('', Validators.required),
tags: new FormArray([], Validators.minLength(1)),
}),
);
and in the template, I was using a component (custom-form-component in this example) which implements ControlValueAccessor:
<li *ngFor="let item of items; let itemIndex = index"
<custom-form-component [formGroupName]="itemIndex"></custom-form-component>
</li>
This group is handled in the custom-form-component component which I'm using, therefore instead a FormControl should be used:
formArray.push(
new FormControl({
value: '',
description: '',
tags: [],
}),
);
and in the template, use formControlName instead of formGroupName:
<li *ngFor="let item of items; let itemIndex = index"
<custom-form-component [formControlName]="itemIndex"></custom-form-component>
</li>
In Angular I had the same problem when I tried to build my own form component and forgot to implement the ControlValueAccessor interface:
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '#angular/forms';
#Component({
selector: 'my-country-select',
templateUrl: './country-select.component.html',
styleUrls: ['./country-select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CountrySelectComponent),
multi: true,
},
],
})
export class CountrySelectComponent implements OnInit, OnChanges, ControlValueAccessor {
propagateChange = (_: any) => { }; // ControlValueAccessor
private internalValue: string | undefined;
get value(): string | undefined {
return this.internalValue;
}
set value(value: string | undefined) {
this.internalValue = value;
this.propagateChange(value);
}
// some other methods here
// implementing the ControlValueAccessor interface with these three methods
writeValue(obj: any): void {
if (obj) {
this.value = obj;
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
// throw new Error('Method not implemented.');
}
}
I encountered this when I accidentally reset the value of my form control property instead of using the .setValue() method.
Wrong
control = new FormControl([1])... control = []
Right
control = new FormControl([1])... control.setValue([])
Adding in what was causing it in my situation.
.ts file,
...
export class MyComponent {
dateFrom = new FormControl(new Date());
...
}
.html file,
<mat-form-field>
<input matInput [matDatepicker]="dateFrom" [formControl]="dateFrom"
placeholder="Min date">
<mat-datepicker-toggle matSuffix [for]="dateFrom"></mat-datepicker-toggle>
<mat-datepicker #dateFrom ></mat-datepicker>
</mat-form-field>
Basically, within the template file, it didn't know which 'dateFrom' to choose, and chooses the one in the template file, which isn't the FormControl in my typescript file, it's the mat-datepicker element in the template file.
Renaming the FormControl to dateFromCtrl fixed this,
i.e.
...
export class MyComponent {
dateFromCtrl = new FormControl(new Date());
...
}
.html file,
<mat-form-field>
<input matInput [matDatepicker]="dateFrom" [formControl]="dateFromCtrl"
placeholder="Min date">
<mat-datepicker-toggle matSuffix [for]="dateFrom"></mat-datepicker-toggle>
<mat-datepicker #dateFrom ></mat-datepicker>
</mat-form-field>
Works as expected.
Kodos to VS Code for figuring this out. I got pushed this direction by doing a Cmd + Click on the initial 'dateFrom' at [formControl]="dateFrom", and it pointed me to the mat-datepicker element.
to me it happened when I used same [formControl]="carBrand" and [matAutocomplete]="carBrandAuto" from my autocomplete input
I changed this
FROM:
...
<input
[formControl]="carBrand"
[matAutocomplete]="carBrand"
>
<mat-autocomplete matAutocomplete #carBrand="matAutocomplete">
...
TO
...
<input
[formControl]="carBrand"
[matAutocomplete]="carBrandAuto"
>
<mat-autocomplete matAutocomplete #carBrandAuto="matAutocomplete">
...
For future readers, my problem was a simple one and it causes TypeError: control.registerOnChange is not a function
When I was creating the new FormGroup I accidentally used FormGroup instead of FormControl
myForm: FormGroup = new FormGroup({
lname: new FormGroup({}),
fnam: new FormGroup({}),
date: new FormGroup({}),
});
It should have been this:
myForm: FormGroup = new FormGroup({
lname: new FormControl(''),
fnam: new FormControl(''),
date: new FormControl(null),
});
Hopefully this will save someone a few minutes in the future!
I had this error when I tried to access formGroup control like this
myFormGroup['controlName']
instead of using .get('controlName'). and btw If .get('...') is causing some typing problems, see this: https://stackoverflow.com/a/67835904/8094012
In my case, I was passing simple property to formControl i.e.
tagsList: Array<string> = ['banana', 'apple']
<myComponent [formControl]="tagsList"></myComponent>
but generally it should be like
tagsList = new FormControl()
<myComponent [formControl]="tagsList"></myComponent>
or
tagsList: Array<string> = ['banana', 'apple']
<myComponent [(ngModel)]="tagsList"></myComponent>