@acorex/platform 20.0.20 → 20.0.22

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.
@@ -694,6 +694,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImpor
694
694
  }] });
695
695
 
696
696
  class AXPWidgetRendererDirective {
697
+ //#endregion
697
698
  constructor() {
698
699
  this.parentNode = input();
699
700
  this.index = input();
@@ -703,6 +704,7 @@ class AXPWidgetRendererDirective {
703
704
  this.options = this._options.asReadonly();
704
705
  this.onOptionsChanged = output();
705
706
  this.onValueChanged = output();
707
+ //#region ---- Properties ----
706
708
  this.mergedOptions = signal({});
707
709
  this.injector = inject(Injector);
708
710
  this.builderService = inject(AXPLayoutBuilderService);
@@ -715,9 +717,15 @@ class AXPWidgetRendererDirective {
715
717
  this.viewContainerRef = inject(ViewContainerRef);
716
718
  this.isLoading = signal(false);
717
719
  this.expressionEvaluators = new Map();
720
+ this.renderTimeoutId = null;
721
+ this.hasInitialRender = false;
718
722
  this.onContextChanged = new Subject();
719
723
  effect(async () => {
720
724
  const changed = this.contextService.changeEvent();
725
+ // Don't trigger re-render during initial setup
726
+ if (!this.hasInitialRender) {
727
+ return;
728
+ }
721
729
  if ((await this.updateOptionsBasedOnContext()) > 0) {
722
730
  this.applyOptions();
723
731
  }
@@ -743,13 +751,21 @@ class AXPWidgetRendererDirective {
743
751
  }
744
752
  }
745
753
  rerenderComponent() {
746
- this.isLoading.set(false);
747
- // Call loadComponent to create and render the component
748
- setTimeout(async () => {
754
+ // Clear any pending render operation
755
+ if (this.renderTimeoutId) {
756
+ clearTimeout(this.renderTimeoutId);
757
+ this.renderTimeoutId = null;
758
+ }
759
+ // Don't reset loading state here to prevent race conditions
760
+ this.renderTimeoutId = setTimeout(async () => {
749
761
  await this.loadComponent();
762
+ this.renderTimeoutId = null;
750
763
  });
751
764
  }
752
765
  ngOnDestroy() {
766
+ if (this.renderTimeoutId) {
767
+ clearTimeout(this.renderTimeoutId);
768
+ }
753
769
  if (this.componentRef) {
754
770
  this.componentRef.destroy();
755
771
  }
@@ -759,68 +775,76 @@ class AXPWidgetRendererDirective {
759
775
  return;
760
776
  }
761
777
  this.isLoading.set(true);
762
- // Destroy the existing component if it exists
763
- if (this.componentRef) {
764
- this.componentRef.destroy();
778
+ try {
779
+ // Destroy the existing component if it exists
780
+ if (this.componentRef) {
781
+ this.componentRef.destroy();
782
+ }
783
+ this.viewContainerRef.clear();
784
+ //
785
+ const widget = this.widgetRegistery.resolve(this.node().type);
786
+ //
787
+ const props = [...(widget?.properties ?? []), ...(widget?.components[this.mode()]?.properties ?? [])]
788
+ ?.filter((c) => c.schema.defaultValue != null)
789
+ .map((c) => ({ [c.name]: cloneDeep(c.schema.defaultValue) }))
790
+ .reduce((acc, curr) => {
791
+ return { ...acc, ...curr };
792
+ }, {});
793
+ //
794
+ this.mergedOptions.set(merge(props, widget?.options, this.node().options) || {});
795
+ this.preprocessAndInitialOptions(cloneDeep(this.node().options));
796
+ await this.updateOptionsBasedOnContext();
797
+ //
798
+ this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
799
+ //
800
+ const tokenValue = {
801
+ node: this.node(),
802
+ options: this.mergedOptions(),
803
+ config: widget,
804
+ };
805
+ const token = Injector.create({
806
+ parent: this.injector,
807
+ providers: [
808
+ {
809
+ provide: AXP_WIDGET_TOKEN,
810
+ useValue: tokenValue,
811
+ },
812
+ ],
813
+ });
814
+ //
815
+ const loadingRef = this.viewContainerRef.createComponent(AXPWidgetPlaceholderComponent);
816
+ //
817
+ const com = await widget?.components[this.mode()]?.component();
818
+ if (!com) {
819
+ console.error(`${this.node().type} widget component not found with mode: ${this.mode()}`);
820
+ return;
821
+ }
822
+ this.componentRef = this.viewContainerRef.createComponent(com, { injector: token });
823
+ this.instance = this.componentRef.instance;
824
+ this.instance.setStatus(AXPWidgetStatus.Rendering);
825
+ this.instance.parent = this.parentNode();
826
+ this.instance.index = this.index();
827
+ this.instance.mode = this.mode();
828
+ this.instance.setStatus(AXPWidgetStatus.Rendered);
829
+ this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
830
+ this.onOptionsChanged.emit({ sender: this, widget: c.sender });
831
+ });
832
+ this.instance?.onValueChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
833
+ this.onValueChanged.emit({ sender: this, widget: c.sender });
834
+ });
835
+ await this.updateValueBasedOnFormula();
836
+ await this.assignTriggers();
837
+ //
838
+ loadingRef.destroy();
839
+ // Mark that initial render is complete
840
+ this.hasInitialRender = true;
765
841
  }
766
- this.viewContainerRef.clear();
767
- //
768
- const widget = this.widgetRegistery.resolve(this.node().type);
769
- //
770
- const props = [...(widget?.properties ?? []), ...(widget?.components[this.mode()]?.properties ?? [])]
771
- ?.filter((c) => c.schema.defaultValue != null)
772
- .map((c) => ({ [c.name]: cloneDeep(c.schema.defaultValue) }))
773
- .reduce((acc, curr) => {
774
- return { ...acc, ...curr };
775
- }, {});
776
- //
777
- this.mergedOptions.set(merge(props, widget?.options, this.node().options) || {});
778
- this.preprocessAndInitialOptions(cloneDeep(this.node().options));
779
- await this.updateOptionsBasedOnContext();
780
- //
781
- this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
782
- //
783
- const tokenValue = {
784
- node: this.node(),
785
- options: this.mergedOptions(),
786
- config: widget,
787
- };
788
- const token = Injector.create({
789
- parent: this.injector,
790
- providers: [
791
- {
792
- provide: AXP_WIDGET_TOKEN,
793
- useValue: tokenValue,
794
- },
795
- ],
796
- });
797
- //
798
- const loadingRef = this.viewContainerRef.createComponent(AXPWidgetPlaceholderComponent);
799
- //
800
- const com = await widget?.components[this.mode()]?.component();
801
- if (!com) {
802
- console.error(`${this.node().type} widget component not found with mode: ${this.mode()}`);
803
- return;
842
+ catch (error) {
843
+ console.error('Error loading component:', error);
844
+ }
845
+ finally {
846
+ this.isLoading.set(false);
804
847
  }
805
- this.componentRef = this.viewContainerRef.createComponent(com, { injector: token });
806
- this.instance = this.componentRef.instance;
807
- this.instance.setStatus(AXPWidgetStatus.Rendering);
808
- this.instance.parent = this.parentNode();
809
- this.instance.index = this.index();
810
- this.instance.mode = this.mode();
811
- this.instance.setStatus(AXPWidgetStatus.Rendered);
812
- this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
813
- this.onOptionsChanged.emit({ sender: this, widget: c.sender });
814
- });
815
- this.instance?.onValueChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
816
- this.onValueChanged.emit({ sender: this, widget: c.sender });
817
- });
818
- await this.updateValueBasedOnFormula();
819
- await this.assignTriggers();
820
- //
821
- loadingRef.destroy();
822
- this.isLoading.set(false);
823
- //
824
848
  }
825
849
  applyOptions() {
826
850
  if (!this.instance)