Using complex object for grouping in Ag Grid - ag-grid

I am trying to use a complex object to group my ag grid rows. Object of my rowdata looks like this -
const rowData= {
id : '123',
name: 'dummy',
category: 'A',
group : {
name : 'dummyGroup',
id : '456',
category: 'A'
}
}
Now, I am using group object to group the rows. And according to this documentation https://www.ag-grid.com/javascript-data-grid/grouping-complex-objects/ I am using keyCreator as keyCreator: params => params.value.name . My group object is uniquely identified by combination of id and catogory.
The problem that I am facing is, as I am using group.name in the keyCreator, if I have two row data object whose group.names are same but id and category are different, ag grid is grouping those rows together. I understand that this is the behavior from ag grid. So can I get any workaround for it? I need to show name on group row. But to identify the groups differently I need to use id+catogory in keyCreator. How can I achieve this ?

You need to utilise the groupRowInnerRenderer property so you can group by a combination of the id and category fields, while displaying the name as the group.
const gridOptions = {
groupDisplayType: 'groupRows',
groupRowInnerRenderer: function (params) {
return params.node.childrenAfterFilter[0].data.name;
},
columnDefs: [
{ field: 'id' },
{ field: 'name' },
{ field: 'category' },
{
field: 'group',
valueFormatter: groupValueFormatter,
rowGroup: true,
keyCreator: function (params) {
return params.value.id + params.value.category;
},
},
],
};
Demo.

Related

Ag grid community Text Filter textMatcher (Text Custom Matcher) doesn't have any affect on filtering

textMatcher is not marked as "e" but trying to implement it in community version, in order to add some special capabilities to the filtering, doesn't have any affect:
columnDefs = [ {
field: 'country',
filter: 'agTextColumnFilter',
filterParams: {
defaultOption: 'contains',
textMatcher: ({ filter, value, filterText }) => {
// return true/false according to some decision
},
},
......
Is it only available in enterprise version or do I need to define something else in order to use it?

Ag-grid column definition and `__metadata__` property

I need to introduce object with my own properties in columnDefs object.
After, I did it, I see in dev console
So, here has been written, that I could mean __metadata__ property. Does this property suit to mine purpose?
I've not found any information in types and docs about this property
ColDef has no custom state property that you could use. Certainly do not touch __metadata__. The name sounds like some internal implementation detail.
You can store your metadata in a separate object, for example using colId as a key:
columnDefs: ColDef[] = [
{
colId: 'id',
field: 'id'
},
{
colId: 'name',
field: 'name'
}
];
columnMetadata: {
id: 'something custom',
name: 'custom data'
}
private getColumnMetadata(column: Column) {
return this.columnMetadata[column.getColId()];
}

ag-grid sort by column A using hidden column B

Is there a way to sort by column A (user clicks on A column header) but under the hood use column B?
For example, I have a column "name" that displays a user's name. But then I have a column "name frequency in general population" that is hidden. I want to display the regular "name" but sort by the other column under the hood.
Create your own customer comparator and set it to your name field by using Custom Sorting.
With custom sorting, you have access to all the data in each row where you can choose what should go where.
var columnDefs = [
{ field: 'name', comparator: customComparator },
{ field: 'name frequency in general population' },
];
function customComparator(valueA, valueB, nodeA, nodeB, isInverted) {
const nodeAValue = nodeA.data['name frequency in general population'];
const nodeBValue = nodeB.data['name frequency in general population'];
return (nodeAValue > nodeBValue) ? 1 : -1;
}

KeystoneJs user-defined order for Relationship

I am using KeystoneJS with PostgreSQL as my backend and Apollo on the frontend for my app.
I have a schema that has a list that is linked to another list.
I want to be able to allow users to change the order of the second list.
This is a simplified version of my schema
keystone.createList(
'forms',
{
fields: {
name: {
type: Text,
isRequired: true,
},
buttons: {
type: Relationship,
ref: 'buttons.attached_forms',
many: true,
},
},
}
);
keystone.createList(
'buttons',
{
fields: {
name: {
type: Text,
isRequired: true,
},
attached_forms: {
type: Relationship,
ref: 'forms.buttons',
many: true,
},
},
}
);
So what I would like to do, is allow users to change the order of buttons so when I fetch them in the future from forms:
const QUERY = gql`
query getForms($formId: ID!) {
allforms(where: {
id: $formId,
}) {
id
name
buttons {
id
name
}
}
}
`;
The buttons should come back from the backend in a predefined order.
{
id: 1,
name: 'Form 1',
buttons: [
{
id: 1,
name: 'Button 1',
},
{
id: 3,
name: 'Button 3',
},
{
id: 2,
name: 'Button 2',
}
]
}
Or even just have some data on that returns with the query that will allow for sorting according to the user-defined sort order on the frontend.
The catch is that this relationship is many to many.
So it wouldn't be enough to add a column to the buttons schema as the ordering needs to be relationship-specific. In other words, if a user puts a particular button last on a particular form, it shouldn't change the order of that same button on other forms.
In a backend that I was creating myself, I would add something to the joining table, like a sortOrder field or similar and then change those values to change the order, or even order them on the frontend using that information.
Something like this answer here.
The many-to-many join table would have columns like formId, buttonId, sortOrder.
I have been diving into the docs for KeystoneJS and I can't figure out a way to make this work without getting into the weeds of overriding the KnexAdapter that we are using.
I am using:
{
"#keystonejs/adapter-knex": "^11.0.7",
"#keystonejs/app-admin-ui": "^7.3.11",
"#keystonejs/app-graphql": "^6.2.1",
"#keystonejs/fields": "^20.1.2",
"#keystonejs/keystone": "^17.1.2",
"#keystonejs/server-side-graphql-client": "^1.1.2",
}
Any thoughts on how I can achieve this?
One approach would be to have two "button" lists, one with a template for a button (buttonTemplate below) with common data such as name etc, and another (button below) which references one buttonTemplate and one form. This allows you to assign a formIndex property to each button, which dictates its position on the corresponding form.
(Untested) example code:
keystone.createList(
'Form',
{
fields: {
name: {
type: Text,
isRequired: true,
},
buttons: {
type: Relationship,
ref: 'Button.form',
many: true,
},
},
}
);
keystone.createList(
'Button',
{
fields: {
buttonTemplate: {
type: Relationship,
ref: 'ButtonTemplate.buttons',
many: false,
},
form: {
type: Relationship,
ref: 'Form.buttons',
many: false,
},
formIndex: {
type: Integer,
isRequired: true,
},
},
}
);
keystone.createList(
'ButtonTemplate',
{
fields: {
name: {
type: Text,
isRequired: true,
},
buttons: {
type: Relationship,
ref: 'Button.buttonTemplate',
many: true,
},
},
}
);
I think this is less likely to cause you headaches (which I'm sure you can see coming) down the line than your buttonOrder solution, e.g. users deleting buttons that are referenced by this field.
If you do decide to go with this approach, you can guard against such issues with the hook functionality in Keystone. E.g. before a button is deleted, go through all the forms and rewrite the buttonOrder field, removing any references to the deleted button.
I had a similar challenge once, so after some research and found this answer, I implemented a solution to a project using PostgreSQL TRIGGER.
So you can add a trigger where on an update, it should shift the buttonOrder.
Here is the SQL I had on me, this was the test code, I regex replaced the terms to fit your question :)
// Assign order
await knex.raw(`
do $$
DECLARE form_id text;
begin
CREATE SEQUENCE buttons_order_seq;
CREATE VIEW buttons_view AS SELECT * FROM "buttons" ORDER BY "createdAt" ASC, "formId";
CREATE RULE buttons_rule AS ON UPDATE TO buttons_view DO INSTEAD UPDATE buttons SET order = NEW.order WHERE id = NEW.id;
FOR form_id IN SELECT id FROM form LOOP
ALTER SEQUENCE buttons_order_seq RESTART;
UPDATE buttons_view SET order = nextval('buttons_order_seq') WHERE "formId" = form_id;
END LOOP;
DROP SEQUENCE buttons_order_seq;
DROP RULE buttons_rule ON buttons_view;
DROP VIEW buttons_view;
END; $$`);
// Create function that shifts orders
await knex.raw(`
CREATE FUNCTION shift_buttons_order()
RETURNS trigger AS
$$
BEGIN
IF NEW.order < OLD.order THEN
UPDATE buttons SET order = order + 1, "shiftOrderFlag" = NOT "shiftOrderFlag"
WHERE order >= NEW.order AND order < OLD.order AND "formId" = OLD."formId";
ELSE
UPDATE buttons SET order = order - 1, "shiftOrderFlag" = NOT "shiftOrderFlag"
WHERE order <= NEW.order AND order > OLD.order AND "formId" = OLD."formId";
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql'`);
// Create trigger to shift orders on update
await knex.raw(`
CREATE TRIGGER shift_buttons_order BEFORE UPDATE OF order ON buttons FOR EACH ROW
WHEN (OLD."shiftOrderFlag" = NEW."shiftOrderFlag" AND OLD.order <> NEW.order)
EXECUTE PROCEDURE shift_buttons_order()`);
One option that we came up with is to add the order to the form table.
keystone.createList(
'forms',
{
fields: {
name: {
type: Text,
isRequired: true,
},
buttonOrder: {
type: Text,
},
buttons: {
type: Relationship,
ref: 'buttons.attached_forms',
many: true,
},
},
}
);
This new field buttonOrder could contain a string representation of the order of the button Ids, like in a JSON stringified array.
The main issue with this is that it will be difficult to keep this field in-sync with the actual linked buttons.

AG-GRID Default Sort Model with Dynamic Column Defs

So I have a Grid setup that works with the Enterprise Row Model.
The columns are very dynamic and so the column defs are not known until the first query for rows is made to the server. This all works fine, but how can I set a default sort state when the column defs are not set until after the request has succeeded?
Once the grid has been set-up with the column defs you can just set the sort on any column
gridOptions.columnApi.getColumn(COLUMN_NAME).setSort("asc")
Adding a sort attribute to your colDef works too.
Example:
const columnDefs = [
{
headerName: 'Created Date',
field: 'CreateDate',
sort: 'desc',
sortingOrder: ['desc','asc'] //optional but for better sorting behaviour
}
]
Try this
const sort = [
{
colId: "firstName",
sort: "asc",
},
{
colId: "lastName"
},
];
this.gridApi.setSortModel(sort);