Updating non-data properties dynamically in a custom AG Grid cell renderer

In my grid setup, I have implemented an editor button in a column for each row and a new item creator button outside the grid.

One of the requirements is that all buttons should be disabled when either the create or edit button is clicked.

To achieve this, I created a custom cell renderer for the edit button with a property controlling the disabled state. However, I faced difficulty updating this property from outside the grid context.

The solution I found was to pass the isDisabled state as a function to the cell renderer, encapsulating the external value and utilizing Angular's expression handling in templates. Although this approach may lead to constant evaluation by Angular due to the non-deterministic nature of the value, it eventually updates as the wrapped external value changes.

For convenience, I have provided a sample at this link.

@Component({
  selector: 'app-ag-grid-edit-renderer',
  template: `
  <button
  class="ag-edit-button"
  (click)="onClick()"
  [disabled]="isDisabled()"
>
  Edit
</button>`,
})
export class AgGridEditRendererComponent implements AgRendererComponent {
  private params: any;

  isDisabled = () => false; // avoid this bad practice of constant evaluation

  agInit(params: any): void {
    this.params = params;
    this.isDisabled = params.isDisabled;
  }

  refresh(): boolean {
    return false;
  }

  onClick() {
    this.params.clicked(this.params.data);
  }
}

The grid setup includes:

export class AppComponent {
  isDisabled = false;
  currentVal: number | string = 0;

  columnDefs = [ 
    { field: "id", 
      cellRendererFramework: AgGridEditRendererComponent, 
      cellRendererParams: { 
        isDisabled: () => this.isDisabled,
        clicked: (params) => this.openEdit(params.id)
      }
    }, 
    { field: "make" }, { field: "model" }, { field: "price" }
  ];

  rowData = [
    { id: 1, make: "Toyota", model: "Celica", price: 35000 },
    { id: 2, make: "Ford", model: "Mondeo", price: 32000 },
    { id: 3, make: "Porsche", model: "Boxter", price: 72000 }
  ];

  openNew() {
    this.isDisabled = true;
    this.currentVal = 'new item';
  }

  openEdit(val: any) {
    this.isDisabled = true;
    this.currentVal = val;
  }

  closeDialog() {
    this.isDisabled = false;
  }
}

I attempted various methods to trigger cell or row refresh using the Grid API without success.

While considering options like using an Observable, I felt it would be too complex for this scenario.

Since this is a general cell renderer, simplicity is key, making solutions like extending the data model with special properties impractical.

Answer №2

Have you attempted to forcefully refresh it?

The problem you are encountering is that ag grid will not refresh if the cell's value has not changed, which is why you need to include force: true. Depending on the version of ag grid you are using, this may be implemented differently, but the concept remains the same - instruct ag-grid to refresh the cell even if the value has not changed.

    gridOptions.api.refreshCells({
      force: true,
      columns: ['id'], // specify colId of the column you want to refresh
    });
  columnDefs = [ 
    { field: "id",
      colId: 'id' // make sure to add this
      cellRendererFramework: AgGridEditRendererComponent, 
      cellRendererParams: { 
        isDisabled: () => this.isDisabled,
        clicked: (params) => this.openEdit(params.id)
      }
    }, 
    { field: "make" }, { field: "model" }, { field: "price" }
  ];

Additionally, ensure that you return true from the refresh callback.

export class AgGridEditRendererComponent implements AgRendererComponent {
  private params: any;

  isDisabled = () => false; // it is considered a bad practice as Angular needs to continuously evaluate its value

  constructor(
    private cdr: ChangeDetectorRef,
  ) {}

  agInit(params: any): void {
    this.init(params);
  }

  // relocate initialization logic to init method and execute it during both refresh and agInit
  refresh(params: any): boolean {
    this.init(params)
    return true;
  }

  onClick() {
    this.params.clicked(this.params.data);
  }

  private init(params: any) {
    this.params = params;
    this.isDisabled = params.isDisabled;
    this.cdr.markForCheck();
  }
}

Why do you think that having a function in the template is considered a bad practice? Your function simply returns a boolean value without any complex computation logic, so it should be acceptable. The issue arises when there is a loop or heavy logic within the function. Nonetheless, this can be mitigated by utilizing onPush change detection to minimize the number of Change Detection checks.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Show a modal component from another component in Angular 2

As a newcomer to Angular, I'm working on a modal component that changes from hiding to showing when a button with (click) is clicked. The goal is to integrate this modal into my main component, allowing me to display the modal on top of the main conte ...

When using the TypeScript && operator, the resulting type is not determined by the second operand

Several past discussions on SO have touched upon the concept that the inferred type from && is based on the last expression. TypeScript’s failure to detect union type with an && operator Uncovering the reason behind the && opera ...

Unable to implement new ecmascript decorators within typescript version 2.4.2

Check out this example code: function enumerable(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.enumerable = value; }; } class A { @enumerable(false) a: number = 1 b: number ...

What are the best ways to maximize a web worker's ability to handle multiple tasks at once

I'm currently working on implementing a Web-Worker to handle its state while also managing multiple asynchronous requests. worker.ts file let a =0; //state of the worker let worker=self as unknown as Worker; worker.onmessage =(e)=>{ console ...

Encountering an issue with top-level await in Angular 17 when utilizing pdfjs-dist module

While using the Pdfjs library, I encountered an error message that reads: Top-level await is not available in the configured target environment ("chrome119.0", "edge119.0", "firefox115.0", "ios16.0", "safari16.0" + 7 overrides) /****/ webpack_exports = g ...

How to link a selection choice to an option using Angular 13

Can anyone provide guidance on how to bind data with a select option in Angular? I've tried multiple approaches but haven't been successful in getting the data to display in the options. Any assistance would be greatly appreciated. Thank you in a ...

Testing in Vue revealed an unexpected absence of data

I am facing difficulties when it comes to creating tests. Currently, I have a view that is supposed to verify an email address using an authentication code. At the moment, the view exists but no connection is established with an email service or code gener ...

Is your React conditional rendering malfunctioning due to state issues?

I am attempting to create a component that will only be displayed after clicking on a search button. Below is the current code that I have: Update After making some changes, I am now encountering this error: Error: ERROR in /home/holborn/Documents/Work ...

Improve the way you manage the active selection of a button

ts isDayClicked: { [key: number]: boolean } = {}; constructor() { } setSelectedDay(day: string, index: number): void { switch (index) { case 0: this.isDayClicked[0] = true; this.isDayClicked[1] = false; this.isDay ...

What is the best way to split a single object into two separate objects based on a specific value within the object using lodash?

Imagine a scenario with an object containing two channels in Dutch (NL) language and one channel in English (EN) language: [ { "name": "De Redactie", "channels": [ { "name": "headlines", "pub ...

Using Angular to pass an index to a pipe function

Currently, I am attempting to incorporate the *ngFor index into my pipe in the following manner: <td *ngFor="let course of courses | matchesTime:time | matchesWeekday:i ; index as i">{{course.courseName}}</td> This is how my pipe is structure ...

The build error TS1036 is thrown when a function with a return statement is moved to index.d.ts, even though it worked perfectly in a standard TS file

In my project using Node v14.x and StencilJS (React) v2.3.x, I encountered an issue with a test-helper file containing a function that converts string-arrays to number-arrays: export function parseNumericStringOrStringArrayToIntegers(value: string | (strin ...

Limit function parameters to only accept values with matching keys

I am relatively new to using TypeScript and am currently working on a project that involves handling various shapes of data from different sources. My goal is to pass this data to different aggregator classes, with one aggregator class corresponding to eac ...

How do you go about making a prop optional in Typescript based on a generic type?

In my app, I have a specific type with optional props based on a generic type: type MyCustomType<R extends Record<string, string> | undefined, A extends string[] | undefined> = { record: R, array: A } There is a function that directly uses ...

Enhancing Vue functionality with vue-class-component and Mixins

In my Vue project, I am using vue-class-component along with TypeScript. Within the project, I have a component and a Mixin set up as follows: // MyComp.vue import Component, { mixins } from 'vue-class-component' import MyMixin from './mixi ...

TypeScript implementation of a reusable component for useFieldArray in React Hook Form

I'm currently working on a dynamic form component using react-hook-form's useFieldArray hook and facing issues with setting the correct type for field. In order to configure the form, I have defined a type and default values: export type NamesAr ...

Tips for utilizing an elective conversion by typing

In my opinion, utilizing code is the most effective approach to articulate my intentions: interface Input { in: string } interface Output { out: string } function doStuff(input: Input): Output { return { out: input.in }; } function f<Out>(input ...

Customizing CSS for tables within a mat-menu in Angular Material

I am currently developing an application using Angular 7 and Angular Material cdk 6. This is my first experience working with Angular Material and I am facing a challenge in overriding the CSS styles of my columns. Despite several attempts, none of them se ...

Inserting items into an array entity

I am attempting to insert objects into an existing array only if a certain condition is met. Let me share the code snippet with you: RequestObj = [{ "parent1": { "ob1": value, "ob2": { "key1": value, "key2": va ...

The issue with Multiselect arises when the array is being set up initially

Using primeng Multiselect, I have implemented a logic to push data based on search value from the backend. To avoid the error of pushing undefined elements, initialization is required before pushing. However, when I initialize the dropdown's array var ...