@memberjunction/ng-base-forms 5.38.0 → 5.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +53 -0
  2. package/dist/lib/base-form-component.d.ts +18 -0
  3. package/dist/lib/base-form-component.d.ts.map +1 -1
  4. package/dist/lib/base-form-component.js +31 -2
  5. package/dist/lib/base-form-component.js.map +1 -1
  6. package/dist/lib/container/record-form-container.component.d.ts +14 -0
  7. package/dist/lib/container/record-form-container.component.d.ts.map +1 -1
  8. package/dist/lib/container/record-form-container.component.js +59 -33
  9. package/dist/lib/container/record-form-container.component.js.map +1 -1
  10. package/dist/lib/field/fk-search-utils.d.ts +31 -0
  11. package/dist/lib/field/fk-search-utils.d.ts.map +1 -0
  12. package/dist/lib/field/fk-search-utils.js +47 -0
  13. package/dist/lib/field/fk-search-utils.js.map +1 -0
  14. package/dist/lib/field/form-field.component.d.ts +401 -6
  15. package/dist/lib/field/form-field.component.d.ts.map +1 -1
  16. package/dist/lib/field/form-field.component.js +1646 -214
  17. package/dist/lib/field/form-field.component.js.map +1 -1
  18. package/dist/lib/field/linked-field-options.d.ts +76 -0
  19. package/dist/lib/field/linked-field-options.d.ts.map +1 -0
  20. package/dist/lib/field/linked-field-options.js +80 -0
  21. package/dist/lib/field/linked-field-options.js.map +1 -0
  22. package/dist/lib/host/entity-form-host.component.d.ts +159 -0
  23. package/dist/lib/host/entity-form-host.component.d.ts.map +1 -0
  24. package/dist/lib/host/entity-form-host.component.js +539 -0
  25. package/dist/lib/host/entity-form-host.component.js.map +1 -0
  26. package/dist/lib/isa-related-panel/isa-related-card.component.js +2 -2
  27. package/dist/lib/overlays/base-form-overlay.d.ts +114 -0
  28. package/dist/lib/overlays/base-form-overlay.d.ts.map +1 -0
  29. package/dist/lib/overlays/base-form-overlay.js +227 -0
  30. package/dist/lib/overlays/base-form-overlay.js.map +1 -0
  31. package/dist/lib/overlays/form-dialog.component.d.ts +35 -0
  32. package/dist/lib/overlays/form-dialog.component.d.ts.map +1 -0
  33. package/dist/lib/overlays/form-dialog.component.js +92 -0
  34. package/dist/lib/overlays/form-dialog.component.js.map +1 -0
  35. package/dist/lib/overlays/form-presenter.service.d.ts +37 -0
  36. package/dist/lib/overlays/form-presenter.service.d.ts.map +1 -0
  37. package/dist/lib/overlays/form-presenter.service.js +130 -0
  38. package/dist/lib/overlays/form-presenter.service.js.map +1 -0
  39. package/dist/lib/overlays/form-presenter.types.d.ts +65 -0
  40. package/dist/lib/overlays/form-presenter.types.d.ts.map +1 -0
  41. package/dist/lib/overlays/form-presenter.types.js +25 -0
  42. package/dist/lib/overlays/form-presenter.types.js.map +1 -0
  43. package/dist/lib/overlays/form-slide-in.component.d.ts +38 -0
  44. package/dist/lib/overlays/form-slide-in.component.d.ts.map +1 -0
  45. package/dist/lib/overlays/form-slide-in.component.js +106 -0
  46. package/dist/lib/overlays/form-slide-in.component.js.map +1 -0
  47. package/dist/lib/overlays/form-window.component.d.ts +32 -0
  48. package/dist/lib/overlays/form-window.component.d.ts.map +1 -0
  49. package/dist/lib/overlays/form-window.component.js +93 -0
  50. package/dist/lib/overlays/form-window.component.js.map +1 -0
  51. package/dist/lib/panel/collapsible-panel.component.d.ts +19 -2
  52. package/dist/lib/panel/collapsible-panel.component.d.ts.map +1 -1
  53. package/dist/lib/panel/collapsible-panel.component.js +70 -13
  54. package/dist/lib/panel/collapsible-panel.component.js.map +1 -1
  55. package/dist/lib/resolver/form-resolver.service.d.ts +139 -0
  56. package/dist/lib/resolver/form-resolver.service.d.ts.map +1 -0
  57. package/dist/lib/resolver/form-resolver.service.js +235 -0
  58. package/dist/lib/resolver/form-resolver.service.js.map +1 -0
  59. package/dist/lib/section-manager/section-manager.component.js +2 -2
  60. package/dist/lib/toolbar/form-toolbar.component.js +2 -2
  61. package/dist/lib/types/entity-form-config.d.ts +125 -0
  62. package/dist/lib/types/entity-form-config.d.ts.map +1 -0
  63. package/dist/lib/types/entity-form-config.js +75 -0
  64. package/dist/lib/types/entity-form-config.js.map +1 -0
  65. package/dist/lib/types/form-types.d.ts +24 -0
  66. package/dist/lib/types/form-types.d.ts.map +1 -1
  67. package/dist/lib/types/form-types.js.map +1 -1
  68. package/dist/lib/types/navigation-events.d.ts +46 -2
  69. package/dist/lib/types/navigation-events.d.ts.map +1 -1
  70. package/dist/lib/types/navigation-events.js.map +1 -1
  71. package/dist/module.d.ts +12 -8
  72. package/dist/module.d.ts.map +1 -1
  73. package/dist/module.js +26 -7
  74. package/dist/module.js.map +1 -1
  75. package/dist/public-api.d.ts +11 -0
  76. package/dist/public-api.d.ts.map +1 -1
  77. package/dist/public-api.js +14 -0
  78. package/dist/public-api.js.map +1 -1
  79. package/package.json +17 -13
@@ -0,0 +1,539 @@
1
+ import { Component, Input, Output, EventEmitter, ViewChild, ViewContainerRef, ChangeDetectorRef, inject } from '@angular/core';
2
+ import { CompositeKey, FieldValueCollection, EntityFieldTSType } from '@memberjunction/core';
3
+ import { MJGlobal } from '@memberjunction/global';
4
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
5
+ import { BaseFormSectionComponent } from '../base-form-section-component';
6
+ import { InteractiveFormComponent } from '../interactive-form/interactive-form.component';
7
+ import { FormResolverService } from '../resolver/form-resolver.service';
8
+ import * as i0 from "@angular/core";
9
+ import * as i1 from "@memberjunction/ng-shared-generic";
10
+ const _c0 = ["anchor"];
11
+ function MjEntityFormHostComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
12
+ i0.ɵɵelementStart(0, "div", 1);
13
+ i0.ɵɵelement(1, "mj-loading", 3);
14
+ i0.ɵɵelementEnd();
15
+ } }
16
+ function MjEntityFormHostComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
17
+ i0.ɵɵelementStart(0, "div", 2);
18
+ i0.ɵɵelement(1, "i", 4);
19
+ i0.ɵɵelementStart(2, "h3", 5);
20
+ i0.ɵɵtext(3);
21
+ i0.ɵɵelementEnd();
22
+ i0.ɵɵelementStart(4, "p", 6);
23
+ i0.ɵɵtext(5);
24
+ i0.ɵɵelementEnd()();
25
+ } if (rf & 2) {
26
+ const ctx_r0 = i0.ɵɵnextContext();
27
+ i0.ɵɵadvance(3);
28
+ i0.ɵɵtextInterpolate(ctx_r0.errorTitle);
29
+ i0.ɵɵadvance(2);
30
+ i0.ɵɵtextInterpolate(ctx_r0.errorDetail);
31
+ } }
32
+ function MjEntityFormHostComponent_ng_template_2_Template(rf, ctx) { }
33
+ /**
34
+ * Presentation-agnostic host that renders ANY MemberJunction entity form —
35
+ * generated (`@RegisterClass` / CodeGen), custom (`*Extended`), or interactive
36
+ * (`EntityFormOverride`) — for a given record.
37
+ *
38
+ * This is the shared core extracted from Explorer's `SingleRecordComponent`. It
39
+ * encapsulates the full lifecycle: **resolve** which form to use (via
40
+ * {@link FormResolverService}, honoring variant overrides) → **load** the
41
+ * record (or accept a pre-loaded one) → **create** the form component
42
+ * dynamically → **bind** record / EditMode / Config / variants → **re-emit**
43
+ * the form's events → **tear down** cleanly.
44
+ *
45
+ * It has ZERO routing and ZERO Explorer dependencies — it only emits events.
46
+ * The surrounding surface (a full-page tab in Explorer, a dialog, or a
47
+ * slide-in) decides what to do with `Navigate`, `Notification`, etc.
48
+ *
49
+ * @example Explorer tab (thin wrapper subscribes + maps to NavigationService)
50
+ * ```html
51
+ * <mj-entity-form-host [EntityName]="entityName" [PrimaryKey]="pk"
52
+ * (Navigate)="handleNavigation($event)" (Saved)="onSaved($event)">
53
+ * </mj-entity-form-host>
54
+ * ```
55
+ *
56
+ * @example Bind a pre-loaded record (dialog/slide-in)
57
+ * ```html
58
+ * <mj-entity-form-host [Record]="myEntity" [Config]="DIALOG_FORM_CONFIG"></mj-entity-form-host>
59
+ * ```
60
+ */
61
+ export class MjEntityFormHostComponent extends BaseAngularComponent {
62
+ cdr = inject(ChangeDetectorRef);
63
+ formResolver = inject(FormResolverService);
64
+ /** Anchor for the dynamically-created form component. Always present in the DOM. */
65
+ anchor;
66
+ // ── Inputs ──────────────────────────────────────────────────────────────
67
+ _entityName = null;
68
+ /** Entity name to load a record for. Ignored when {@link Record} is supplied. */
69
+ set EntityName(value) {
70
+ const changed = this._entityName !== value;
71
+ this._entityName = value;
72
+ if (changed && this._viewInitialized)
73
+ this.reload();
74
+ }
75
+ get EntityName() { return this._entityName; }
76
+ _primaryKey = new CompositeKey();
77
+ /** Primary key of the record to load. Empty key → new record. */
78
+ set PrimaryKey(value) {
79
+ const changed = !this.compositeKeysEqual(this._primaryKey, value);
80
+ this._primaryKey = value ?? new CompositeKey();
81
+ if (!changed || !this._viewInitialized)
82
+ return;
83
+ // Skip reload when the incoming key just reflects the current record's now-saved PK
84
+ // (post new-record save rebind). Genuine external swaps still reload.
85
+ if (this._currentRecord && this.compositeKeysEqual(value, this._currentRecord.PrimaryKey))
86
+ return;
87
+ this.reload();
88
+ }
89
+ get PrimaryKey() { return this._primaryKey; }
90
+ _record = null;
91
+ /**
92
+ * Pre-loaded record to bind directly. When supplied, the host skips the
93
+ * load step entirely and mounts the resolved form against this instance —
94
+ * `EntityName`/`PrimaryKey` are not required.
95
+ */
96
+ set Record(value) {
97
+ const changed = this._record !== value;
98
+ this._record = value;
99
+ if (changed && this._viewInitialized)
100
+ this.reload();
101
+ }
102
+ get Record() { return this._record; }
103
+ /** New-record default values: URL-segment string or a plain object. */
104
+ NewRecordValues = null;
105
+ /**
106
+ * Render a single registered form **section** (a `BaseFormSectionComponent`
107
+ * registered as `'<EntityName>.<SectionName>'`) instead of the full form.
108
+ * When set, the full-form resolver / variant picker / toolbar container are
109
+ * bypassed — the section component renders its own fields and the host saves
110
+ * the record directly. Used for compact, focused editors (e.g. a quick-edit
111
+ * grid row). Leave null for the normal full-form behavior.
112
+ */
113
+ SectionName = null;
114
+ _editMode = null;
115
+ /** Force edit mode. When omitted, new records start in edit, existing in read. */
116
+ set EditMode(value) { this._editMode = value; }
117
+ get EditMode() { return this._editMode; }
118
+ /** Per-instance form presentation config (toolbar, sections, width, links). */
119
+ Config = null;
120
+ // ── Outputs ─────────────────────────────────────────────────────────────
121
+ /** Form navigation request (record link, new record, hierarchy, email, external, dismiss). */
122
+ Navigate = new EventEmitter();
123
+ /** User-facing notification request (success/error/warning/info). */
124
+ Notification = new EventEmitter();
125
+ /** Emitted after the record is successfully saved (carries the live entity). */
126
+ Saved = new EventEmitter();
127
+ /** Re-emitted form `RecordSaved` (richer payload). */
128
+ RecordSaved = new EventEmitter();
129
+ /** Re-emitted form `RecordDeleted`. */
130
+ RecordDeleted = new EventEmitter();
131
+ /** Re-emitted form `RecordSaveFailed`. */
132
+ RecordSaveFailed = new EventEmitter();
133
+ /** Re-emitted form `ValidationFailed`. */
134
+ ValidationFailed = new EventEmitter();
135
+ /** Re-emitted form `RecordReady` (record fully initialized). */
136
+ RecordReady = new EventEmitter();
137
+ /** Form asked to be dismissed (e.g. Discard on a brand-new record). */
138
+ Dismissed = new EventEmitter();
139
+ /** Initial load + mount finished (success path). */
140
+ LoadComplete = new EventEmitter();
141
+ /** Load failed; carries a user-facing title + detail. */
142
+ LoadError = new EventEmitter();
143
+ /** The live form instance, emitted right after it's created (for power-user wiring). */
144
+ FormCreated = new EventEmitter();
145
+ // ── State ───────────────────────────────────────────────────────────────
146
+ loading = true;
147
+ errorTitle = null;
148
+ errorDetail = null;
149
+ _formComponentRef = null;
150
+ _sectionRef = null;
151
+ _isSection = false;
152
+ _currentRecord = null;
153
+ _saveHandlerSub = null;
154
+ _formEventSubs = [];
155
+ _viewInitialized = false;
156
+ /** The live form component instance, or null before mount / after teardown. */
157
+ get Form() {
158
+ return this._formComponentRef?.instance ?? null;
159
+ }
160
+ /** Whether the bound record has unsaved changes. */
161
+ get Dirty() {
162
+ return this._currentRecord?.Dirty ?? false;
163
+ }
164
+ // ── Lifecycle ───────────────────────────────────────────────────────────
165
+ ngAfterViewInit() {
166
+ this._viewInitialized = true;
167
+ this.reload();
168
+ }
169
+ ngOnDestroy() {
170
+ this.teardown();
171
+ }
172
+ // ── Public API (driven by dialog/slide-in chrome) ─────────────────────────
173
+ /**
174
+ * Save the bound record. In full-form mode this runs the form's save
175
+ * pipeline (validation, pending records, notifications); in section mode the
176
+ * section component has no pipeline, so we save the record directly (the
177
+ * record's own save event still drives the `Saved` output).
178
+ */
179
+ async Save() {
180
+ if (this._isSection) {
181
+ return this._currentRecord ? this._currentRecord.Save() : false;
182
+ }
183
+ const f = this.Form;
184
+ if (!f)
185
+ return false;
186
+ return f.SaveRecord(true);
187
+ }
188
+ /** Cancel edits (revert). Full-form mode uses the form's cancel pipeline. */
189
+ Cancel() {
190
+ if (this._isSection) {
191
+ if (this._currentRecord?.Dirty)
192
+ this._currentRecord.Revert();
193
+ return;
194
+ }
195
+ this.Form?.CancelEdit();
196
+ }
197
+ // ── Core: resolve → load → create → bind → wire ──────────────────────────
198
+ reload() {
199
+ this.teardown();
200
+ this.loading = true;
201
+ this.errorTitle = null;
202
+ this.errorDetail = null;
203
+ void this.loadAndMount();
204
+ }
205
+ async loadAndMount() {
206
+ const md = this.ProviderToUse;
207
+ // When a pre-loaded record is supplied, derive the entity from it.
208
+ const entityName = this._record?.EntityInfo?.Name ?? this._entityName;
209
+ if (!entityName || entityName.trim().length === 0) {
210
+ // Nothing to load yet — a caller will re-invoke once an entity is known.
211
+ return;
212
+ }
213
+ try {
214
+ const entity = md.EntityByName(entityName);
215
+ if (!entity) {
216
+ this.fail(`Entity not found: "${entityName}"`, `This MemberJunction instance has no metadata for entity "${entityName}".`);
217
+ return;
218
+ }
219
+ const permissions = entity.GetUserPermisions(md.CurrentUser);
220
+ // Section mode short-circuits the full-form resolver / container.
221
+ if (this.SectionName) {
222
+ await this.mountSection(entityName, permissions);
223
+ return;
224
+ }
225
+ const resolution = await this.formResolver.ResolveFormForEntity(entity, md.CurrentUser, md);
226
+ if (resolution.kind === 'none') {
227
+ this.fail(`No form is registered for "${entityName}".`, `No EntityFormOverride or class-based form (@RegisterClass(BaseFormComponent, '${entityName}')) was found. Run CodeGen or register a custom form.`);
228
+ return;
229
+ }
230
+ const record = await this.obtainRecord(entityName, md);
231
+ if (!record)
232
+ return; // fail() already called
233
+ // Emit Saved on the record's own save event (covers all save paths).
234
+ this._saveHandlerSub = record.RegisterEventHandler((e) => {
235
+ if (e.type === 'save')
236
+ this.Saved.emit(record);
237
+ });
238
+ this.anchor.clear();
239
+ const componentRef = resolution.kind === 'interactive'
240
+ ? this.anchor.createComponent(InteractiveFormComponent)
241
+ : this.anchor.createComponent(resolution.subClass);
242
+ if (resolution.kind === 'interactive') {
243
+ componentRef.instance.ComponentID = resolution.override.ComponentID;
244
+ }
245
+ this._formComponentRef = componentRef;
246
+ this._currentRecord = record;
247
+ const instance = componentRef.instance;
248
+ instance.record = record;
249
+ instance.userPermissions = permissions;
250
+ instance.Config = this.effectiveConfigFor(record);
251
+ instance.EditMode = this._editMode ?? this.Config?.StartInEditMode ?? !record.IsSaved;
252
+ this.applyVariants(instance, resolution, entityName);
253
+ this.subscribeToFormEvents(instance);
254
+ this.FormCreated.emit(instance);
255
+ this.errorTitle = null;
256
+ this.errorDetail = null;
257
+ this.LoadComplete.emit();
258
+ }
259
+ catch (err) {
260
+ const msg = err instanceof Error ? err.message : String(err);
261
+ this.fail(`Failed to load ${entityName} record.`, `An unexpected error occurred: ${msg}`);
262
+ }
263
+ finally {
264
+ this.loading = false;
265
+ this.cdr.detectChanges();
266
+ }
267
+ }
268
+ /**
269
+ * The config to bind for a given record. For brand-new (unsaved) records we hide the
270
+ * `systemMetadata` section by default — its fields (audit timestamps, IDs, etc.) are
271
+ * empty and irrelevant until the record exists. Doesn't mutate the consumer's config,
272
+ * and defers entirely to a consumer that uses an explicit `VisibleSectionKeys` allowlist.
273
+ */
274
+ effectiveConfigFor(record) {
275
+ if (record.IsSaved)
276
+ return this.Config;
277
+ if (this.Config?.VisibleSectionKeys?.length)
278
+ return this.Config; // consumer owns visibility
279
+ const SYSTEM_METADATA_KEY = 'systemMetadata';
280
+ const hidden = this.Config?.HiddenSectionKeys ?? [];
281
+ if (hidden.includes(SYSTEM_METADATA_KEY))
282
+ return this.Config;
283
+ return { ...(this.Config ?? {}), HiddenSectionKeys: [...hidden, SYSTEM_METADATA_KEY] };
284
+ }
285
+ /**
286
+ * Section mode: resolve a `BaseFormSectionComponent` registered as
287
+ * `'<entity>.<section>'`, mount it, and bind the record. No resolver,
288
+ * variants, container, or form-event surface — section components are bare
289
+ * field editors; the host saves the record directly (see {@link Save}).
290
+ */
291
+ async mountSection(entityName, permissions) {
292
+ const key = `${entityName}.${this.SectionName}`;
293
+ const reg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseFormSectionComponent, key);
294
+ if (!reg?.SubClass) {
295
+ this.fail(`No form section "${this.SectionName}" is registered for "${entityName}".`, `Expected a @RegisterClass(BaseFormSectionComponent, '${key}'). Check the section name.`);
296
+ return;
297
+ }
298
+ const record = await this.obtainRecord(entityName);
299
+ if (!record)
300
+ return;
301
+ this._saveHandlerSub = record.RegisterEventHandler((e) => {
302
+ if (e.type === 'save')
303
+ this.Saved.emit(record);
304
+ });
305
+ this.anchor.clear();
306
+ const ref = this.anchor.createComponent(reg.SubClass);
307
+ this._sectionRef = ref;
308
+ this._currentRecord = record;
309
+ this._isSection = true;
310
+ const instance = ref.instance;
311
+ instance.record = record;
312
+ instance.userPermissions = permissions;
313
+ instance.EditMode = this._editMode ?? this.Config?.StartInEditMode ?? !record.IsSaved;
314
+ this.RecordReady.emit(record);
315
+ this.LoadComplete.emit();
316
+ }
317
+ /** Resolve the BaseEntity to bind: the supplied instance, or a freshly loaded/new one. */
318
+ async obtainRecord(entityName, md = this.ProviderToUse) {
319
+ if (this._record)
320
+ return this._record;
321
+ const record = await md.GetEntityObject(entityName, md.CurrentUser);
322
+ if (!record) {
323
+ this.fail(`Failed to load ${entityName} record.`, `Unable to instantiate entity "${entityName}".`);
324
+ return null;
325
+ }
326
+ if (this._primaryKey.HasValue) {
327
+ const loadOk = await record.InnerLoad(this._primaryKey);
328
+ if (!loadOk) {
329
+ this.fail(`Could not load ${entityName} record.`, record.LatestResult?.Message
330
+ ? `Server error: ${record.LatestResult.Message}`
331
+ : `InnerLoad returned false for key ${this._primaryKey.ToString()}.`);
332
+ return null;
333
+ }
334
+ }
335
+ else {
336
+ record.NewRecord();
337
+ this.applyNewRecordValues(record);
338
+ }
339
+ return record;
340
+ }
341
+ /** Push variant list + active selection and wire the variant-switch handler. */
342
+ applyVariants(instance, resolution, entityName) {
343
+ instance.Variants = (resolution.variants ?? [])
344
+ .filter(v => v.Status === 'Active')
345
+ .map(v => ({ ID: v.ID, Label: v.Name ?? `Override ${v.ID.substring(0, 8)}`, Scope: v.Scope, Status: v.Status }));
346
+ instance.CurrentVariantID = resolution.kind === 'interactive' ? resolution.override.ID : null;
347
+ instance.OnVariantChanged = (variantID) => {
348
+ if (variantID === null) {
349
+ this.formResolver.SetExplicitDefault(entityName);
350
+ }
351
+ else {
352
+ this.formResolver.SetSelectedVariant(entityName, variantID);
353
+ }
354
+ // Reload via the resolver so tier/priority + the new selection apply.
355
+ this._record = null; // force a fresh load against the new variant
356
+ this.reload();
357
+ };
358
+ }
359
+ /** Subscribe to the form's outputs and re-emit them through the host. */
360
+ subscribeToFormEvents(form) {
361
+ this.cleanupFormSubs();
362
+ this._formEventSubs.push(form.Navigate.subscribe(e => this.Navigate.emit(e)), form.Notification.subscribe(e => this.Notification.emit(e)), form.RecordSaved.subscribe(e => this.RecordSaved.emit(e)), form.RecordDeleted.subscribe(e => this.RecordDeleted.emit(e)), form.RecordSaveFailed.subscribe(e => this.RecordSaveFailed.emit(e)), form.ValidationFailed.subscribe(e => this.ValidationFailed.emit(e)), form.RecordReady.subscribe(e => this.RecordReady.emit(e)));
363
+ // Surface 'dismiss' navigation as a first-class Dismissed event too.
364
+ this._formEventSubs.push(form.Navigate.subscribe(e => { if (e.Kind === 'dismiss')
365
+ this.Dismissed.emit(); }));
366
+ }
367
+ // ── New-record value application (URL segment or object) ──────────────────
368
+ applyNewRecordValues(record) {
369
+ if (!this.NewRecordValues)
370
+ return;
371
+ if (typeof this.NewRecordValues === 'string') {
372
+ if (this.NewRecordValues.length === 0)
373
+ return;
374
+ const fv = new FieldValueCollection();
375
+ fv.SimpleLoadFromURLSegment(this.NewRecordValues);
376
+ fv.KeyValuePairs
377
+ .filter(kvp => kvp.Value !== null && kvp.Value !== undefined)
378
+ .forEach(kvp => this.setTypedField(record, kvp.FieldName, kvp.Value));
379
+ }
380
+ else {
381
+ const values = this.NewRecordValues;
382
+ Object.keys(values)
383
+ .filter(key => values[key] !== null && values[key] !== undefined)
384
+ .forEach(key => this.setTypedField(record, key, values[key]));
385
+ }
386
+ }
387
+ /** Set a field with TSType-aware coercion (shared by string + object paths). */
388
+ setTypedField(record, fieldName, value) {
389
+ const f = record.Fields.find(x => x.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());
390
+ if (!f)
391
+ return;
392
+ switch (f.EntityFieldInfo.TSType) {
393
+ case EntityFieldTSType.String:
394
+ record.Set(fieldName, value?.toString() ?? '');
395
+ break;
396
+ case EntityFieldTSType.Number:
397
+ record.Set(fieldName, typeof value === 'number' ? value : parseFloat(value?.toString() ?? '0'));
398
+ break;
399
+ case EntityFieldTSType.Boolean:
400
+ if (typeof value === 'boolean')
401
+ record.Set(fieldName, value);
402
+ else if (typeof value === 'string')
403
+ record.Set(fieldName, value !== 'false' && value !== '0' && value.trim().length > 0);
404
+ else
405
+ record.Set(fieldName, !!value);
406
+ break;
407
+ case EntityFieldTSType.Date:
408
+ record.Set(fieldName, value instanceof Date ? value : new Date(value?.toString() ?? ''));
409
+ break;
410
+ default:
411
+ record.Set(fieldName, value);
412
+ break;
413
+ }
414
+ }
415
+ // ── Helpers ───────────────────────────────────────────────────────────────
416
+ fail(title, detail) {
417
+ this.errorTitle = title;
418
+ this.errorDetail = detail;
419
+ this.loading = false;
420
+ if (this._formComponentRef) {
421
+ try {
422
+ this._formComponentRef.destroy();
423
+ }
424
+ catch { /* noop */ }
425
+ this._formComponentRef = null;
426
+ }
427
+ this.anchor?.clear();
428
+ this.LoadError.emit({ title, detail });
429
+ this.cdr.detectChanges();
430
+ }
431
+ compositeKeysEqual(a, b) {
432
+ if (a === b)
433
+ return true;
434
+ if (!a || !b)
435
+ return false;
436
+ const aKvps = (a.KeyValuePairs || []).filter(kvp => String(kvp.Value ?? '').length > 0);
437
+ const bKvps = (b.KeyValuePairs || []).filter(kvp => String(kvp.Value ?? '').length > 0);
438
+ if (aKvps.length !== bKvps.length)
439
+ return false;
440
+ for (let i = 0; i < aKvps.length; i++) {
441
+ if (aKvps[i].FieldName !== bKvps[i].FieldName)
442
+ return false;
443
+ if (String(aKvps[i].Value ?? '') !== String(bKvps[i].Value ?? ''))
444
+ return false;
445
+ }
446
+ return true;
447
+ }
448
+ cleanupFormSubs() {
449
+ for (const sub of this._formEventSubs)
450
+ sub.unsubscribe();
451
+ this._formEventSubs = [];
452
+ }
453
+ teardown() {
454
+ this.cleanupFormSubs();
455
+ if (this._saveHandlerSub) {
456
+ this._saveHandlerSub.unsubscribe();
457
+ this._saveHandlerSub = null;
458
+ }
459
+ if (this._formComponentRef) {
460
+ try {
461
+ this._formComponentRef.destroy();
462
+ }
463
+ catch { /* noop */ }
464
+ this._formComponentRef = null;
465
+ }
466
+ if (this._sectionRef) {
467
+ try {
468
+ this._sectionRef.destroy();
469
+ }
470
+ catch { /* noop */ }
471
+ this._sectionRef = null;
472
+ }
473
+ this._isSection = false;
474
+ this._currentRecord = null;
475
+ this.anchor?.clear();
476
+ }
477
+ static ɵfac = /*@__PURE__*/ (() => { let ɵMjEntityFormHostComponent_BaseFactory; return function MjEntityFormHostComponent_Factory(__ngFactoryType__) { return (ɵMjEntityFormHostComponent_BaseFactory || (ɵMjEntityFormHostComponent_BaseFactory = i0.ɵɵgetInheritedFactory(MjEntityFormHostComponent)))(__ngFactoryType__ || MjEntityFormHostComponent); }; })();
478
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MjEntityFormHostComponent, selectors: [["mj-entity-form-host"]], viewQuery: function MjEntityFormHostComponent_Query(rf, ctx) { if (rf & 1) {
479
+ i0.ɵɵviewQuery(_c0, 7, ViewContainerRef);
480
+ } if (rf & 2) {
481
+ let _t;
482
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.anchor = _t.first);
483
+ } }, inputs: { EntityName: "EntityName", PrimaryKey: "PrimaryKey", Record: "Record", NewRecordValues: "NewRecordValues", SectionName: "SectionName", EditMode: "EditMode", Config: "Config" }, outputs: { Navigate: "Navigate", Notification: "Notification", Saved: "Saved", RecordSaved: "RecordSaved", RecordDeleted: "RecordDeleted", RecordSaveFailed: "RecordSaveFailed", ValidationFailed: "ValidationFailed", RecordReady: "RecordReady", Dismissed: "Dismissed", LoadComplete: "LoadComplete", LoadError: "LoadError", FormCreated: "FormCreated" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 4, vars: 2, consts: [["anchor", ""], [1, "mj-form-host-loading"], ["role", "alert", 1, "mj-form-host-error"], ["text", "Loading record..."], [1, "fa-solid", "fa-triangle-exclamation", "mj-form-host-error-icon"], [1, "mj-form-host-error-title"], [1, "mj-form-host-error-detail"]], template: function MjEntityFormHostComponent_Template(rf, ctx) { if (rf & 1) {
484
+ i0.ɵɵconditionalCreate(0, MjEntityFormHostComponent_Conditional_0_Template, 2, 0, "div", 1);
485
+ i0.ɵɵconditionalCreate(1, MjEntityFormHostComponent_Conditional_1_Template, 6, 2, "div", 2);
486
+ i0.ɵɵtemplate(2, MjEntityFormHostComponent_ng_template_2_Template, 0, 0, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
487
+ } if (rf & 2) {
488
+ i0.ɵɵconditional(ctx.loading ? 0 : -1);
489
+ i0.ɵɵadvance();
490
+ i0.ɵɵconditional(!ctx.loading && ctx.errorTitle ? 1 : -1);
491
+ } }, dependencies: [i1.LoadingComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n}\n\n.mj-form-host-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n height: 100%;\n}\n\n.mj-form-host-error[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px 24px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.mj-form-host-error-icon[_ngcontent-%COMP%] {\n font-size: 32px;\n color: var(--mj-status-error);\n margin-bottom: 8px;\n}\n\n.mj-form-host-error-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-form-host-error-detail[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n max-width: 560px;\n line-height: 1.5;\n color: var(--mj-text-muted);\n}"] });
492
+ }
493
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjEntityFormHostComponent, [{
494
+ type: Component,
495
+ args: [{ standalone: false, selector: 'mj-entity-form-host', template: "<!-- Loading state -->\n@if (loading) {\n <div class=\"mj-form-host-loading\">\n <mj-loading text=\"Loading record...\"></mj-loading>\n </div>\n}\n\n<!-- Error state -->\n@if (!loading && errorTitle) {\n <div class=\"mj-form-host-error\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation mj-form-host-error-icon\"></i>\n <h3 class=\"mj-form-host-error-title\">{{ errorTitle }}</h3>\n <p class=\"mj-form-host-error-detail\">{{ errorDetail }}</p>\n </div>\n}\n\n<!--\n Anchor for the dynamically-created form component. Always present (static)\n so the ViewContainerRef resolves at AfterViewInit. The form's own template\n carries the <mj-record-form-container>, so nothing wraps it here.\n-->\n<ng-template #anchor></ng-template>\n", styles: [":host {\n display: block;\n height: 100%;\n}\n\n.mj-form-host-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n height: 100%;\n}\n\n.mj-form-host-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px 24px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.mj-form-host-error-icon {\n font-size: 32px;\n color: var(--mj-status-error);\n margin-bottom: 8px;\n}\n\n.mj-form-host-error-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-form-host-error-detail {\n margin: 0;\n font-size: 13px;\n max-width: 560px;\n line-height: 1.5;\n color: var(--mj-text-muted);\n}\n"] }]
496
+ }], null, { anchor: [{
497
+ type: ViewChild,
498
+ args: ['anchor', { read: ViewContainerRef, static: true }]
499
+ }], EntityName: [{
500
+ type: Input
501
+ }], PrimaryKey: [{
502
+ type: Input
503
+ }], Record: [{
504
+ type: Input
505
+ }], NewRecordValues: [{
506
+ type: Input
507
+ }], SectionName: [{
508
+ type: Input
509
+ }], EditMode: [{
510
+ type: Input
511
+ }], Config: [{
512
+ type: Input
513
+ }], Navigate: [{
514
+ type: Output
515
+ }], Notification: [{
516
+ type: Output
517
+ }], Saved: [{
518
+ type: Output
519
+ }], RecordSaved: [{
520
+ type: Output
521
+ }], RecordDeleted: [{
522
+ type: Output
523
+ }], RecordSaveFailed: [{
524
+ type: Output
525
+ }], ValidationFailed: [{
526
+ type: Output
527
+ }], RecordReady: [{
528
+ type: Output
529
+ }], Dismissed: [{
530
+ type: Output
531
+ }], LoadComplete: [{
532
+ type: Output
533
+ }], LoadError: [{
534
+ type: Output
535
+ }], FormCreated: [{
536
+ type: Output
537
+ }] }); })();
538
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjEntityFormHostComponent, { className: "MjEntityFormHostComponent", filePath: "src/lib/host/entity-form-host.component.ts", lineNumber: 57 }); })();
539
+ //# sourceMappingURL=entity-form-host.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-form-host.component.js","sourceRoot":"","sources":["../../../src/lib/host/entity-form-host.component.ts","../../../src/lib/host/entity-form-host.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter, ViewChild, ViewContainerRef,\n ComponentRef, ChangeDetectorRef, inject, AfterViewInit, OnDestroy, Type\n} from '@angular/core';\nimport {\n CompositeKey, BaseEntity, BaseEntityEvent, FieldValueCollection, EntityFieldTSType\n} from '@memberjunction/core';\nimport { MJGlobal } from '@memberjunction/global';\nimport { Subscription } from 'rxjs';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\nimport { BaseFormComponent } from '../base-form-component';\nimport { BaseFormSectionComponent } from '../base-form-section-component';\nimport { InteractiveFormComponent } from '../interactive-form/interactive-form.component';\nimport { FormResolverService } from '../resolver/form-resolver.service';\nimport { EntityFormConfig } from '../types/entity-form-config';\nimport { FormNavigationEvent } from '../types/navigation-events';\nimport {\n FormNotificationEvent, RecordSavedEvent, RecordDeletedEvent,\n RecordSaveFailedEvent, ValidationFailedEvent\n} from '../types/form-types';\n\n/**\n * Presentation-agnostic host that renders ANY MemberJunction entity form —\n * generated (`@RegisterClass` / CodeGen), custom (`*Extended`), or interactive\n * (`EntityFormOverride`) — for a given record.\n *\n * This is the shared core extracted from Explorer's `SingleRecordComponent`. It\n * encapsulates the full lifecycle: **resolve** which form to use (via\n * {@link FormResolverService}, honoring variant overrides) → **load** the\n * record (or accept a pre-loaded one) → **create** the form component\n * dynamically → **bind** record / EditMode / Config / variants → **re-emit**\n * the form's events → **tear down** cleanly.\n *\n * It has ZERO routing and ZERO Explorer dependencies — it only emits events.\n * The surrounding surface (a full-page tab in Explorer, a dialog, or a\n * slide-in) decides what to do with `Navigate`, `Notification`, etc.\n *\n * @example Explorer tab (thin wrapper subscribes + maps to NavigationService)\n * ```html\n * <mj-entity-form-host [EntityName]=\"entityName\" [PrimaryKey]=\"pk\"\n * (Navigate)=\"handleNavigation($event)\" (Saved)=\"onSaved($event)\">\n * </mj-entity-form-host>\n * ```\n *\n * @example Bind a pre-loaded record (dialog/slide-in)\n * ```html\n * <mj-entity-form-host [Record]=\"myEntity\" [Config]=\"DIALOG_FORM_CONFIG\"></mj-entity-form-host>\n * ```\n */\n@Component({\n standalone: false,\n selector: 'mj-entity-form-host',\n templateUrl: './entity-form-host.component.html',\n styleUrls: ['./entity-form-host.component.css'],\n})\nexport class MjEntityFormHostComponent extends BaseAngularComponent implements AfterViewInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n private formResolver = inject(FormResolverService);\n\n /** Anchor for the dynamically-created form component. Always present in the DOM. */\n @ViewChild('anchor', { read: ViewContainerRef, static: true }) private anchor!: ViewContainerRef;\n\n // ── Inputs ──────────────────────────────────────────────────────────────\n\n private _entityName: string | null = null;\n /** Entity name to load a record for. Ignored when {@link Record} is supplied. */\n @Input()\n set EntityName(value: string | null) {\n const changed = this._entityName !== value;\n this._entityName = value;\n if (changed && this._viewInitialized) this.reload();\n }\n get EntityName(): string | null { return this._entityName; }\n\n private _primaryKey: CompositeKey = new CompositeKey();\n /** Primary key of the record to load. Empty key → new record. */\n @Input()\n set PrimaryKey(value: CompositeKey) {\n const changed = !this.compositeKeysEqual(this._primaryKey, value);\n this._primaryKey = value ?? new CompositeKey();\n if (!changed || !this._viewInitialized) return;\n // Skip reload when the incoming key just reflects the current record's now-saved PK\n // (post new-record save rebind). Genuine external swaps still reload.\n if (this._currentRecord && this.compositeKeysEqual(value, this._currentRecord.PrimaryKey)) return;\n this.reload();\n }\n get PrimaryKey(): CompositeKey { return this._primaryKey; }\n\n private _record: BaseEntity | null = null;\n /**\n * Pre-loaded record to bind directly. When supplied, the host skips the\n * load step entirely and mounts the resolved form against this instance —\n * `EntityName`/`PrimaryKey` are not required.\n */\n @Input()\n set Record(value: BaseEntity | null) {\n const changed = this._record !== value;\n this._record = value;\n if (changed && this._viewInitialized) this.reload();\n }\n get Record(): BaseEntity | null { return this._record; }\n\n /** New-record default values: URL-segment string or a plain object. */\n @Input() NewRecordValues: string | Record<string, unknown> | null = null;\n\n /**\n * Render a single registered form **section** (a `BaseFormSectionComponent`\n * registered as `'<EntityName>.<SectionName>'`) instead of the full form.\n * When set, the full-form resolver / variant picker / toolbar container are\n * bypassed — the section component renders its own fields and the host saves\n * the record directly. Used for compact, focused editors (e.g. a quick-edit\n * grid row). Leave null for the normal full-form behavior.\n */\n @Input() SectionName: string | null = null;\n\n private _editMode: boolean | null = null;\n /** Force edit mode. When omitted, new records start in edit, existing in read. */\n @Input()\n set EditMode(value: boolean | null) { this._editMode = value; }\n get EditMode(): boolean | null { return this._editMode; }\n\n /** Per-instance form presentation config (toolbar, sections, width, links). */\n @Input() Config: EntityFormConfig | null = null;\n\n // ── Outputs ─────────────────────────────────────────────────────────────\n\n /** Form navigation request (record link, new record, hierarchy, email, external, dismiss). */\n @Output() Navigate = new EventEmitter<FormNavigationEvent>();\n /** User-facing notification request (success/error/warning/info). */\n @Output() Notification = new EventEmitter<FormNotificationEvent>();\n /** Emitted after the record is successfully saved (carries the live entity). */\n @Output() Saved = new EventEmitter<BaseEntity>();\n /** Re-emitted form `RecordSaved` (richer payload). */\n @Output() RecordSaved = new EventEmitter<RecordSavedEvent>();\n /** Re-emitted form `RecordDeleted`. */\n @Output() RecordDeleted = new EventEmitter<RecordDeletedEvent>();\n /** Re-emitted form `RecordSaveFailed`. */\n @Output() RecordSaveFailed = new EventEmitter<RecordSaveFailedEvent>();\n /** Re-emitted form `ValidationFailed`. */\n @Output() ValidationFailed = new EventEmitter<ValidationFailedEvent>();\n /** Re-emitted form `RecordReady` (record fully initialized). */\n @Output() RecordReady = new EventEmitter<BaseEntity>();\n /** Form asked to be dismissed (e.g. Discard on a brand-new record). */\n @Output() Dismissed = new EventEmitter<void>();\n /** Initial load + mount finished (success path). */\n @Output() LoadComplete = new EventEmitter<void>();\n /** Load failed; carries a user-facing title + detail. */\n @Output() LoadError = new EventEmitter<{ title: string; detail: string }>();\n /** The live form instance, emitted right after it's created (for power-user wiring). */\n @Output() FormCreated = new EventEmitter<BaseFormComponent>();\n\n // ── State ───────────────────────────────────────────────────────────────\n\n public loading = true;\n public errorTitle: string | null = null;\n public errorDetail: string | null = null;\n\n private _formComponentRef: ComponentRef<BaseFormComponent> | null = null;\n private _sectionRef: ComponentRef<BaseFormSectionComponent> | null = null;\n private _isSection = false;\n private _currentRecord: BaseEntity | null = null;\n private _saveHandlerSub: Subscription | null = null;\n private _formEventSubs: Subscription[] = [];\n private _viewInitialized = false;\n\n /** The live form component instance, or null before mount / after teardown. */\n get Form(): BaseFormComponent | null {\n return this._formComponentRef?.instance ?? null;\n }\n\n /** Whether the bound record has unsaved changes. */\n get Dirty(): boolean {\n return this._currentRecord?.Dirty ?? false;\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────────────\n\n ngAfterViewInit(): void {\n this._viewInitialized = true;\n this.reload();\n }\n\n ngOnDestroy(): void {\n this.teardown();\n }\n\n // ── Public API (driven by dialog/slide-in chrome) ─────────────────────────\n\n /**\n * Save the bound record. In full-form mode this runs the form's save\n * pipeline (validation, pending records, notifications); in section mode the\n * section component has no pipeline, so we save the record directly (the\n * record's own save event still drives the `Saved` output).\n */\n async Save(): Promise<boolean> {\n if (this._isSection) {\n return this._currentRecord ? this._currentRecord.Save() : false;\n }\n const f = this.Form;\n if (!f) return false;\n return f.SaveRecord(true);\n }\n\n /** Cancel edits (revert). Full-form mode uses the form's cancel pipeline. */\n Cancel(): void {\n if (this._isSection) {\n if (this._currentRecord?.Dirty) this._currentRecord.Revert();\n return;\n }\n this.Form?.CancelEdit();\n }\n\n // ── Core: resolve → load → create → bind → wire ──────────────────────────\n\n private reload(): void {\n this.teardown();\n this.loading = true;\n this.errorTitle = null;\n this.errorDetail = null;\n void this.loadAndMount();\n }\n\n private async loadAndMount(): Promise<void> {\n const md = this.ProviderToUse;\n // When a pre-loaded record is supplied, derive the entity from it.\n const entityName = this._record?.EntityInfo?.Name ?? this._entityName;\n\n if (!entityName || entityName.trim().length === 0) {\n // Nothing to load yet — a caller will re-invoke once an entity is known.\n return;\n }\n\n try {\n const entity = md.EntityByName(entityName);\n if (!entity) {\n this.fail(`Entity not found: \"${entityName}\"`,\n `This MemberJunction instance has no metadata for entity \"${entityName}\".`);\n return;\n }\n const permissions = entity.GetUserPermisions(md.CurrentUser);\n\n // Section mode short-circuits the full-form resolver / container.\n if (this.SectionName) {\n await this.mountSection(entityName, permissions);\n return;\n }\n\n const resolution = await this.formResolver.ResolveFormForEntity(entity, md.CurrentUser, md);\n if (resolution.kind === 'none') {\n this.fail(`No form is registered for \"${entityName}\".`,\n `No EntityFormOverride or class-based form (@RegisterClass(BaseFormComponent, '${entityName}')) was found. Run CodeGen or register a custom form.`);\n return;\n }\n\n const record = await this.obtainRecord(entityName, md);\n if (!record) return; // fail() already called\n\n // Emit Saved on the record's own save event (covers all save paths).\n this._saveHandlerSub = record.RegisterEventHandler((e: BaseEntityEvent) => {\n if (e.type === 'save') this.Saved.emit(record);\n });\n\n this.anchor.clear();\n const componentRef: ComponentRef<BaseFormComponent> = resolution.kind === 'interactive'\n ? this.anchor.createComponent(InteractiveFormComponent)\n : this.anchor.createComponent(resolution.subClass);\n\n if (resolution.kind === 'interactive') {\n (componentRef as ComponentRef<InteractiveFormComponent>).instance.ComponentID = resolution.override.ComponentID;\n }\n\n this._formComponentRef = componentRef;\n this._currentRecord = record;\n\n const instance = componentRef.instance as BaseFormComponent & { userPermissions?: unknown };\n instance.record = record;\n instance.userPermissions = permissions;\n instance.Config = this.effectiveConfigFor(record);\n instance.EditMode = this._editMode ?? this.Config?.StartInEditMode ?? !record.IsSaved;\n\n this.applyVariants(instance, resolution, entityName);\n this.subscribeToFormEvents(instance);\n\n this.FormCreated.emit(instance);\n this.errorTitle = null;\n this.errorDetail = null;\n this.LoadComplete.emit();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n this.fail(`Failed to load ${entityName} record.`, `An unexpected error occurred: ${msg}`);\n } finally {\n this.loading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * The config to bind for a given record. For brand-new (unsaved) records we hide the\n * `systemMetadata` section by default — its fields (audit timestamps, IDs, etc.) are\n * empty and irrelevant until the record exists. Doesn't mutate the consumer's config,\n * and defers entirely to a consumer that uses an explicit `VisibleSectionKeys` allowlist.\n */\n private effectiveConfigFor(record: BaseEntity): EntityFormConfig | null {\n if (record.IsSaved) return this.Config;\n if (this.Config?.VisibleSectionKeys?.length) return this.Config; // consumer owns visibility\n const SYSTEM_METADATA_KEY = 'systemMetadata';\n const hidden = this.Config?.HiddenSectionKeys ?? [];\n if (hidden.includes(SYSTEM_METADATA_KEY)) return this.Config;\n return { ...(this.Config ?? {}), HiddenSectionKeys: [...hidden, SYSTEM_METADATA_KEY] };\n }\n\n /**\n * Section mode: resolve a `BaseFormSectionComponent` registered as\n * `'<entity>.<section>'`, mount it, and bind the record. No resolver,\n * variants, container, or form-event surface — section components are bare\n * field editors; the host saves the record directly (see {@link Save}).\n */\n private async mountSection(entityName: string, permissions: unknown): Promise<void> {\n const key = `${entityName}.${this.SectionName}`;\n const reg = MJGlobal.Instance.ClassFactory.GetRegistration(BaseFormSectionComponent, key);\n if (!reg?.SubClass) {\n this.fail(`No form section \"${this.SectionName}\" is registered for \"${entityName}\".`,\n `Expected a @RegisterClass(BaseFormSectionComponent, '${key}'). Check the section name.`);\n return;\n }\n\n const record = await this.obtainRecord(entityName);\n if (!record) return;\n\n this._saveHandlerSub = record.RegisterEventHandler((e: BaseEntityEvent) => {\n if (e.type === 'save') this.Saved.emit(record);\n });\n\n this.anchor.clear();\n const ref = this.anchor.createComponent(reg.SubClass as Type<BaseFormSectionComponent>);\n this._sectionRef = ref;\n this._currentRecord = record;\n this._isSection = true;\n\n const instance = ref.instance as BaseFormSectionComponent & { userPermissions?: unknown };\n instance.record = record;\n instance.userPermissions = permissions;\n instance.EditMode = this._editMode ?? this.Config?.StartInEditMode ?? !record.IsSaved;\n\n this.RecordReady.emit(record);\n this.LoadComplete.emit();\n }\n\n /** Resolve the BaseEntity to bind: the supplied instance, or a freshly loaded/new one. */\n private async obtainRecord(entityName: string, md = this.ProviderToUse): Promise<BaseEntity | null> {\n if (this._record) return this._record;\n\n const record = await md.GetEntityObject<BaseEntity>(entityName, md.CurrentUser);\n if (!record) {\n this.fail(`Failed to load ${entityName} record.`, `Unable to instantiate entity \"${entityName}\".`);\n return null;\n }\n\n if (this._primaryKey.HasValue) {\n const loadOk = await record.InnerLoad(this._primaryKey);\n if (!loadOk) {\n this.fail(`Could not load ${entityName} record.`,\n record.LatestResult?.Message\n ? `Server error: ${record.LatestResult.Message}`\n : `InnerLoad returned false for key ${this._primaryKey.ToString()}.`);\n return null;\n }\n } else {\n record.NewRecord();\n this.applyNewRecordValues(record);\n }\n return record;\n }\n\n /** Push variant list + active selection and wire the variant-switch handler. */\n private applyVariants(\n instance: BaseFormComponent,\n resolution: Awaited<ReturnType<FormResolverService['ResolveFormForEntity']>>,\n entityName: string,\n ): void {\n instance.Variants = (resolution.variants ?? [])\n .filter(v => v.Status === 'Active')\n .map(v => ({ ID: v.ID, Label: v.Name ?? `Override ${v.ID.substring(0, 8)}`, Scope: v.Scope, Status: v.Status }));\n instance.CurrentVariantID = resolution.kind === 'interactive' ? resolution.override.ID : null;\n instance.OnVariantChanged = (variantID: string | null) => {\n if (variantID === null) {\n this.formResolver.SetExplicitDefault(entityName);\n } else {\n this.formResolver.SetSelectedVariant(entityName, variantID);\n }\n // Reload via the resolver so tier/priority + the new selection apply.\n this._record = null; // force a fresh load against the new variant\n this.reload();\n };\n }\n\n /** Subscribe to the form's outputs and re-emit them through the host. */\n private subscribeToFormEvents(form: BaseFormComponent): void {\n this.cleanupFormSubs();\n this._formEventSubs.push(\n form.Navigate.subscribe(e => this.Navigate.emit(e)),\n form.Notification.subscribe(e => this.Notification.emit(e)),\n form.RecordSaved.subscribe(e => this.RecordSaved.emit(e)),\n form.RecordDeleted.subscribe(e => this.RecordDeleted.emit(e)),\n form.RecordSaveFailed.subscribe(e => this.RecordSaveFailed.emit(e)),\n form.ValidationFailed.subscribe(e => this.ValidationFailed.emit(e)),\n form.RecordReady.subscribe(e => this.RecordReady.emit(e)),\n );\n // Surface 'dismiss' navigation as a first-class Dismissed event too.\n this._formEventSubs.push(\n form.Navigate.subscribe(e => { if (e.Kind === 'dismiss') this.Dismissed.emit(); }),\n );\n }\n\n // ── New-record value application (URL segment or object) ──────────────────\n\n private applyNewRecordValues(record: BaseEntity): void {\n if (!this.NewRecordValues) return;\n if (typeof this.NewRecordValues === 'string') {\n if (this.NewRecordValues.length === 0) return;\n const fv = new FieldValueCollection();\n fv.SimpleLoadFromURLSegment(this.NewRecordValues);\n fv.KeyValuePairs\n .filter(kvp => kvp.Value !== null && kvp.Value !== undefined)\n .forEach(kvp => this.setTypedField(record, kvp.FieldName, kvp.Value));\n } else {\n const values = this.NewRecordValues as Record<string, unknown>;\n Object.keys(values)\n .filter(key => values[key] !== null && values[key] !== undefined)\n .forEach(key => this.setTypedField(record, key, values[key]));\n }\n }\n\n /** Set a field with TSType-aware coercion (shared by string + object paths). */\n private setTypedField(record: BaseEntity, fieldName: string, value: unknown): void {\n const f = record.Fields.find(x => x.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());\n if (!f) return;\n switch (f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.String:\n record.Set(fieldName, value?.toString() ?? '');\n break;\n case EntityFieldTSType.Number:\n record.Set(fieldName, typeof value === 'number' ? value : parseFloat(value?.toString() ?? '0'));\n break;\n case EntityFieldTSType.Boolean:\n if (typeof value === 'boolean') record.Set(fieldName, value);\n else if (typeof value === 'string') record.Set(fieldName, value !== 'false' && value !== '0' && value.trim().length > 0);\n else record.Set(fieldName, !!value);\n break;\n case EntityFieldTSType.Date:\n record.Set(fieldName, value instanceof Date ? value : new Date(value?.toString() ?? ''));\n break;\n default:\n record.Set(fieldName, value);\n break;\n }\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────────\n\n private fail(title: string, detail: string): void {\n this.errorTitle = title;\n this.errorDetail = detail;\n this.loading = false;\n if (this._formComponentRef) {\n try { this._formComponentRef.destroy(); } catch { /* noop */ }\n this._formComponentRef = null;\n }\n this.anchor?.clear();\n this.LoadError.emit({ title, detail });\n this.cdr.detectChanges();\n }\n\n private compositeKeysEqual(a: CompositeKey | null | undefined, b: CompositeKey | null | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const aKvps = (a.KeyValuePairs || []).filter(kvp => String(kvp.Value ?? '').length > 0);\n const bKvps = (b.KeyValuePairs || []).filter(kvp => String(kvp.Value ?? '').length > 0);\n if (aKvps.length !== bKvps.length) return false;\n for (let i = 0; i < aKvps.length; i++) {\n if (aKvps[i].FieldName !== bKvps[i].FieldName) return false;\n if (String(aKvps[i].Value ?? '') !== String(bKvps[i].Value ?? '')) return false;\n }\n return true;\n }\n\n private cleanupFormSubs(): void {\n for (const sub of this._formEventSubs) sub.unsubscribe();\n this._formEventSubs = [];\n }\n\n private teardown(): void {\n this.cleanupFormSubs();\n if (this._saveHandlerSub) {\n this._saveHandlerSub.unsubscribe();\n this._saveHandlerSub = null;\n }\n if (this._formComponentRef) {\n try { this._formComponentRef.destroy(); } catch { /* noop */ }\n this._formComponentRef = null;\n }\n if (this._sectionRef) {\n try { this._sectionRef.destroy(); } catch { /* noop */ }\n this._sectionRef = null;\n }\n this._isSection = false;\n this._currentRecord = null;\n this.anchor?.clear();\n }\n}\n","<!-- Loading state -->\n@if (loading) {\n <div class=\"mj-form-host-loading\">\n <mj-loading text=\"Loading record...\"></mj-loading>\n </div>\n}\n\n<!-- Error state -->\n@if (!loading && errorTitle) {\n <div class=\"mj-form-host-error\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation mj-form-host-error-icon\"></i>\n <h3 class=\"mj-form-host-error-title\">{{ errorTitle }}</h3>\n <p class=\"mj-form-host-error-detail\">{{ errorDetail }}</p>\n </div>\n}\n\n<!--\n Anchor for the dynamically-created form component. Always present (static)\n so the ViewContainerRef resolves at AfterViewInit. The form's own template\n carries the <mj-record-form-container>, so nothing wraps it here.\n-->\n<ng-template #anchor></ng-template>\n"]}