Implementing CRUD Operations in Master and Detail Grid
Environment
| Product | Progress® Kendo UI for Angular Grid |
Description
I need to enable full CRUD (Create, Read, Update, Delete) operations in a master-detail Grid where both the master and the detail Grid support editing. How can I ensure that changes made in the detail Grid persist when collapsing and re-expanding the detail rows?
This Knowledge Base article also answers the following questions:
- How to edit data in both master and detail Grids?
- How to synchronize data between master and detail Grids?
- How to ensure changes persist when collapsing/expanding detail rows?
- How to use inline editing in a hierarchical Grid structure?
Solution
To implement CRUD operations in both the master and detail Grids with proper data synchronization, use custom editing services that implement the EditService interface. This approach ensures that changes persist across expand/collapse operations.
The following example demonstrates the full implementation of the suggested approach.
Follow these steps to achieve the implementation:
-
Create custom editing services that implement the
EditServiceinterface for both the master and detail Grids.For complete details on implementing custom editing services, refer to the Custom Editing Service documentation.
-
Declare the data collections outside the service classes so all instances share the same data. This ensures that when multiple service instances are created (one for each expanded detail row), they all reference and modify the same underlying data collections.
typescript// Data storage const categories: Category[] = []; const products: Product[] = []; @Injectable() export class CategoryEditService extends BehaviorSubject<Category[]> implements EditService { // Service methods reference the categories array (master Grid) } @Injectable() export class ProductEditService extends BehaviorSubject<Product[]> implements EditService { // Service methods reference the products array (detail Grid) } -
Configure the master Grid with the
kendoGridReactiveEditingdirective to enable inline editing and bind the respective editing service using theeditServiceinput.typescript@Component({ providers: [CategoryEditService], template: ` <kendo-grid [kendoGridReactiveEditing]="createCategoryFormGroup" [editService]="categoryEditService" [data]="view | async" > ... </kendo-grid> ` }) export class AppComponent implements OnInit { public view: Observable<Category[]>; constructor(public categoryEditService: CategoryEditService) {} } -
Define a
trackByfunction for the master Grid to track changes by a unique field, thus ensuring proper detail Grid references during CRUD operations.TSpublic trackByItem(index: number, item: GridItem): any { return item.data['CategoryID']; } -
Add a detail template with a condition in the master Grid using the
kendoGridDetailTemplateShowIfdirective to display a detail Grid only when the parent row has related data.HTML<kendo-grid ...> <ng-template kendoGridDetailTemplate let-dataItem [kendoGridDetailTemplateShowIf]="hasProducts"> <category-details [category]="dataItem"></category-details> </ng-template> </kendo-grid> -
Create a separate component for the detail Grid that receives the parent row data as an input. Bind the input to the
let-dataItemvariable from the detail template.typescript@Component({ selector: 'category-details', template: ` <kendo-grid> </kendo-grid> ` }) export class CategoryDetailComponent { @Input() public category: Category; } -
Register the detail editing service at the component level to create a separate instance for each detail Grid. This allows each detail Grid to manage CRUD operations independently while all service instances reference the same underlying data collection.
typescript@Component({ selector: 'category-details', providers: [ProductEditService], ... }) export class CategoryDetailComponent { constructor(public productEditService: ProductEditService) {} } -
In a similar way, enable inline editing in the detail Grid and use the
editServiceinput to bind the detail editing service.HTML<kendo-grid [kendoGridReactiveEditing]="createProductFormGroup" [editService]="productEditService" [data]="view | async" > ... </kendo-grid> -
Use the parent row data (received from the
let-dataItemvariable in the detail template) to load the detail data specific to that parent row.typescriptngOnInit(): void { this.view = this.productEditService; this.productEditService.setCategoryId(this.category.CategoryID); this.productEditService.read(); }