diff --git a/projects/dashboards-demo/webcomponents/src/app/chart-widget/chart-widget.component.ts b/projects/dashboards-demo/webcomponents/src/app/chart-widget/chart-widget.component.ts index 427aadd51..2221b7262 100644 --- a/projects/dashboards-demo/webcomponents/src/app/chart-widget/chart-widget.component.ts +++ b/projects/dashboards-demo/webcomponents/src/app/chart-widget/chart-widget.component.ts @@ -4,7 +4,7 @@ */ import { AsyncPipe } from '@angular/common'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, input, OnInit } from '@angular/core'; import { SiChartCartesianComponent } from '@siemens/charts-ng/cartesian'; import { WidgetConfig, WidgetInstance } from '@siemens/dashboards-ng'; import { ContentActionBarMainItem } from '@siemens/element-ng/content-action-bar'; @@ -25,7 +25,7 @@ export interface WidgetChartCartesianConfig { templateUrl: './chart-widget.component.html' }) export class ChartWidgetComponent implements OnInit, WidgetInstance { - @Input() config!: WidgetConfig; + readonly config = input.required(); primaryActions: ContentActionBarMainItem[] = [ { type: 'action', label: 'Print', action: () => alert('do print') } ]; @@ -45,7 +45,7 @@ export class ChartWidgetComponent implements OnInit, WidgetInstance { } get cartesianConfig(): WidgetChartCartesianConfig { - return this.config.payload.config as WidgetChartCartesianConfig; + return this.config().payload.config as WidgetChartCartesianConfig; } private getCartesianChartData(): Observable { diff --git a/projects/dashboards-demo/webcomponents/src/app/contact-widget-editor/contact-widget-editor.component.ts b/projects/dashboards-demo/webcomponents/src/app/contact-widget-editor/contact-widget-editor.component.ts index 43d699d0a..3d625990b 100644 --- a/projects/dashboards-demo/webcomponents/src/app/contact-widget-editor/contact-widget-editor.component.ts +++ b/projects/dashboards-demo/webcomponents/src/app/contact-widget-editor/contact-widget-editor.component.ts @@ -8,7 +8,7 @@ import { DestroyRef, HostListener, inject, - Input, + model, output, signal } from '@angular/core'; @@ -41,10 +41,7 @@ import { SiFormModule } from '@siemens/element-ng/form'; templateUrl: './contact-widget-editor.component.html' }) export class ContactWidgetEditorComponent implements WidgetInstanceEditorWizard, AfterViewInit { - @Input({ - required: true - }) - config!: WidgetConfig; + readonly config = model.required>(); readonly statusChanges = output>(); get state(): WidgetInstanceEditorWizardState { @@ -82,25 +79,25 @@ export class ContactWidgetEditorComponent implements WidgetInstanceEditorWizard, // must emit initial state this.stateChange.emit(this.state); - const date: Date = new Date(this.config?.payload?.birthday); + const date: Date = new Date(this.config()?.payload?.birthday); this.personal.patchValue({ - firstName: this.config?.payload?.firstName ?? '', - lastName: this.config?.payload?.lastName ?? '', + firstName: this.config()?.payload?.firstName ?? '', + lastName: this.config()?.payload?.lastName ?? '', birthday: !isNaN(date.getTime()) ? date : '' }); this.company.patchValue({ - jobTitle: this.config?.payload?.jobTitle ?? '', - company: this.config?.payload?.company ?? '', - email: this.config?.payload?.email ?? '', - phone: this.config?.payload?.phone ?? '' + jobTitle: this.config()?.payload?.jobTitle ?? '', + company: this.config()?.payload?.company ?? '', + email: this.config()?.payload?.email ?? '', + phone: this.config()?.payload?.phone ?? '' }); this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => { - const config = this.config; + const config = this.config(); Object.assign(config.payload, value.personal); Object.assign(config.payload, value.company); - this.config = { ...config }; + this.config.set({ ...config }); if (!this.modified) { this.modified = true; this.statusChanges.emit({ modified: this.modified }); diff --git a/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.html b/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.html index de1f5b040..b803019f6 100644 --- a/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.html +++ b/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.html @@ -1,4 +1,4 @@ -@let payload = config.payload; +@let payload = config().payload;

{{ payload.firstName }} {{ payload.lastName }}

@if (payload.birthday) { diff --git a/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.ts b/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.ts index 2bbfc5d82..ce82e196e 100644 --- a/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.ts +++ b/projects/dashboards-demo/webcomponents/src/app/contact-widget/contact-widget.component.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: MIT */ import { DatePipe } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { WidgetConfig, WidgetInstance } from '@siemens/dashboards-ng'; @Component({ selector: 'app-contact-widget', @@ -12,6 +12,5 @@ import { WidgetConfig, WidgetInstance } from '@siemens/dashboards-ng'; styleUrl: './contact-widget.component.scss' }) export class ContactWidgetComponent implements WidgetInstance { - @Input({ required: true }) - readonly config!: WidgetConfig; + readonly config = input.required(); } diff --git a/projects/dashboards-demo/webcomponents/src/app/note-widget-editor/note-widget-editor.component.ts b/projects/dashboards-demo/webcomponents/src/app/note-widget-editor/note-widget-editor.component.ts index 94e63e2b9..5c0f5bfc9 100644 --- a/projects/dashboards-demo/webcomponents/src/app/note-widget-editor/note-widget-editor.component.ts +++ b/projects/dashboards-demo/webcomponents/src/app/note-widget-editor/note-widget-editor.component.ts @@ -2,7 +2,7 @@ * Copyright (c) Siemens 2016 - 2025 * SPDX-License-Identifier: MIT */ -import { Component, Input, OnInit, output } from '@angular/core'; +import { Component, model, OnInit, output } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { WidgetConfig, WidgetConfigStatus, WidgetInstanceEditor } from '@siemens/dashboards-ng'; @@ -12,20 +12,23 @@ import { WidgetConfig, WidgetConfigStatus, WidgetInstanceEditor } from '@siemens templateUrl: './note-widget-editor.component.html' }) export class NoteWidgetEditorComponent implements WidgetInstanceEditor, OnInit { - @Input() config!: WidgetConfig | Omit; + readonly config = model.required>(); + readonly statusChanges = output>(); protected heading = ''; protected message = ''; ngOnInit(): void { - this.heading = this.config?.heading ?? ''; - this.message = this.config?.payload?.message ?? ''; + this.heading = this.config()?.heading ?? ''; + this.message = this.config()?.payload?.message ?? ''; } onChange(): void { - this.config!.heading = this.heading ?? ''; - this.config!.payload.message = this.message ?? ''; - this.statusChanges.emit({ invalid: this.config!.heading.trim().length === 0, modified: true }); + const config = this.config(); + config!.heading = this.heading ?? ''; + config!.payload.message = this.message ?? ''; + this.statusChanges.emit({ invalid: config!.heading.trim().length === 0, modified: true }); + this.config.set(config); } } diff --git a/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.html b/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.html index 2de26030d..07508d26d 100644 --- a/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.html +++ b/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.html @@ -1,4 +1,4 @@ -

{{ config.payload?.message ?? '' }}

+

{{ config().payload?.message ?? '' }}

@if (editable) {

Click edit to configure this web-component widget.

} diff --git a/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.ts b/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.ts index f3feda1e7..565ddd376 100644 --- a/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.ts +++ b/projects/dashboards-demo/webcomponents/src/app/note-widget/note-widget.component.ts @@ -2,7 +2,7 @@ * Copyright (c) Siemens 2016 - 2025 * SPDX-License-Identifier: MIT */ -import { Component, EventEmitter, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, input, OnInit } from '@angular/core'; import { WidgetConfig, WidgetConfigEvent, WidgetInstance } from '@siemens/dashboards-ng'; import { ContentActionBarMainItem } from '@siemens/element-ng/content-action-bar'; import { MenuItem } from '@siemens/element-ng/menu'; @@ -12,8 +12,8 @@ import { MenuItem } from '@siemens/element-ng/menu'; templateUrl: './note-widget.component.html' }) export class NoteWidgetComponent implements WidgetInstance, OnInit { - @Input() config!: WidgetConfig; - @Input() editable = false; + readonly config = input.required(); + editable = false; primaryActions: ContentActionBarMainItem[] = [ { diff --git a/projects/dashboards-ng/src/components/flexible-dashboard/si-flexible-dashboard.component.spec.ts b/projects/dashboards-ng/src/components/flexible-dashboard/si-flexible-dashboard.component.spec.ts index 1a5bed0dd..547f25713 100644 --- a/projects/dashboards-ng/src/components/flexible-dashboard/si-flexible-dashboard.component.spec.ts +++ b/projects/dashboards-ng/src/components/flexible-dashboard/si-flexible-dashboard.component.spec.ts @@ -5,7 +5,6 @@ import { Component, input, - Input, model, NO_ERRORS_SCHEMA, OnInit, @@ -55,13 +54,13 @@ export class SiWidgetCatalogMockComponent extends SiWidgetCatalogComponent imple template: '' }) export class SiDashboardToolbarStubComponent { - @Input() primaryEditActions: MenuItem[] = []; - @Input() secondaryEditActions: MenuItem[] = []; - @Input() disableSaveButton = false; - @Input() disabled = false; - @Input() editable = false; - @Input() hideEditButton = false; - @Input() showEditButtonLabel = false; + readonly primaryEditActions = input([]); + readonly secondaryEditActions = input([]); + readonly disableSaveButton = input(false); + readonly disabled = input(false); + readonly editable = input(false); + readonly hideEditButton = input(false); + readonly showEditButtonLabel = input(false); } @Component({ diff --git a/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-editor-wrapper.component.ts b/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-editor-wrapper.component.ts index f52b29333..b55fb162f 100644 --- a/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-editor-wrapper.component.ts +++ b/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-editor-wrapper.component.ts @@ -55,7 +55,7 @@ export class SiWebComponentEditorWrapperComponent this.webComponent?.addEventListener('stateChange', this.webComponentStateChangeListener); this.webComponent?.addEventListener('statusChanges', this.webComponentStatusChangesListener); this.webComponent?.addEventListener('configChange', this.webComponentEventListener); - this.webComponentHost.nativeElement.appendChild(this.webComponent); + this.webComponentHost().nativeElement.appendChild(this.webComponent); } ngOnDestroy(): void { diff --git a/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper-base.component.ts b/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper-base.component.ts index 993d98843..49aa743c6 100644 --- a/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper-base.component.ts +++ b/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper-base.component.ts @@ -6,11 +6,13 @@ import { AfterViewInit, ElementRef, inject, - Input, Renderer2, - ViewChild, DOCUMENT, - Directive + Directive, + input, + viewChild, + SimpleChanges, + OnChanges } from '@angular/core'; import { WidgetConfig, WidgetInstance, WidgetInstanceEditor } from '../../model/widgets.model'; @@ -18,38 +20,42 @@ import { WidgetConfig, WidgetInstance, WidgetInstanceEditor } from '../../model/ @Directive() export abstract class SiWebComponentWrapperBaseComponent< T extends WidgetInstance | WidgetInstanceEditor -> implements AfterViewInit { - private _config!: WidgetConfig; - get config(): WidgetConfig { - return this._config; - } +> + implements AfterViewInit, OnChanges +{ + readonly config = + input.required< + T extends WidgetInstance ? WidgetConfig : WidgetConfig | Omit + >(); - @Input() set config(config: WidgetConfig) { - this._config = config; - if (this.webComponent) { - this.webComponent.config = config; - } - } + readonly elementTagName = input.required(); + readonly url = input.required(); + + protected readonly webComponentHost = viewChild.required('webComponentHost', { + read: ElementRef + }); - @Input() elementTagName!: string; - @Input() url!: string; - @ViewChild('webComponentHost', { static: true, read: ElementRef }) - protected webComponentHost!: ElementRef; protected webComponent?: HTMLElement & T; private renderer2 = inject(Renderer2); private document = inject(DOCUMENT); + ngOnChanges(changes: SimpleChanges): void { + if (changes.config && this.webComponent) { + this.webComponent.config = this.config(); + } + } + ngAfterViewInit(): void { - if (!this.isScriptLoaded(this.url)) { + if (!this.isScriptLoaded(this.url())) { const script = this.renderer2.createElement('script'); - script.src = this.url; + script.src = this.url(); this.renderer2.appendChild(this.document.body, script); } - this.webComponent = this.renderer2.createElement(this.elementTagName); + this.webComponent = this.renderer2.createElement(this.elementTagName()); if (this.webComponent) { - this.webComponent.config = this.config; + this.webComponent.config = this.config(); } } diff --git a/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper.component.ts b/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper.component.ts index e27073d78..ece246882 100644 --- a/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper.component.ts +++ b/projects/dashboards-ng/src/components/web-component-wrapper/si-web-component-wrapper.component.ts @@ -2,7 +2,7 @@ * Copyright (c) Siemens 2016 - 2025 * SPDX-License-Identifier: MIT */ -import { AfterViewInit, Component, EventEmitter, Input, OnDestroy } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, OnDestroy } from '@angular/core'; import { MenuItem as MenuItemLegacy } from '@siemens/element-ng/common'; import { ContentActionBarMainItem } from '@siemens/element-ng/content-action-bar'; import { MenuItem } from '@siemens/element-ng/menu'; @@ -23,7 +23,7 @@ export class SiWebComponentWrapperComponent return this._editable ?? false; } - @Input() set editable(editable: boolean) { + set editable(editable: boolean) { this._editable = editable; if (this.webComponent) { this.webComponent.editable = editable; @@ -43,7 +43,7 @@ export class SiWebComponentWrapperComponent override ngAfterViewInit(): void { super.ngAfterViewInit(); this.webComponent?.addEventListener('configChange', this.webComponentEventListener); - this.webComponentHost.nativeElement.appendChild(this.webComponent); + this.webComponentHost().nativeElement.appendChild(this.webComponent); } ngOnDestroy(): void { diff --git a/projects/dashboards-ng/src/widget-loader.ts b/projects/dashboards-ng/src/widget-loader.ts index c0b460031..5c584c2ba 100644 --- a/projects/dashboards-ng/src/widget-loader.ts +++ b/projects/dashboards-ng/src/widget-loader.ts @@ -198,8 +198,8 @@ const loadAndAttachWebComponentWrapper = ( } else { widgetInstanceRef = host.createComponent(SiWebComponentEditorWrapperComponent); } - widgetInstanceRef.instance.elementTagName = factory[componentName]; - widgetInstanceRef.instance.url = factory.url; + widgetInstanceRef.setInput('elementTagName', factory[componentName]); + widgetInstanceRef.setInput('url', factory.url); result.next(widgetInstanceRef); result.complete(); } else {