@vectoriox/iox-builder 2.6.0 → 2.7.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.
@@ -5057,6 +5057,9 @@ class BuilderComponent {
5057
5057
  if (inGlobal)
5058
5058
  this.globalElementsChange.emit([...this.globalElements]);
5059
5059
  this.cdr.detectChanges();
5060
+ // If the wrapped node lives in a Repeater template, rebuild its repeated rows so
5061
+ // every clone picks up the new wrapper (and re-resolves the now-nested bindings).
5062
+ this._refreshRepeaterFor(node);
5060
5063
  this.panelEventService.emit(PanelEventTypes.LAYOUT_CHANGED, this.layout);
5061
5064
  this._selectAfterDrop(wrapped);
5062
5065
  }
@@ -5324,6 +5327,17 @@ class BuilderComponent {
5324
5327
  }
5325
5328
  return null;
5326
5329
  }
5330
+ /** If `node` lives inside a Repeater template, tell that repeater to rebuild its preview rows so
5331
+ * a structural change (e.g. wrapping a template child) propagates to every repeated row. No-op
5332
+ * when the node is not inside a repeater. */
5333
+ _refreshRepeaterFor(node) {
5334
+ const repeater = this.findRepeaterAncestorOf(node, this.layout)
5335
+ ?? this.findRepeaterAncestorOf(node, this.globalElements);
5336
+ if (!repeater)
5337
+ return;
5338
+ const ref = this.overlayService.getNodeRef(repeater);
5339
+ ref?.componentRef?.instance?.refreshTemplate?.();
5340
+ }
5327
5341
  /** Emit ELEMENT_SELECT with the ancestors the panels need (symbol + repeater item scope). */
5328
5342
  _emitElementSelect(node, componentRef) {
5329
5343
  const symbolAncestor = this.findSymbolAncestorOf(node, this.layout)
@@ -6071,6 +6085,10 @@ class BuilderRepeaterComponent {
6071
6085
  this._source = '';
6072
6086
  this._sourcePath = '';
6073
6087
  this._limit = 10;
6088
+ /** Monotonic fetch token — editing a binding sets `source` and `sourcePath` as two separate inputs,
6089
+ * each firing a fetch; the transient mismatched one (old source + new path, or vice-versa) must not
6090
+ * clobber the correct result if it resolves last. Only the newest fetch's response is applied. */
6091
+ this._fetchSeq = 0;
6074
6092
  }
6075
6093
  ngOnInit() {
6076
6094
  this.bindingSub = this.panelEventService.subscribe(event => {
@@ -6096,11 +6114,14 @@ class BuilderRepeaterComponent {
6096
6114
  fetchData() {
6097
6115
  if (!this._source)
6098
6116
  return;
6117
+ const seq = ++this._fetchSeq;
6099
6118
  this.isLoading = true;
6100
6119
  this.cdr.markForCheck();
6101
6120
  // Resolve BY ALIAS so `derived` sources (e.g. galleryImages = product.gallery) follow their
6102
6121
  // parent. `sourcePath` (legacy/back-compat) still repeats over an array property inline.
6103
6122
  this.dataSourceService.fetchRepeaterItemsByAlias(this._source, (a) => this.dsRegistry.findByAlias(a), this._sourcePath).subscribe(items => {
6123
+ if (seq !== this._fetchSeq)
6124
+ return; // a newer fetch superseded this one — ignore stale items
6104
6125
  this.items = items.slice(0, this._limit);
6105
6126
  this.refreshPreviews();
6106
6127
  this.isLoading = false;
@@ -6126,21 +6147,36 @@ class BuilderRepeaterComponent {
6126
6147
  const item = this.items[0];
6127
6148
  if (!item || !this.children.length)
6128
6149
  return;
6129
- for (const child of this.children) {
6130
- if (!child.bindings?.length)
6131
- continue;
6132
- const ref = this.overlayService.getNodeRef(child);
6133
- if (!ref?.componentRef)
6134
- continue;
6135
- for (const binding of child.bindings) {
6136
- const value = resolvePath(item, binding.path);
6137
- if (value !== undefined) {
6138
- ref.componentRef.instance[binding.prop] = value;
6150
+ this.applyBindingsToNodes(this.children, item);
6151
+ }
6152
+ /** Apply item[0]'s bound values to the live template row, walking the WHOLE subtree — not just
6153
+ * direct children. A bound node can sit any level deep once a template child is wrapped in a
6154
+ * container (or otherwise nested), so recursion is required or the first row shows defaults. */
6155
+ applyBindingsToNodes(nodes, item) {
6156
+ for (const child of nodes) {
6157
+ if (child.bindings?.length) {
6158
+ const ref = this.overlayService.getNodeRef(child);
6159
+ if (ref?.componentRef) {
6160
+ for (const binding of child.bindings) {
6161
+ const value = resolvePath(item, binding.path);
6162
+ if (value !== undefined) {
6163
+ ref.componentRef.instance[binding.prop] = value;
6164
+ }
6165
+ }
6166
+ ref.componentRef.changeDetectorRef.detectChanges();
6139
6167
  }
6140
6168
  }
6141
- ref.componentRef.changeDetectorRef.detectChanges();
6169
+ if (child.children?.length) {
6170
+ this.applyBindingsToNodes(child.children, item);
6171
+ }
6142
6172
  }
6143
6173
  }
6174
+ /** Rebuild preview rows after the template STRUCTURE changed (e.g. a child was wrapped, deleted,
6175
+ * or moved). Deferred so the new DOM + overlay refs exist before bindings are re-applied. */
6176
+ refreshTemplate() {
6177
+ if (this.items.length)
6178
+ setTimeout(() => this.refreshPreviews());
6179
+ }
6144
6180
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: BuilderRepeaterComponent, deps: [{ token: DragEngineService }, { token: OverlayService }, { token: BuilderDataSourceService }, { token: DataSourceRegistryService }, { token: PanelEventService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
6145
6181
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: BuilderRepeaterComponent, isStandalone: false, selector: "app-builder-repeater", inputs: { children: "children", style: "style", nodeId: "nodeId", dropListId: "dropListId", source: "source", sourcePath: "sourcePath", limit: "limit" }, outputs: { childClick: "childClick", childMouseEnter: "childMouseEnter", childMouseLeave: "childMouseLeave" }, ngImport: i0, template: `
6146
6182
  <ng-template #badgeContent>