From a2674461fbb4c3f8d6a9def812587154db18ae58 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Tue, 17 Dec 2024 22:00:59 -0800 Subject: [PATCH 1/2] fix: data items nested templates --- .../data-items/data-items.service.ts | 2 +- .../services/instance/template-row.service.ts | 7 ++-- .../components/template/template-component.ts | 28 +++++++++---- .../template-container.component.html | 42 +++++++++---------- .../template/template-container.component.ts | 7 +--- 5 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/app/shared/components/template/components/data-items/data-items.service.ts b/src/app/shared/components/template/components/data-items/data-items.service.ts index ffa7138976..7fc03406ca 100644 --- a/src/app/shared/components/template/components/data-items/data-items.service.ts +++ b/src/app/shared/components/template/components/data-items/data-items.service.ts @@ -117,6 +117,6 @@ export class DataItemsService { // HACK - still want to be able to use localContext from parent rows so copy to child processor processor.templateRowMap = JSON.parse(JSON.stringify(templateRowMap)); await processor.processContainerTemplateRows(); - return processor.renderedRows; + return processor.renderedRows(); } } diff --git a/src/app/shared/components/template/services/instance/template-row.service.ts b/src/app/shared/components/template/services/instance/template-row.service.ts index 460c1ab0ad..b91059efa3 100644 --- a/src/app/shared/components/template/services/instance/template-row.service.ts +++ b/src/app/shared/components/template/services/instance/template-row.service.ts @@ -1,4 +1,4 @@ -import { Injector } from "@angular/core"; +import { Injector, signal } from "@angular/core"; import { FlowTypes } from "src/app/shared/model"; import { getGlobalService } from "src/app/shared/services/global.service"; import { SyncServiceBase } from "src/app/shared/services/syncService.base"; @@ -9,6 +9,7 @@ import { mergeTemplateRows } from "../../utils/template-utils"; import { TemplateFieldService } from "../template-field.service"; import { TemplateTranslateService } from "../template-translate.service"; import { TemplateVariablesService } from "../template-variables.service"; +import { isEqual } from "packages/shared/src/utils/object-utils"; /** Logging Toggle - rewrite default functions to enable or disable inline logs */ let SHOW_DEBUG_LOGS = false; @@ -34,7 +35,7 @@ export class TemplateRowService extends SyncServiceBase { /** List of overrides set by parent templates for access during parent processing */ /** Hashmap of all rows keyed by nested row name (e.g. contentBox1.row1.title) */ public templateRowMap: ITemplateRowMap = {}; - public renderedRows: FlowTypes.TemplateRow[]; // rows processed and filtered by condition + public renderedRows = signal([], { equal: isEqual }); // rows processed and filtered by condition constructor( private injector: Injector, @@ -234,7 +235,7 @@ export class TemplateRowService extends SyncServiceBase { const renderedRows = this.filterConditionalTemplateRows( JSON.parse(JSON.stringify(processedRows)) ); - this.renderedRows = renderedRows; + this.renderedRows.set(renderedRows); log("[Rows Processed]", logName, { rows, processedRows, renderedRows }); return processedRows; } diff --git a/src/app/shared/components/template/template-component.ts b/src/app/shared/components/template/template-component.ts index 821ea072a4..2c0fa6d6e4 100644 --- a/src/app/shared/components/template/template-component.ts +++ b/src/app/shared/components/template/template-component.ts @@ -71,7 +71,8 @@ export class TemplateComponent implements OnInit, AfterContentInit, ITemplateRow this._row = row; if (this.componentRef) { log("[Component Update]", row.name, row); - this.componentRef.instance.row = row; + this.componentRef.setInput("row", row); + this.hackForceReprocessNestedTemplate(); } else { log("[Component Create]", row.name, row); } @@ -130,7 +131,7 @@ export class TemplateComponent implements OnInit, AfterContentInit, ITemplateRow // Depending on row type, either prepare instantiation of a nested template or a display component switch (row.type) { case "template": - return this.renderTemplateComponent(TemplateContainerComponent, row); + return this.renderTemplateComponent(row); default: const displayComponent = TEMPLATE_COMPONENT_MAPPING[row.type]; if (displayComponent) { @@ -145,15 +146,12 @@ export class TemplateComponent implements OnInit, AfterContentInit, ITemplateRow } /** Create and render a nested template component */ - private renderTemplateComponent( - component: typeof TemplateContainerComponent, - row: FlowTypes.TemplateRow - ) { + private renderTemplateComponent(row: FlowTypes.TemplateRow) { const viewContainerRef = this.tmplComponentHost.viewContainerRef; - const componentRef = viewContainerRef.createComponent(component); + const componentRef = viewContainerRef.createComponent(TemplateContainerComponent); // assign input variables (note template name taken from the row's value column) componentRef.instance.parent = this.parent; - componentRef.instance.row = row; + componentRef.setInput("row", row); componentRef.instance.name = row.name; // assign templatename input using signal componentRef.setInput("templatename", row.value); @@ -169,4 +167,18 @@ export class TemplateComponent implements OnInit, AfterContentInit, ITemplateRow componentRef.instance.row = row; this.componentRef = componentRef; } + + /** + * If the current template is generated as a child of data_items then updates to + * parent variables will not propagate down. Force reprocessing to workaround + * See issue https://github.com/IDEMSInternational/open-app-builder/issues/2636 + */ + private hackForceReprocessNestedTemplate() { + if (this._row.type === "template") { + const componentRef = this.componentRef as ComponentRef; + if (componentRef.instance.parent) { + componentRef.instance.templateRowService.processContainerTemplateRows(); + } + } + } } diff --git a/src/app/shared/components/template/template-container.component.html b/src/app/shared/components/template/template-container.component.html index 8e6f73efc2..c6935cf717 100644 --- a/src/app/shared/components/template/template-container.component.html +++ b/src/app/shared/components/template/template-container.component.html @@ -5,31 +5,27 @@
name: {{ name || "(undefined)" }}
- + @for (row of templateRowService.renderedRows() | filterDisplayComponent; track row._nested_name) { + + } - + @for (row of templateRowService.renderedRows() | filterDisplayComponent; track row._nested_name) { + + } diff --git a/src/app/shared/components/template/template-container.component.ts b/src/app/shared/components/template/template-container.component.ts index 410fe7dc04..809a5b9e91 100644 --- a/src/app/shared/components/template/template-container.component.ts +++ b/src/app/shared/components/template/template-container.component.ts @@ -146,15 +146,12 @@ export class TemplateContainerComponent implements OnInit, OnDestroy { if (shouldProcess) { if (full) { console.log("[Force Reload]", this.name); - // ensure angular destroys previous row components before rendering new - // (note - will cause short content flicker) - this.templateRowService.renderedRows = []; // allow time for other pending ops to finish await _wait(50); await this.renderTemplate(this.templatename()); } else { await this.templateRowService.processRowUpdates(); - console.log("[Force Reprocess]", this.name, this.templateRowService.renderedRows); + console.log("[Force Reprocess]", this.name, this.templateRowService.renderedRows()); for (const child of Object.values(this.children || {})) { await child.forceRerender(full, shouldProcess); } @@ -203,7 +200,7 @@ export class TemplateContainerComponent implements OnInit, OnDestroy { log("[Template] Rendered", this.name, { template, ctxt: { ...this }, - renderedRows: { ...this.templateRowService.renderedRows }, + renderedRows: { ...this.templateRowService.renderedRows() }, rowMap: this.templateRowService.templateRowMap, }); // if a parent exists also provide parent reference to this as a child From 6ff0da832cee1518f9d4c2c6b5a90d2b1b7d9dcc Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 19 Dec 2024 13:20:37 -0800 Subject: [PATCH 2/2] fix: container recreate on template name change Fix issue where changing the template name would not correctly process child template rows if deep nested --- .../components/data-items/data-items.utils.ts | 2 +- .../shared/components/template/template-component.ts | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/shared/components/template/components/data-items/data-items.utils.ts b/src/app/shared/components/template/components/data-items/data-items.utils.ts index 50dd55081c..d6c9d176f5 100644 --- a/src/app/shared/components/template/components/data-items/data-items.utils.ts +++ b/src/app/shared/components/template/components/data-items/data-items.utils.ts @@ -58,7 +58,7 @@ export function updateItemMeta( } // Apply recursively to ensure item children with nested rows (e.g. display groups) also inherit item context - if (r.rows) { + if (r.rows && r.type !== "data_items" && r.type !== "items") { r.rows = updateItemMeta(r.rows, itemData, dataListName); } diff --git a/src/app/shared/components/template/template-component.ts b/src/app/shared/components/template/template-component.ts index 2c0fa6d6e4..8ec4961ded 100644 --- a/src/app/shared/components/template/template-component.ts +++ b/src/app/shared/components/template/template-component.ts @@ -173,11 +173,18 @@ export class TemplateComponent implements OnInit, AfterContentInit, ITemplateRow * parent variables will not propagate down. Force reprocessing to workaround * See issue https://github.com/IDEMSInternational/open-app-builder/issues/2636 */ - private hackForceReprocessNestedTemplate() { + private async hackForceReprocessNestedTemplate() { if (this._row.type === "template") { const componentRef = this.componentRef as ComponentRef; if (componentRef.instance.parent) { - componentRef.instance.templateRowService.processContainerTemplateRows(); + // HACK - if parent template changes name of nested child template instance fully + // recreate the template container + if ((componentRef.instance.templatename(), this._row.value)) { + this.componentRef.destroy(); + this.renderTemplateComponent(this._row); + // TODO - test better ways to manage from container, confirming test cases from + // https://github.com/IDEMSInternational/open-app-builder/issues/2636 + } } } }