{"id":3114,"date":"2025-10-03T10:17:07","date_gmt":"2025-10-03T10:17:07","guid":{"rendered":"https:\/\/www.infragistics.com\/blogs\/?p=3114"},"modified":"2025-10-07T06:05:54","modified_gmt":"2025-10-07T06:05:54","slug":"on-demand-data-loading","status":"publish","type":"post","link":"https:\/\/www.infragistics.com\/blogs\/on-demand-data-loading","title":{"rendered":"On-Demand Data Loading in Master-Detail Grid Layouts\u202f\u00a0"},"content":{"rendered":"\n<p>In our previous article, <a href=\"https:\/\/www.infragistics.com\/blogs\/master-detail-layout\/\" target=\"_blank\" rel=\"noreferrer noopener\">Getting Started with Master-Detail Layout Using Ignite UI for Angular Grid<\/a>\u202f, we explored how to set up a clean and efficient master-detail interface. This pattern is ideal for presenting related records, such as orders with items or departments with employees, without cluttering the screen.&nbsp;<\/p>\n\n\n\n<p>But what happens when your dataset is huge? Loading all the details for all records at once is inefficient. That\u2019s where on-demand data loading comes in. Instead of fetching every possible detail upfront, you only load the data when the user expands a row, leading to faster initial renders, smoother interactions, and improved scalability.&nbsp;<\/p>\n\n\n\n<p>In this article, we\u2019ll dive into why on-demand loading matters, how to implement it in the <a href=\"https:\/\/www.infragistics.com\/products\/ignite-ui-angular\/angular\/components\/grids-and-lists\" target=\"_blank\" rel=\"noreferrer noopener\">Ignite UI for Angular Grid component<\/a>, and best practices to make the most of it.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"extending-the-master-detail-template\">Extending the Master-Detail Template&nbsp;<\/h2>\n\n\n\n<p>If you\u2019ve followed our first article, you already know how to set up a master grid with expandable rows and a detail template, and where to display data&nbsp; from:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The same dataset &#8211; For example, orders and their associated items are bundled together&nbsp;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>An external dataset &#8211; for example, a service that takes customerID as a parameter and calls the API to retrieve customer orders data.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>For large datasets, the second approach (external detail fetching) is where on-demand loading proves most effective.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"setting-up-the-angular-project\">Setting Up the Angular Project\u202f&nbsp;<\/h2>\n\n\n\n<p>Before we jump into the on-demand loading implementation, let\u2019s prepare an Angular workspace for the demo. Here are the steps.&nbsp;<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Create a new Angular project<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>If you don\u2019t already have one, start by generating a new Angular application using the Angular CLI and open the project in your preffered IDE.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ng new load-on-demand-grid\u00a0\ncd load-on-demand-grid\u00a0<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Install Ignite UI for Angular<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>We\u2019ll be using the <a href=\"https:\/\/www.infragistics.com\/products\/ignite-ui-angular\" target=\"_blank\" rel=\"noreferrer noopener\">Ignite UI for Angular component library<\/a>, which provides a rich set of UI components &#8211; most importantly the igxGrid, the foundation for our master-detail and on-demand loading examples. Add it to your project with:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ng add igniteui-angular\u00a0<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Create a demo component<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>To keep everything organized, generate a dedicated Angular component where we\u2019ll build the grid demo:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ng generate component pages\/grid-demo\u00a0<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>Set up routing for the demo<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>Finally, adjust your routing configuration so the demo component is the default view. Replace your existing app.routes.ts (or your routing module) with a minimal setup.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import { Routes } from '@angular\/router';  \nexport const routes: Routes = [  \n  { path: '', redirectTo: '\/demo', pathMatch: 'full' },  \n  { path: 'demo', loadComponent: () => import('.\/pages\/grid-demo\/grid-demo.component').then(m => m.GridDemoComponent) },  \n  { path: '**', redirectTo: '\/demo' }  \n]; <\/pre>\n\n\n\n<p>Add provideHttpClient to the providers in app.config.ts.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">export const appConfig: ApplicationConfig = { \n    providers: [\n\tprovideZoneChangeDetection({ eventCoalescing: true }),\n\tprovideRouter(routes),\n\tprovideAnimations(),\n\tprovideHttpClient()\n    ]\n}; <\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"setting-up-the-data\">Setting Up the Data\u202f&nbsp;<\/h2>\n\n\n\n<p>Before implementing on-demand loading in the <a href=\"https:\/\/www.infragistics.com\/products\/ignite-ui-angular\/angular\/components\/grid\/master-detail\" target=\"_blank\" rel=\"noreferrer noopener\">master-detail grid<\/a>, we need a reliable data layer. Preparing your data models and services ensures the grid displays meaningful content and can fetch related details as users interact with rows.&nbsp;<\/p>\n\n\n\n<p>Your data for the IgxGrid can come from various sources:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Static\/local data &#8211; JSON files in the assets folder.&nbsp;<\/li>\n\n\n\n<li>Remote APIs &#8211; fetched via Angular\u2019s HttpClient from a backend service.&nbsp;<\/li>\n\n\n\n<li>Mocked data &#8211; generated directly in a service during development.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>For this example, we\u2019ll connect to a custom remote Northwind Swagger API, which provides Customers and their related Orders. The concepts remain the same regardless of your data source structure.\u202f&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Define Data Models&nbsp;<\/h3>\n\n\n\n<p>To enforce type safety and clarity, define TypeScript interfaces representing your data in a dedicated models.ts file. These models mirror the structure of the API responses and make it easy to bind data to the grid.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">export interface AddressDto { \n  street?: string; \n  city?: string; \n  region?: string; \n  postalCode?: string; \n  country?: string; \n  phone?: string; \n} \nexport interface CustomerDto { \n  customerId?: string; \n  companyName: string; \n  contactName?: string; \n  contactTitle?: string; \n  address?: AddressDto; \n} \nexport interface OrderDto { \n  orderId: number; \n  customerId: string; \n  employeeId: number; \n  shipperId?: number; \n  orderDate?: Date; \n  requiredDate?: Date; \n  shipVia?: string; \n  freight: number; \n  shipName?: string; \n  completed: boolean; \n  shipAddress?: AddressDto; \n} <\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Data Service&nbsp;<\/h3>\n\n\n\n<p>Next, centralize all API calls in a dedicated service &#8211; for example, northwind-swagger.service.ts. We\u2019ll also add basic error handling to keep the app resilient.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const API_ENDPOINT = 'https:\/\/data-northwind.indigo.design'; \n@Injectable({ \n  providedIn: 'root' \n}) \nexport class NorthwindSwaggerService { \n  constructor( \n  private http: HttpClient \n  ) { } \n  public getCustomerDtoList(): Observable&lt;CustomerDto[]> { \n    return this.http.get&lt;CustomerDto[]>(`${API_ENDPOINT}\/Customers`) \n      .pipe(catchError(this.handleError&lt;CustomerDto[]>('getCustomerDtoList', []))); \n  }\n  public getOrderDtoList(id: string): Observable&lt;OrderDto[]> { \n    return this.http.get&lt;OrderDto[]>(`${API_ENDPOINT}\/Customers\/${id}\/Orders`) \n      .pipe(catchError(this.handleError&lt;OrderDto[]>('getOrderDtoList', []))); \n  }\n  private handleError&lt;T>(operation = 'operation', result?: T) {  \n    return (error: any): Observable&lt;T> => {  \n      console.error(`${operation} failed: ${error.message}`, error); \n      return of(result as T);  \n  };\n }  \n} <\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Bind the Grid to Customer Data&nbsp;<\/h3>\n\n\n\n<p>With the models and service in place, the next step is to display the Customers dataset in the grid. The grid\u2019s data property is bound to a northwindSwaggerCustomerDto array, which is populated when the component initializes by fetching data from the service.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;igx-grid [data]=\"northwindSwaggerCustomerDto\" primaryKey=\"customerId\" [allowFiltering]=\"true\" filterMode=\"excelStyleFilter\" class=\"grid\"> \n  &lt;igx-column field=\"customerId\" dataType=\"string\" header=\"customerId\" [filterable]=\"true\" [sortable]=\"true\" [selectable]=\"false\">&lt;\/igx-column> \n  &lt;igx-column field=\"companyName\" dataType=\"string\" header=\"companyName\" [filterable]=\"true\" [sortable]=\"true\" required=\"true\" [maxlength]=\"100\" [selectable]=\"false\">&lt;\/igx-column> \n  &lt;igx-column field=\"contactName\" dataType=\"string\" header=\"contactName\" [filterable]=\"true\" [sortable]=\"true\" [maxlength]=\"50\" [selectable]=\"false\">&lt;\/igx-column>\n&lt;\/igx-grid>\n<\/pre>\n\n\n\n<p>On the TypeScript side, add a service subscription in the GridDemoComponent class to retrieve the customers data and store it in the northwindSwaggerCustomerDto property. Then bind the grid data input property to `northwindSwaggerCustomerDto `.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Component({ \n  selector: 'app-grid-demo', \n  standalone: true,\n    imports: [IgxCheckboxComponent, IgxSimpleComboComponent, IgxGridComponent, IgxColumnComponent, IgxColumnMaxLengthValidatorDirective, NgIf, AsyncPipe, IgxGridDetailTemplateDirective],\n  templateUrl: '.\/grid-demo.component.html',\n  styleUrl: '.\/grid-demo.component.css'\n})\nexport class GridDemoComponent implements OnInit, OnDestroy{\n  private destroy$ = new Subject&lt;void>();\n  public northwindSwaggerCustomerDto: CustomerDto[] = [];\n  constructor(private northwindSwaggerService: NorthwindSwaggerService) {}\n  ngOnInit() {\n    this.northwindSwaggerService\n      .getCustomerDtoList()\n      .pipe(takeUntil(this.destroy$))\n      .subscribe(data => (this.northwindSwaggerCustomerDto = data));\n  }\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}<\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"501\" src=\"https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-1.png\" alt=\"angular on-demand data loading\" class=\"wp-image-3192\" srcset=\"https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-1.png 828w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-1-300x182.png 300w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-1-768x465.png 768w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-1-480x290.png 480w\" sizes=\"auto, (max-width: 828px) 100vw, 828px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"designing-the-detail-template-implementing-load-on-demand\">Designing the Detail Template &amp; Implementing Load-on-Demand&nbsp;<\/h2>\n\n\n\n<p>When the master grid presents many parent rows (customers), it\u2019s inefficient to fetch every related child list (orders) for every row up front. Instead, load the details only when the user expands a row. This is the essence of on-demand loading: request child data only when it\u2019s needed, then show it in the detail template.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How It Works?&nbsp;<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Put an igxGridDetail template inside the master igx-grid.&nbsp;<\/li>\n\n\n\n<li>In that template, call a method (e.g. getOrders(customerId)) that returns an Observable&lt;OrderDto[]&gt;.&nbsp;<\/li>\n\n\n\n<li>Use the async pipe to subscribe to that observable in the template so Angular renders results once available.&nbsp;<\/li>\n\n\n\n<li>Cache each customer\u2019s observable\/result so multiple expansions (or rapid change detection cycles) don\u2019t trigger new HTTP calls.&nbsp;<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">How Do We Implement It?&nbsp;<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Add the detail template<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>First, we define a placeholder for the detail content by adding an ng-template with the igxGridDetail directive. This marks where the detail view for each expanded row will render:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;ng-template igxGridDetail let-rowData>&lt;\/ng-template>\u00a0<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Add a combo bound to orders (loaded on demand)<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>Inside the template, we can render any component we want. In this example, we\u2019ll add a combo box to display the orders for the selected customer. The key point here is that the orders are not loaded until the row is expanded.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> &lt;ng-container *ngIf=\"getOrders(rowData.customerId) | async as orders\">\n      &lt;div class=\"row-layout group\">\n          &lt;igx-simple-combo\n            type=\"border\"\n            [data]=\"orders\"\n            displayKey=\"orderId\"\n            class=\"single-select-combo\">\n            &lt;label igxLabel>Order Id&lt;\/label>\n          &lt;\/igx-simple-combo>\n        &lt;\/div>\n    &lt;\/ng-container><\/pre>\n\n\n\n<p>The first time a row is expanded, the getOrders(customerId) method makes an HTTP call and returns an observable. Because of using the async pipe, Angular subscribes automatically and renders the combo once the data arrives.&nbsp;<\/p>\n\n\n\n<p>A very important note here is that when we have a ng-template, Angular will keep loading data into this template every frame (change detection). If this data is loaded from a request, this means that for every visible template few requests will be made every second until the application inevitably freezes. This is why data needs to be cached after fetching.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">private ordersCache = new Map&lt;string, Observable&lt;OrderDto[]>>(); \ngetOrders(customerId: string): Observable&lt;OrderDto[]> { \n    if (!this.ordersCache.has(customerId)) { \n      const request$ = this.northwindSwaggerService \n        .getOrderDtoList(customerId) \n        .pipe(take(1), shareReplay(1)); \n      this.ordersCache.set(customerId, request$); \n    } \n    return this.ordersCache.get(customerId)!; \n  }<\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"501\" src=\"https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-2.png\" alt=\"On-Demand Data Loading with ignite ui\" class=\"wp-image-3193\" srcset=\"https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-2.png 828w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-2-300x182.png 300w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-2-768x465.png 768w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-2-480x290.png 480w\" sizes=\"auto, (max-width: 828px) 100vw, 828px\" \/><\/figure>\n\n\n\n<p>A couple of things are happening here:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We cache the observable in a Map, keyed by customerId. This ensures that once data has been fetched for a row, the same observable is reused instead of triggering new HTTP calls.&nbsp;<\/li>\n\n\n\n<li>take(1) guarantees the observable completes after the first response, which makes subscription management clean and avoids memory leaks.&nbsp;<\/li>\n\n\n\n<li>shareReplay(1) ensures that if multiple subscribers (or change detection cycles) request the same data, only one HTTP request is sent. It also replays the result to late subscribers (e.g., when the row is collapsed and expanded again).&nbsp;<\/li>\n\n\n\n<li>Then we store the data in ordersCache and return it&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>By combining caching with shareReplay, we make sure each customer row triggers at most one request, no matter how many times it is expanded or how often Angular runs change detection.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"displaying-the-details-for-the-selected-order\">Displaying the Details for the Selected Order&nbsp;<\/h2>\n\n\n\n<p>So far, we\u2019ve shown how to expand a customer row and fetch the list of orders on demand. Next, we want the user to select a specific order from that list and display its details right under the row.&nbsp;<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Add a selection event to the combo<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>We extend the combo box to notify us whenever a new order is selected. This is done by handling the selectionChanging event:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;igx-simple-combo \n        type=\"border\" \n        [data]=\"orders\" \n        displayKey=\"orderId\" \n        (selectionChanging)=\"onOrderSelectionChange(rowData.customerId, $event.newValue)\" \n        class=\"single-select-combo\" \n      > \n        &lt;label igxLabel>Order Id&lt;\/label> \n      &lt;\/igx-simple-combo><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Track the selected order per customer<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>We use a Map&lt;string, OrderDto&gt; to keep track of the selection, keyed by the customerId. This ensures that each expanded row remembers its own selected order:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public selectedOrders = new Map&lt;string, OrderDto>(); \nonOrderSelectionChange(customerId: string, order: OrderDto) { \n    this.selectedOrders.set(customerId, order); \n} \ngetSelectedOrder(customerId: string): OrderDto | undefined { \n    return this.selectedOrders.get(customerId); \n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Show order details<\/strong>&nbsp;<\/li>\n<\/ol>\n\n\n\n<p>Finally, once an order is selected, we can render its details in the expanded template. By calling getSelectedOrder(customerId), we retrieve the saved selection and display its fields:&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;div *ngIf=\"getSelectedOrder(rowData.customerId) as selectedOrder\" class=\"column-layout group_1\"> \n        &lt;h6 class=\"h6\">Order Details&lt;\/h6> \n        &lt;div class=\"row-layout group_2\"> \n          &lt;div class=\"column-layout group_3\"> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">Completed&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">Shipper&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">Order Date&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">Country&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">City&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">Street&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_5\">&lt;p class=\"text\">Postal Code&lt;\/p>&lt;\/div> \n          &lt;\/div> \n          &lt;div class=\"column-layout group_6\"> \n            &lt;div class=\"row-layout group_7\">&lt;igx-checkbox [checked]=\"!!selectedOrder?.completed\" class=\"checkbox\">&lt;\/igx-checkbox>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">{{ selectedOrder?.shipperId }}&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">{{ selectedOrder?.orderDate }}&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">{{ selectedOrder?.shipAddress?.country }}&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">{{ selectedOrder?.shipAddress?.city }}&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_4\">&lt;p class=\"text\">{{ selectedOrder?.shipAddress?.street }}&lt;\/p>&lt;\/div> \n            &lt;div class=\"row-layout group_5\">&lt;p class=\"text_1\">{{ selectedOrder?.shipAddress?.postalCode }}&lt;\/p>&lt;\/div>\u00a0\n          &lt;\/div> \n        &lt;\/div> \n      &lt;\/div> \n    &lt;\/div><\/pre>\n\n\n\n<p>\u202f \u202fThis gives us a simple yet functional detail view: when the user expands a customer row, they can pick an order from the combo, and the order details are displayed immediately underneath.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"501\" src=\"https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-3.png\" alt=\"angular on-demand data loading demo image\" class=\"wp-image-3194\" srcset=\"https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-3.png 828w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-3-300x182.png 300w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-3-768x465.png 768w, https:\/\/www.infragistics.com\/blogs\/wp-content\/uploads\/2025\/10\/ignite-ui-grid-3-480x290.png 480w\" sizes=\"auto, (max-width: 828px) 100vw, 828px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"cache-invalidation-refresh-strategies\">Cache Invalidation &amp; Refresh Strategies&nbsp;<\/h2>\n\n\n\n<p>A cache that never expires can become stale or grow unbounded. You can choose to implement a strategy that fits your app:&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Manual Refresh&nbsp;<\/h3>\n\n\n\n<p>Add a \u201cRefresh\u201d button in the detail template calling refreshOrders(customerId) that replaces the cache entry.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">refreshOrders(customerId: string) { \n  const request$ = this.northwindSwaggerService.getOrderDtoList(customerId).pipe(take(1), shareReplay(1)); \n  this.ordersCache.set(customerId, request$); \n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Clear on Collapse&nbsp;<\/h3>\n\n\n\n<p>Clear the cached observable when the row is collapsed. This ensures a fresh request next time the user expands:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;igx-grid (rowToggle)=\"onRowToggle($event)\"...> <\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">onRowToggle(event: IRowToggleEventArgs) { \n    if (this.ordersCache.size > 0 &amp;&amp; this.ordersCache.has(event.rowID)) { \n      this.ordersCache.delete(event.rowID); \n    } \n  }<\/pre>\n\n\n\n<p>We\u2019ve set up the master grid, implemented on-demand detail loading, cached results to avoid redundant requests, and even discussed refresh strategies.&nbsp;<\/p>\n\n\n\n<p>Now let\u2019s look at additional considerations and best practices that will help you scale this pattern to larger datasets and production scenarios.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"handling-large-detail-data\">Handling Large Detail Data&nbsp;<\/h2>\n\n\n\n<p>When the detail dataset itself is large, fetching and rendering everything at once can hurt performance. Instead, we use one of these strategies:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Client or <a href=\"https:\/\/www.infragistics.com\/products\/ignite-ui-angular\/angular\/components\/grid\/paging\">Server-side paging<\/a>: Fetch only a slice of the data at a time by passing page and pageSize parameters to your orders API.<\/li>\n\n\n\n<li>Load-On-Demand: Fetching only the data that is needed at the moment&nbsp;<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"memory-lifecycle-considerations\">Memory &amp; Lifecycle Considerations&nbsp;<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Clear caches in ngOnDestroy() to avoid memory leaks: this.ordersCache.clear().&nbsp;<\/li>\n\n\n\n<li>If you keep cached data (not just observables), consider limiting total memory.&nbsp;<\/li>\n\n\n\n<li>Avoid storing large, nested object Maps indefinitely.&nbsp;<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"comparison-with-preloaded-details\">Comparison with Preloaded Details&nbsp;<\/h2>\n\n\n\n<section class=\"container pb-5\">\n  <div class=\"row\">\n    <div class=\"col-12\">\n      <div class=\"table-header-controls\">\n        <button id=\"expandTable\" class=\"expand-icon\" title=\"Expand full table\">\n          <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/static.infragistics.com\/marketing\/Website\/shared\/expand-icon.svg\" alt=\"Expand icon\" width=\"20\" height=\"20\" class=\"position-relative\" style=\"top:7px\" \/>\n        <\/button>\n      <\/div>\n      <div id=\"tableContainer\" class=\"table-responsive-sm mw-100\" style=\"overflow-x: auto; -webkit-overflow-scrolling: touch; position: relative;\">\n        <table class=\"table comparison-table mw-100 text-center\" border=\"0\" cellpadding=\"10\">\n          <thead>\n            <tr>\n              <th>Approach<\/th>\n              <th>Best For<\/th>\n              <th>The Downsides<\/th>\n            <\/tr>\n          <\/thead>\n          <tbody>\n            <tr>\n              <td>Preloaded details<\/td>\n              <td>Small datasets, simple demos, and quick prototypes<\/td>\n              <td>Slower initial load and higher memory usage<\/td>\n            <\/tr>\n            <tr>\n              <td>On-demand loading<\/td>\n              <td>Large\/complex datasets and production apps<\/td>\n              <td>Slightly more code complexity and requires async handling<\/td>\n            <\/tr>\n          <\/tbody>\n        <\/table>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n<style>\n    \/* Table header controls container *\/\n    .table-header-controls {\n        display: flex;\n        justify-content: flex-end;\n        align-items: center;\n        margin-bottom: 10px;\n        position: relative;\n    }\ntbody td {\n  text-align: left !important;\n}\n    \/* Expand icon - now always visible outside the table *\/\n    .expand-icon {\n        background: #fff;\n        color: white;\n        border: none;\n        border-radius: 6px;\n        width: 40px;\n        height: 40px;\n        cursor: pointer;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        transition: all 0.3s ease;\n        \n        backdrop-filter: blur(4px);\n        opacity: 1;\n        visibility: visible;\n        transform: translateY(0);\n        position: relative;\n        z-index: 10;\n    }\n\n    .expand-icon:hover {\n        background: #fff;\n        transform: scale(1.1);\n        \n    }\n.comparison-table td:first-child, .comparison-table th:first-child {\n    white-space: normal !important; \/* \u041f\u043e\u0437\u0432\u043e\u043b\u0438 \u043f\u0440\u0435\u043d\u0430\u0441\u044f\u043d\u0435 *\/\n    overflow-wrap: break-word !important;\n    max-width: 130px !important; \/* \u0421\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u0430 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u043d\u0430 \u0448\u0438\u0440\u0438\u043d\u0430 *\/\n    min-width: 60px !important;  \/* \u041f\u043e\u0437\u0432\u043e\u043b\u0438 \u0434\u0430 \u0441\u0435 \u0441\u0432\u0438\u0432\u0430 *\/\n}\n\n    .expand-icon img {\n        transition: transform 0.2s ease;\n    }\n\n    .expand-icon:hover img {\n        transform: scale(1.1);\n    }\n\n    .table-responsive-sm {\n        overflow-x: auto !important;\n        -webkit-overflow-scrolling: touch;\n        max-width: 100vw;\n        position: relative;\n        border: none;\n        border-radius: 0.375rem;\n        box-shadow: inset -5px 0 11px 1px #00000014;\n        transition: all 0.5s ease;\n    }\n\n    \/* Expanded mode - table takes full screen *\/\n    .table-expanded {\n        position: fixed !important;\n        top: 0;\n        left: 0;\n        width: 100vw !important;\n        height: 100vh !important;\n        z-index: 999999;\n        background: rgba(255, 255, 255, 0.95);\n        margin: 0 !important;\n        border-radius: 0 !important;\n        box-shadow: none !important;\n        overflow: auto !important;\n        padding: 40px 20px 20px 20px;\n        backdrop-filter: blur(10px);\n        -webkit-backdrop-filter: blur(10px);\n        display: flex;\n        align-items: center;\n        justify-content: center;\n    }\n\n    .table-expanded .table-responsive-sm {\n        max-width: 95vw !important;\n        max-height: 85vh !important;\n        overflow: auto !important;\n        border-radius: 8px !important;\n        box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3) !important;\n        background: white !important;\n        z-index: 1;\n    }\n\n    .table-expanded .comparison-table {\n        min-width: auto !important;\n        width: 100% !important;\n        margin: 0 !important;\n        position: relative !important;\n        top: auto !important;\n        left: auto !important;\n        transform: none !important;\n        max-height: none !important;\n    }\n\n    .table-expanded .comparison-table th,\n    .table-expanded .comparison-table td {\n        white-space: normal !important;\n        word-wrap: break-word;\n        max-width: none !important;\n        padding: 15px 10px !important;\n        font-size: 14px;\n    }\n\n    \/* Hide expand icon when in expanded mode *\/\n    .table-expanded .table-header-controls {\n        display: none !important;\n    }\n\n    \/* Close button in expanded mode *\/\n    .close-expanded {\n        position: fixed;\n        top: 20px;\n        right: 20px;\n        z-index: 1000000;\n        background: #dc3545;\n        color: white;\n        border: none;\n        border-radius: 50%;\n        width: 50px;\n        height: 50px;\n        font-size: 20px;\n        cursor: pointer;\n        box-shadow: 0 4px 8px rgba(0,0,0,0.2);\n        transition: all 0.3s ease;\n    }\n\n    .close-expanded:hover {\n        background: #c82333;\n        transform: scale(1.1);\n    }\n\n    .comparison-table {\n        min-width: 750px !important;\n        margin-bottom: 0;\n        position: relative;\n        z-index: -1;\n    }\n\n.comparison-table th,\n.comparison-table td {\n    white-space: normal !important;\n    word-wrap: break-word;\n    overflow-wrap: break-word;\n    padding: 12px 8px !important;\n    min-width: 50px;\n    border: none !important;\n}\n\n\n    .comparison-table th {\n        background-color: #f8f9fa;\n        font-weight: 600;\n        position: sticky;\n        top: 0;\n        z-index: 10;\n    }\n\n    .comparison-table tr th {\n        background: #666;\n        color: #FFF;\n    }\n\n    .comparison-table tr td {\n        border: none !important;\n        z-index: 1;\n        position: relative;\n    }\n\n    .comparison-table td:first-child,\n    .comparison-table th:first-child {\n        position: sticky !important;\n        left: 0;\n        z-index: 5;\n        min-width: 130px;\n        font-weight: 600;\n        border: none !important;\n        overflow: visible;\n    }\n\n    \/* First column styling with pseudo-element for shadow *\/\n    .comparison-table td:first-child::after,\n    .comparison-table th:first-child::after {\n        content: \"\";\n        position: absolute;\n        top: 0;\n        right: 0;\n        bottom: 0;\n        width: 10px;\n        pointer-events: none;\n        border-right: 1px solid #ccc;\n        box-shadow: 10px 0px 10px 0px #00000014;\n    }\n\n    \/* Background colors for first column *\/\n    .comparison-table tbody tr:nth-of-type(odd) td:first-child {\n        background-color: #fff !important;\n    }\n\n    .comparison-table tbody tr:nth-of-type(even) td:first-child {\n        background-color: #f5f6fb !important;\n    }\n\n    \/* Row background colors *\/\n    .comparison-table tbody tr:nth-of-type(even) td {\n        background-color: #f5f6fb;\n    }\n\n    .comparison-table tbody tr:nth-of-type(odd) td {\n        background-color: #fff;\n    }\n\n    .comparison-table th:first-child {\n        background-color: #4d51b6 !important;\n        z-index: 15;\n        color: #fff;\n        width: 20%;\n    }\n\n    \/* Scroll indicator *\/\n    .table-responsive-sm::after {\n        content: \"\u2190 Swipe to see more \u2192\";\n        display: block;\n        text-align: center;\n        font-size: 12px;\n        color: #6c757d;\n        padding: 8px;\n        background-color: #f8f9fa;\n        border-top: 1px solid #dee2e6;\n    }\n\n    \/* Hide scroll indicator in expanded mode *\/\n    .table-expanded::after {\n        display: none !important;\n    }\n\n    @media (min-width: 1200px) {\n        .table-responsive-sm::after {\n            display: none;\n        }\n    }\n\n    \/* Responsive for small screens *\/\n    @media (max-width: 768px) {\n        .expand-icon {\n            width: 35px;\n            height: 35px;\n        }\n        \n        .table-expanded {\n            padding: 10px;\n        }\n        \n        .table-expanded .comparison-table th,\n        .table-expanded .comparison-table td {\n            font-size: 12px;\n            padding: 8px 5px !important;\n        }\n    }\n<\/style>\n\n<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n    \/\/ \u041d\u0430\u043c\u0438\u0440\u0430\u043c\u0435 \u0432\u0441\u0438\u0447\u043a\u0438 \u0431\u0443\u0442\u043e\u043d\u0438 \u0437\u0430 \u0440\u0430\u0437\u0448\u0438\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0438\n    const expandButtons = document.querySelectorAll('.expand-icon');\n\n    expandButtons.forEach(expandBtn => {\n        expandBtn.addEventListener('click', function() {\n            console.log('\ud83d\udd0d Table expand button clicked - entering full screen mode');\n\n            \/\/ \u041d\u0430\u043c\u0438\u0440\u0430\u043c\u0435 \u043d\u0430\u0439-\u0431\u043b\u0438\u0437\u043a\u0438\u044f \u0440\u043e\u0434\u0438\u0442\u0435\u043b, \u043a\u043e\u0439\u0442\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430 \u0438 \u0431\u0443\u0442\u043e\u043d\u0438\u0442\u0435 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0442\u0430\n            \/\/ \u0412 \u0442\u0432\u043e\u044f \u0441\u043b\u0443\u0447\u0430\u0439 - \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u0442 \u0441 \u043a\u043b\u0430\u0441 col-12, \u043a\u043e\u0439\u0442\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430 .table-header-controls \u0438 #tableContainer\n            const colContainer = this.closest('.col-12');\n\n            \/\/ \u0410\u043a\u043e \u0432\u0435\u0447\u0435 \u0438\u043c\u0430 \u043e\u0442\u0432\u043e\u0440\u0435\u043d \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d \u0438\u0437\u0433\u043b\u0435\u0434 - \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u044f\u0432\u0430\u043c\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0442\u0432\u043e\u0440\u0438 \u0432\u0442\u043e\u0440\u0438\n            if (colContainer.classList.contains('table-expanded')) {\n                return;\n            }\n\n            \/\/ \u0421\u044a\u0437\u0434\u0430\u0432\u0430\u043c\u0435 \u0431\u0443\u0442\u043e\u043d \u0437\u0430 \u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435\n            const closeBtn = document.createElement('button');\n            closeBtn.className = 'close-expanded';\n            closeBtn.innerHTML = '\u2715';\n            closeBtn.title = 'Close expanded view';\n\n            \/\/ \u0414\u043e\u0431\u0430\u0432\u044f\u043c\u0435 \u0431\u0443\u0442\u043e\u043d\u044a\u0442 \u0437\u0430 \u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 \u043a\u044a\u043c body\n            document.body.appendChild(closeBtn);\n\n            \/\/ \u0414\u043e\u0431\u0430\u0432\u044f\u043c\u0435 \u043a\u043b\u0430\u0441\u0430 \u0437\u0430 \u0440\u0430\u0437\u0448\u0438\u0440\u0435\u043d \u0438\u0437\u0433\u043b\u0435\u0434\n            colContainer.classList.add('table-expanded');\n\n            \/\/ \u0417\u0430\u0431\u0440\u0430\u043d\u044f\u0432\u0430\u043c\u0435 \u0441\u043a\u0440\u043e\u043b \u043d\u0430 body\n            document.body.style.overflow = 'hidden';\n\n            \/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0437\u0430 \u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435\n            function closeExpanded() {\n                console.log('\u274c Closing expanded table view');\n                colContainer.classList.remove('table-expanded');\n                if (document.body.contains(closeBtn)) {\n                    document.body.removeChild(closeBtn);\n                }\n                document.body.style.overflow = '';\n                document.removeEventListener('keydown', handleEscape);\n            }\n\n            \/\/ \u041a\u043b\u0438\u043a \u0432\u044a\u0440\u0445\u0443 \u0431\u0443\u0442\u043e\u043d\u0430 \u0437\u0430 \u0437\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435\n            closeBtn.addEventListener('click', closeExpanded);\n\n            \/\/ \u0417\u0430\u0442\u0432\u0430\u0440\u044f\u043d\u0435 \u0441 ESC\n            function handleEscape(e) {\n                if (e.key === 'Escape') {\n                    closeExpanded();\n                }\n            }\n            document.addEventListener('keydown', handleEscape);\n        });\n    });\n});\n<\/script>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"conclusion\">Conclusion&nbsp;<\/h2>\n\n\n\n<p>On-demand data loading is a natural extension of the Master-Detail Grid Layout. It improves performance, scales seamlessly with large datasets, and ensures a smooth user experience.&nbsp;<\/p>\n\n\n\n<p>If you\u2019re new to this pattern, check out our <a href=\"https:\/\/www.infragistics.com\/blogs\/master-detail-layout\/\">Getting Started with Master-Detail Layout Using Ignite UI for Angular Grid<\/a> article first. Then, apply on-demand loading to your own apps to take your data grids to the next level.&nbsp;<\/p>\n\n\n\n<p>With Ignite UI for Angular, you can build responsive, data-driven applications that stay fast, even as your data grows.&nbsp;<\/p>\n\n\n\n<p>Check out the complete <a href=\"https:\/\/github.com\/IgniteUI\/MasterDetailGridWithLoadOnDemand\" rel=\"noopener\">Load-on-demand app sample<\/a>.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>On-demand data loading is a powerful technique for scaling master-detail interfaces. Instead of fetching every detail upfront, you load related records only when the user needs them. Learn more in this article.<\/p>\n","protected":false},"author":173,"featured_media":3190,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[36,20,38],"class_list":["post-3114","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-angular","tag-app-development","tag-ignite-ui","tag-ignite-ui-angular"],"_links":{"self":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts\/3114","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/users\/173"}],"replies":[{"embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/comments?post=3114"}],"version-history":[{"count":18,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts\/3114\/revisions"}],"predecessor-version":[{"id":3263,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/posts\/3114\/revisions\/3263"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/media\/3190"}],"wp:attachment":[{"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/media?parent=3114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/categories?post=3114"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.infragistics.com\/blogs\/wp-json\/wp\/v2\/tags?post=3114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}