Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to…

Follow publication

Angular: Using Reactive Forms within Ag-Grid tables and performing CRUD operations on the table

--

Performing CRUD operation on a simple table using reactive forms is quite straight forward. Below is the story for the same, where have taken a ToDo list application as an example

In this story, I will use the same example but in an Ag-Grid table. At the end of the story, I have provided a more complex example involving author and book selection.

Below is a short demonstration of the ToDo list application in ag-grid table.

Updating a todo item and Adding a new todo item
Deleting todo item from table

Lets begin !

Below is the data that we are going to use. Its an array of 20 objects exported from todos.ts.

I. Displaying the initial list of 20 ToDo items

  1. We have placed the <ag-grid-angular> element within the <form> as below.
<form [formGroup]=”todosFormGroup”>
<ag-grid-angular
#gdTable
[columnDefs]="columnDefs"
[defaultColDef]="defaultColDef"
class="ag-theme-alpine"
[rowData]="rowData"
[gridOptions]="gridOptions"
(gridReady)="onGridReady($event)"
(firstDataRendered)="dataRendered($event)"
style="width:100vh;height:100vw"
(rowDataChanged)="refreshFormControls()"
>
</ag-grid-angular>
</form>

2. Inside the constructor of the component class, we have set the rowData to the list of 20 ToDo items exported from todos.ts.

We have also initialized the todosFormGroup to a new FormGroup and added a new control todoList. The todoList control is initialized to a FormArray. The contents of the FormArray is determined by the loadData() which we will see next.

constructor() {
this.rowData = todoList;

this.todosFormGroup = new FormGroup({
todoList: new FormArray(this.loadData()),
});

this.todosFormGroup.valueChanges.subscribe((result) => {
this.rowData = [...result.todoList];
if (this.gridApi) {
this.gridApi.setRowData(this.rowData); //refreshFormControls will be called
}
});

}

In order to keep the reactive form value and the rowData in sync, we are subscribing to the valueChanges observable and setting the rowData to latest value of the form. After updating the rowData, we are also calling setRowData() on the gridApi to make the changes reflect in the grid table.

3. Inside the loadData(), we have mapped each ToDo item inside the rowData to a new FormGroup returned by the createToDoItem().

loadData() {
return this.rowData.map((todo: ToDoModel) => {
return this.createToDoItem(todo);
});
}

Inside the createToDoItem(), we have mapped each ToDo item to a FormGroup with 5 FormControls.

createToDoItem(todo: ToDoModel) {
return new FormGroup({
id: new FormControl(todo.id),
title: new FormControl(todo.title),
completed: new FormControl(todo.completed),
priority: new FormControl(todo.priority),
});
}

4. Inside the ngOnInit() lifecycle hook, we have initialized the column definitions. We are using the same <ng-template> with reference columnTemplate to display the content of all the columns.

We have created a new component: ReusableGridTemplateComponent, where the template defined within <ng-template> with reference columnTemplate will be displayed.

ngOnInit() {

this.columnDefs = [
{
headerName: ‘Title’,
cellRendererFramework: ReusableGridComponent,
cellRendererParams: {
ngTemplate: this.columnTemplate,
},
},
{
headerName: ‘Priority’,
cellRendererFramework: ReusableGridComponent,
cellRendererParams: {
ngTemplate: this.columnTemplate,
},
},
{
headerName: ‘Completed’,
cellRendererFramework: ReusableGridComponent,
cellRendererParams: {
ngTemplate: this.columnTemplate,
},
},
{
headerName: ‘Action’,
cellRendererFramework: ReusableGridComponent,
cellRendererParams: {
ngTemplate: this.columnTemplate,
},
},
];
}

ReusableGridTemplateComponent Template:

<ng-container [ngTemplateOutlet]=”template” [ngTemplateOutletContext]=”context”
>
</ng-container>

The variable template (passed to the ngTemplateOutlet) will contain the TemplateRef (columnTemplate) passed from the ColumnDefs.

To understand what the variable context (passed to the ngTemplateOutletContext) contains, let’s check the class.

Below is the ReusableGridComponent class.

As seen before in the columnDefs, the template reference was passed to a property ngTemplate in the cellRendererParams object.

{
headerName: 'Priority',
cellRendererFramework: ReusableGridComponent,
cellRendererParams: {
ngTemplate: this.columnTemplate,
},

Hence the variable template (passed to the ngTemplateOutlet)is defined as below in the setTemplateAndParams(). It gets its value from the ngTemplate property of the params.

  this.template = params['ngTemplate'];
this.context = {
rowInfo: {
rowData: params.data,
rowId: params.node.id,
columnName: params.colDef.headerName,
},
};

The variable context (passed to the ngTemplateOutletContext) is an object which contains a property rowInfo. This property rowInfo in turn is another object with 2 properties rowData,columnName and rowId. You can pass even more properties depending on the row information you require in the <ng-template>.

Please do check my story if you want a more detailed example on how this works.

5. Looking at the <ng-template>, based on the column name, we are able to differentiate the content to be displayed for each column. Also we need to differentiate the data in the rows based on some unique id. The id assigned to each ToDo item is unique and is used for differentiating between the rows.

II. Adding and Deleting ToDo items

When we click on the “Add ToDo” button, we are executing the below method add().

add() {

const newToDo: ToDoModel = {
id: this.toDoCount++,
userId: 1,
title: ‘Grocery’,
completed: false,
priority: 0,
};

(<FormArray>this.todosFormGroup.get(‘todoList’)).push(
this.createToDoItem(newToDo)
);

}

=>Here we have created a new ToDo item: newToDo. The id assigned to the item gets incremented(starting 21) every time we click on the button to keep it unique.

const newToDo: ToDoModel = {
id: this.toDoCount++,
userId: 1,
title: ‘Grocery’,
completed: false,
priority: 0,
};

=>We also need to add a new FormGroup(representing the new item) to the todoList FormArray.

(<FormArray>this.todosFormGroup.get(‘todoList’)).push(
this.createToDoItem(newToDo)
);

=>Whenever any changes are made to todosFormGroup, the valueChanges observable triggers, which keeps the rowData in sync with the form. Also we are calling setRowData() on the gridApi to make the changes reflect in the table.

this.todosFormGroup.valueChanges.subscribe((result) => {
this.rowData = [...result.todoList];
if (this.gridApi) {
this.gridApi.setRowData(this.rowData); //refreshFormControls will be called
}
});

=>Updating the rowData, triggers the rowDataChanged event on the table, which in turn calls the refreshFormControls().

refreshFormControls() {
if (this.gridApi) {
this.gridApi.refreshCells({ force: true });
}
}

Clicking on the “Delete” button corresponding to a ToDo item, calls the below delete(), passing the row node id as argument. We use this row node id to remove the specific ToDo item from rowData and the todoList FormArray. As discussed earlier, whenever any changes are made to todosFormGroup, the valueChanges observable triggers, which keeps the rowData in sync with the form. Also calling the setRowData(), causes the changes to reflect in the table.

delete(rowIndex: number) {
(<FormArray>this.todosFormGroup.get(‘todoList’)).removeAt(rowIndex);
}

Below is the working example for the same:

III. More Complex Example

As mentioned in the beginning of the story, below are demonstrations of a slightly more complex example. I have labelled the below example as “complex” because of the presence of an array of objects i.e usage of FormGroups within a FormArray and also the content of a column updates based on the changes in another column.

I have displayed(READ) details of 2 book genre’s: Horror and Science & Fiction. I can select(UPDATE) an author from each of the genre’s, view the books written by the selected authors and also select(UPDATE) 1 or more books written by the selected author.

In the demo below, I am able to add(CREATE) a new genre: Comedy to the table. Observe that adding a new row, doesnt impact the state of the other rows.

Adding new book genre and updating existing book genre

In the below demo, I am able to DELETE 1 or more book genre’s. Observe that deleting a row, doesn’t impact the state of the other rows.

Deleting book genre

Below is the working example for the same:

Stackademic 🎓

Thank you for reading until the end. Before you go:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.

Written by Angular&NodeEnthusiast

Loves Angular and Node. I wish to target issues that front end developers struggle with the most.

No responses yet

Write a response