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

Is it possible to detach keyboard events from mat-chip components?

Is there a way to allow editing of content within a mat-chip component? The process seems simple in HTML: <mat-chip contenteditable="true">Editable content</mat-chip> Check out the StackBlitz demo here While you can edit the content within ...

When attempting to dispatch an action with switchMap, it fails, while map successfully triggers the

I recently started utilizing ngrx and ngrx/effects. In the code snippet below, I encountered an issue where using switchMap with concatLatestFrom and dispatching an action fails, while using map seems to work perfectly fine. Any idea why? When trying to ...

Merging an assortment of items based on specific criteria

I have the following TypeScript code snippet: interface Stop { code: string } interface FareZone { name: string; stops: Stop[]; } const outbound: FareZone[] = [{name: 'Zone A', stops: [{ code: 'C00'}] }, {name: 'Zone B ...

Getting the version from package.json in Next.js can be easily achieved by accessing the `version

In my quest to retrieve the "version" from the package.json in a Next.js application, I encountered a roadblock. I attempted using process.env.npm_package_version, similar to how it is done in a Node application, but unfortunately, it returned undefined. ...

What exactly does <T> signify within the realm of dynamic forms?

Currently, I am experimenting with the Angular2 cookbook instructions for Dynamic Forms. The instructions mention: export class QuestionBase<T>{ value: T, ... I am confused about the purpose of the "<T>" in both instances. Do you have any ins ...

Obtaining a Child Component Reference in Angular 5 Testing

I am exploring the interaction dynamics between a host component and a child component within an Angular application. One challenge I'm facing is obtaining a reference to the child component that is created when the parent component is initialized. Th ...

Tips for creating dynamic alerts using mui v5 Snackbar

My goal is to call an API and perform several actions. After each action, I want to display the response in a Snackbar or alert. Despite iterating through the messages in a map, I'm only able to show the first response and none of the others. Here is ...

Having trouble sending an email using nodejs and mailgun

Before accusing me of asking a duplicate question, I want to clarify that I have already searched for solutions and none of them worked for me. For example, I tried the solution provided in this link: Example of the domain name for mailgun before nodejs? ...

Encountering a TSLint interface error when adding a value to a nested array

Currently, I am transforming responses into an array of Months and Days, with each Day containing an array of numbers. The logic itself is functioning properly, however, I am encountering a tslint error specifically when attempting to push a value into the ...

Guide on creating a static method to generate a subclass instance

I am currently working on creating an abstract class called Enum, which consists of static methods that return instances of the class they are invoked upon. However, I am encountering difficulties in properly typing these methods. My goal is to help TypeS ...

Utilize mapping to object and preserve type inference

I am currently developing a function that utilizes a map function to map objects. interface Dictionary<T> { [key: string]: T; } function objectMap<TValue, TResult>( obj: Dictionary<TValue>, valSelector: (val: TValue) => TResult ...

Unexpected Union Type Return from Apollo Server

When I call my resolver to return a union type (either a User or an object with a message key and value of type String, such as my UserNotFoundError type), it always comes back with "__typename": "User". Why is this happening and how ca ...

Adding Images Using Angular 8

I'm encountering difficulties with image upload in the file located at '../src/app/assets/'. Below is the Form I am using: <form [formGroup]="formRegister" novalidate=""> <div class="form-group"> <label for="ex ...

Having trouble getting the installed datejs typings to work properly

As I delve into Typescript due to my interest in Angular 2, I have come across the datejs Javascript library. To incorporate it into my Angular 2 project, I went ahead and installed datejs via npm, ensuring that it is correctly listed in my package.json. A ...

Sharing data between components in Angular 4: How to pass variables and form content to

Perhaps my wording is incorrect. I am filling out a Client Form with the data from an API response within the NgOnInit function. ngOnInit() { this.indexForm = new FormGroup({ name: new FormControl(Validators.required), status: new FormCo ...

Gather keyboard information continuously

Currently working with Angular 10 and attempting to capture window:keyup events over a specific period using RXJS. So far, I've been facing some challenges in achieving the desired outcome. Essentially, my goal is to input data and submit the request ...

Transferring data from the server to the client side in Next JS with the help of getInitialProps

I am currently developing an application using nextJS. Within server/index.ts, I have the following code: expressApp.get('/', (req: express.Request, res: express.Response) => { const parsedUrl = parse(req.url, true); const { query } = ...

Assigning a Value to a Dropdown Menu in Angular

I have some JSON data that contains a True/False value. Depending on whether it is true or false, I want a specific option in a Select Dropdown to be automatically selected. This is the HTML code using Angular 16: <select name="reportNo" id=& ...

When working in React, I often encounter the frustrating TypeError: Cannot read property 'content' of undefined error

Trying to customize a React project, I attempted to add a class to a div using the following code: <div className={styles.content}> In the case of deleting the Data Source, you will lose all configuration sett ...

Displaying messages in an Angular 2 chatbox from the bottom to the top

As a newcomer to using typescript, I am currently working on an angular 2 project and facing some challenges with creating a chatbox. My goal is to have new messages displayed at the bottom while pushing the old ones up one line at a time, as shown in this ...