diff --git a/packages/sitecore-jss-angular/src/components/form.component.ts b/packages/sitecore-jss-angular/src/components/form.component.ts index e017236438f..eca48c3f558 100644 --- a/packages/sitecore-jss-angular/src/components/form.component.ts +++ b/packages/sitecore-jss-angular/src/components/form.component.ts @@ -65,6 +65,7 @@ export class FormComponent implements OnInit, OnDestroy { * The rendering data for the component */ @Input() rendering: FormRendering; + @Input() data: unknown; hasError = false; diff --git a/packages/sitecore-jss-angular/src/components/missing-component.component.ts b/packages/sitecore-jss-angular/src/components/missing-component.component.ts index 03026090f5c..42a85a6e9c5 100644 --- a/packages/sitecore-jss-angular/src/components/missing-component.component.ts +++ b/packages/sitecore-jss-angular/src/components/missing-component.component.ts @@ -14,4 +14,5 @@ import { ComponentRendering } from '@sitecore-jss/sitecore-jss/layout'; }) export class MissingComponentComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; } diff --git a/packages/sitecore-jss-angular/src/components/placeholder.component.spec.ts b/packages/sitecore-jss-angular/src/components/placeholder.component.spec.ts index ec7af1e1196..59e1f5a6464 100644 --- a/packages/sitecore-jss-angular/src/components/placeholder.component.spec.ts +++ b/packages/sitecore-jss-angular/src/components/placeholder.component.spec.ts @@ -1,35 +1,36 @@ +import { Location } from '@angular/common'; import { Component, DebugElement, EventEmitter, Injectable, + input, Input, Output, TemplateRef, ViewChild, } from '@angular/core'; -import { RedirectCommand, Router } from '@angular/router'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { Location } from '@angular/common'; import { By } from '@angular/platform-browser'; +import { RedirectCommand, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { ComponentRendering } from '@sitecore-jss/sitecore-jss/layout'; import { JssModule } from '../lib.module'; +import { JssStateService } from '../services/jss-state.service'; +import { JssCanActivate, JssCanActivateFn, JssResolve } from '../services/placeholder.token'; import { convertedData as eeData } from '../test-data/ee-data'; +import * as lazyLoadingData from '../test-data/lazy-loading/data'; +import { LazyComponent } from '../test-data/lazy-loading/lazy-component.component'; +import * as metadataData from '../test-data/metadata-data'; import { convertedDevData as nonEeDevData, convertedLayoutServiceData as nonEeLsData, sxaRenderingData, - sxaRenderingDynamicPlaceholderData, sxaRenderingDoubleDigitDynamicPlaceholderData, + sxaRenderingDynamicPlaceholderData, } from '../test-data/non-ee-data'; -import * as metadataData from '../test-data/metadata-data'; -import { LazyComponent } from '../test-data/lazy-loading/lazy-component.component'; -import { JssCanActivate, JssCanActivateFn, JssResolve } from '../services/placeholder.token'; -import * as lazyLoadingData from '../test-data/lazy-loading/data'; -import { MissingComponentComponent } from './missing-component.component'; -import { JssStateService } from '../services/jss-state.service'; import { cleanHtml } from '../test-utils'; +import { MissingComponentComponent } from './missing-component.component'; @Component({ selector: 'test-placeholder', @@ -42,16 +43,19 @@ import { cleanHtml } from '../test-utils'; class TestPlaceholderComponent { @Input() rendering: ComponentRendering; @Input() name: string; + @Input() data: unknown; } @Component({ selector: 'test-download-callout', + standalone: true, template: ` - {{ rendering?.fields?.linkText?.value }} + {{ rendering()?.fields?.linkText?.value }} `, }) class TestDownloadCalloutComponent { - @Input() rendering: ComponentRendering; + rendering = input.required(); + data = input(); } @Component({ @@ -64,13 +68,17 @@ class TestDownloadCalloutComponent { }) class TestHomeComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; } @Component({ selector: 'test-jumbotron', template: '', }) -class TestJumbotronComponent {} +class TestJumbotronComponent { + @Input() rendering: ComponentRendering; + @Input() data: unknown; +} describe('', () => { let fixture: ComponentFixture; @@ -80,13 +88,9 @@ describe('', () => { beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ - TestPlaceholderComponent, - TestDownloadCalloutComponent, - TestHomeComponent, - TestJumbotronComponent, - ], + declarations: [TestPlaceholderComponent, TestHomeComponent, TestJumbotronComponent], imports: [ + TestDownloadCalloutComponent, RouterTestingModule, JssModule.withComponents( [ @@ -345,6 +349,7 @@ describe('', () => { }) class TestParentComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; @Input() name: string; clickMessage = ''; public inputs = { @@ -370,6 +375,8 @@ class TestParentComponent { `, }) class TestChildComponent { + @Input() rendering: ComponentRendering; + @Input() data: unknown; @Input() childMessage: string; @Input() childNumber: number; @Output() childEvent: EventEmitter = new EventEmitter(); @@ -725,6 +732,7 @@ describe(' with lazy loaded modules', () => { }) class TestRichTextComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; @ViewChild('default', { static: true }) defaultVariant: TemplateRef; @ViewChild('withTitle', { static: true }) withTitleVariant: TemplateRef; public get variant(): TemplateRef { @@ -880,6 +888,7 @@ describe('Placeholder Metadata:', () => { }) class TestNestingComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; nestedRendering: ComponentRendering = layoutData.sitecore.route.placeholders.main[0]; } @@ -891,6 +900,7 @@ describe('Placeholder Metadata:', () => { }) class LogoComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; } let fixture: ComponentFixture; @@ -1091,6 +1101,7 @@ describe('Placeholder Metadata: dynamic placeholder:', () => { }) class TestNestingComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; nestedRendering: ComponentRendering = layoutData.sitecore.route.placeholders.main[0]; } @@ -1102,6 +1113,7 @@ describe('Placeholder Metadata: dynamic placeholder:', () => { }) class LogoComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; } let fixture: ComponentFixture; diff --git a/packages/sitecore-jss-angular/src/components/placeholder.component.ts b/packages/sitecore-jss-angular/src/components/placeholder.component.ts index d2584b98a25..daef6d2cd08 100644 --- a/packages/sitecore-jss-angular/src/components/placeholder.component.ts +++ b/packages/sitecore-jss-angular/src/components/placeholder.component.ts @@ -3,6 +3,7 @@ import { isPlatformServer } from '@angular/common'; import { ChangeDetectorRef, Component, + ComponentRef, ContentChild, DoCheck, ElementRef, @@ -123,7 +124,7 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr chromeType: string; private _inputs: { [key: string]: unknown }; private _differ: KeyValueDiffer; - private _componentInstances: { [prop: string]: unknown }[] = []; + private _componentRefs: ComponentRef[] = []; private placeholderData?: (ComponentRendering | HtmlElementRendering)[]; private destroyed = false; private parentStyleAttribute = ''; @@ -176,7 +177,7 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr ngOnDestroy() { this.destroyed = true; - this._componentInstances = []; + this._componentRefs = []; if (this.contextSubscription) { this.contextSubscription.unsubscribe(); } @@ -191,7 +192,7 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr } ngDoCheck() { - if (!this._differ || !this._inputs || this._componentInstances.length === 0) { + if (!this._differ || !this._inputs || this._componentRefs.length === 0) { return; } @@ -203,7 +204,7 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr changes.forEachRemovedItem((change) => (updates[change.key] = null)); changes.forEachAddedItem((change) => (updates[change.key] = change.currentValue)); changes.forEachChangedItem((change) => (updates[change.key] = change.currentValue)); - this._componentInstances.forEach((componentInstance) => + this._componentRefs.forEach((componentInstance) => this._setComponentInputs(componentInstance, updates) ); } @@ -285,11 +286,11 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr } private _setComponentInputs( - componentInstance: { [key: string]: unknown }, + componentRef: ComponentRef, inputs: { [key: string]: unknown } ) { - Object.entries(inputs).forEach( - ([input, inputValue]) => (componentInstance[input] = inputValue) + Object.entries(inputs).forEach(([input, inputValue]) => + componentRef.setInput(input, inputValue) ); } @@ -313,7 +314,7 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr return; } - this._componentInstances = []; + this._componentRefs = []; this.view.clear(); if (!this.rendering && !this.renderings) { @@ -448,15 +449,16 @@ export class PlaceholderComponent implements OnInit, OnChanges, DoCheck, OnDestr } const componentInstance = createdComponentRef.instance; - componentInstance.rendering = rendering.componentDefinition; - componentInstance.data = data; - + createdComponentRef.setInput('rendering', rendering.componentDefinition); + if (data) { + createdComponentRef.setInput('data', data); + } if (this._inputs) { - this._setComponentInputs(componentInstance, this._inputs); + this._setComponentInputs(createdComponentRef, this._inputs); } if (this.outputs) { this._subscribeComponentOutputs(componentInstance, this.outputs); } - this._componentInstances.push(componentInstance); + this._componentRefs.push(createdComponentRef); } } diff --git a/packages/sitecore-jss-angular/src/components/raw.component.ts b/packages/sitecore-jss-angular/src/components/raw.component.ts index 63a2e5a4a42..58d3ea9fa5c 100644 --- a/packages/sitecore-jss-angular/src/components/raw.component.ts +++ b/packages/sitecore-jss-angular/src/components/raw.component.ts @@ -7,6 +7,7 @@ import { HtmlElementRendering } from '@sitecore-jss/sitecore-jss/layout'; }) export class RawComponent implements OnInit { @Input() rendering: HtmlElementRendering; + @Input() data: unknown; constructor(private renderer: Renderer2, private elementRef: ElementRef) {} diff --git a/packages/sitecore-jss-angular/src/components/render-component.component.ts b/packages/sitecore-jss-angular/src/components/render-component.component.ts index 56a6b5ff466..3089fd96765 100644 --- a/packages/sitecore-jss-angular/src/components/render-component.component.ts +++ b/packages/sitecore-jss-angular/src/components/render-component.component.ts @@ -1,5 +1,6 @@ import { Component, + ComponentRef, Inject, Input, KeyValueDiffer, @@ -62,12 +63,11 @@ export class RenderComponentComponent implements OnChanges { } private _setComponentInputs( - componentInstance: { [key: string]: unknown }, + componentRef: ComponentRef, inputs: { [key: string]: unknown } ) { - Object.entries(inputs).forEach( - ([input, inputValue]) => - ((componentInstance as { [prop: string]: unknown })[input] = inputValue) + Object.entries(inputs).forEach(([input, inputValue]) => + componentRef.setInput(input, inputValue) ); } @@ -114,14 +114,13 @@ export class RenderComponentComponent implements OnChanges { rendering.componentImplementation = this.missingComponentComponent; } - const componentInstance = this.view.createComponent(rendering.componentImplementation) - .instance; - componentInstance.rendering = rendering.componentDefinition; + const componentRef = this.view.createComponent(rendering.componentImplementation); + componentRef.setInput('rendering', rendering.componentDefinition); if (this._inputs) { - this._setComponentInputs(componentInstance, this._inputs); + this._setComponentInputs(componentRef, this._inputs); } if (this.outputs) { - this._subscribeComponentOutputs(componentInstance, this.outputs); + this._subscribeComponentOutputs(componentRef.instance, this.outputs); } }); } diff --git a/packages/sitecore-jss-angular/src/test-data/lazy-loading/lazy-component.component.ts b/packages/sitecore-jss-angular/src/test-data/lazy-loading/lazy-component.component.ts index d04573dc235..ab5585b953a 100644 --- a/packages/sitecore-jss-angular/src/test-data/lazy-loading/lazy-component.component.ts +++ b/packages/sitecore-jss-angular/src/test-data/lazy-loading/lazy-component.component.ts @@ -14,6 +14,7 @@ import { Component, Input } from '@angular/core'; }) export class LazyComponent { @Input() rendering: ComponentRendering; + @Input() data: unknown; constructor(private mockService: MockService) {}