diff --git a/packages/angular-material/src/library/layouts/array-layout.renderer.ts b/packages/angular-material/src/library/layouts/array-layout.renderer.ts index 9f8850600..3106d8ab1 100644 --- a/packages/angular-material/src/library/layouts/array-layout.renderer.ts +++ b/packages/angular-material/src/library/layouts/array-layout.renderer.ts @@ -22,12 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { - ChangeDetectionStrategy, - Component, - OnDestroy, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { JsonFormsAngularService, JsonFormsAbstractControl, @@ -169,7 +164,7 @@ import { }) export class ArrayLayoutRenderer extends JsonFormsAbstractControl - implements OnInit, OnDestroy + implements OnInit { noData: boolean; translations: ArrayTranslations = {}; diff --git a/packages/angular-material/src/library/layouts/categorization-layout.renderer.ts b/packages/angular-material/src/library/layouts/categorization-layout.renderer.ts index 0c5ebfd7f..65ea94f77 100644 --- a/packages/angular-material/src/library/layouts/categorization-layout.renderer.ts +++ b/packages/angular-material/src/library/layouts/categorization-layout.renderer.ts @@ -38,12 +38,11 @@ import { rankWith, uiTypeIs, } from '@jsonforms/core'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { JsonFormsAngularService, JsonFormsBaseRenderer, } from '@jsonforms/angular'; -import { Subscription } from 'rxjs'; @Component({ selector: 'jsonforms-categorization-layout', @@ -69,11 +68,10 @@ import { Subscription } from 'rxjs'; }) export class CategorizationTabLayoutRenderer extends JsonFormsBaseRenderer - implements OnInit, OnDestroy + implements OnInit { hidden: boolean; visibleCategories: (Category | Categorization)[]; - private subscription: Subscription; categoryLabels: string[]; constructor(private jsonFormsService: JsonFormsAngularService) { @@ -81,29 +79,25 @@ export class CategorizationTabLayoutRenderer } ngOnInit() { - this.subscription = this.jsonFormsService.$state.subscribe({ - next: (state: JsonFormsState) => { - const props = mapStateToLayoutProps(state, this.getOwnProps()); - this.hidden = !props.visible; - this.visibleCategories = this.uischema.elements.filter( - (category: Category | Categorization) => - isVisible(category, props.data, undefined, getAjv(state)) - ); - this.categoryLabels = this.visibleCategories.map((element) => - deriveLabelForUISchemaElement( - element as Labelable, - state.jsonforms.i18n?.translate ?? - defaultJsonFormsI18nState.translate - ) - ); - }, - }); - } - - ngOnDestroy() { - if (this.subscription) { - this.subscription.unsubscribe(); - } + this.addSubscription( + this.jsonFormsService.$state.subscribe({ + next: (state: JsonFormsState) => { + const props = mapStateToLayoutProps(state, this.getOwnProps()); + this.hidden = !props.visible; + this.visibleCategories = this.uischema.elements.filter( + (category: Category | Categorization) => + isVisible(category, props.data, undefined, getAjv(state)) + ); + this.categoryLabels = this.visibleCategories.map((element) => + deriveLabelForUISchemaElement( + element as Labelable, + state.jsonforms.i18n?.translate ?? + defaultJsonFormsI18nState.translate + ) + ); + }, + }) + ); } } diff --git a/packages/angular-material/src/library/layouts/layout.renderer.ts b/packages/angular-material/src/library/layouts/layout.renderer.ts index 35b795814..109b1a269 100644 --- a/packages/angular-material/src/library/layouts/layout.renderer.ts +++ b/packages/angular-material/src/library/layouts/layout.renderer.ts @@ -23,7 +23,6 @@ THE SOFTWARE. */ import { - OnDestroy, OnInit, ChangeDetectorRef, Component, @@ -42,18 +41,16 @@ import { UISchemaElement, JsonSchema, } from '@jsonforms/core'; -import type { Subscription } from 'rxjs'; @Component({ template: '', }) export class LayoutRenderer extends JsonFormsBaseRenderer - implements OnInit, OnDestroy + implements OnInit { hidden: boolean; label: string | undefined; - private subscription: Subscription; constructor( private jsonFormsService: JsonFormsAngularService, @@ -63,20 +60,16 @@ export class LayoutRenderer } ngOnInit() { - this.subscription = this.jsonFormsService.$state.subscribe({ - next: (state: JsonFormsState) => { - const props = mapStateToLayoutProps(state, this.getOwnProps()); - this.label = props.label; - this.hidden = !props.visible; - this.changeDetectionRef.markForCheck(); - }, - }); - } - - ngOnDestroy() { - if (this.subscription) { - this.subscription.unsubscribe(); - } + this.addSubscription( + this.jsonFormsService.$state.subscribe({ + next: (state: JsonFormsState) => { + const props = mapStateToLayoutProps(state, this.getOwnProps()); + this.label = props.label; + this.hidden = !props.visible; + this.changeDetectionRef.markForCheck(); + }, + }) + ); } trackElement(_index: number, renderProp: OwnPropsOfRenderer): string { diff --git a/packages/angular-material/src/library/other/label.renderer.ts b/packages/angular-material/src/library/other/label.renderer.ts index ebaedc329..3a049ec37 100644 --- a/packages/angular-material/src/library/other/label.renderer.ts +++ b/packages/angular-material/src/library/other/label.renderer.ts @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { JsonFormsAngularService, JsonFormsBaseRenderer, @@ -36,7 +36,6 @@ import { rankWith, uiTypeIs, } from '@jsonforms/core'; -import { Subscription } from 'rxjs'; @Component({ selector: 'LabelRenderer', @@ -51,33 +50,27 @@ import { Subscription } from 'rxjs'; }) export class LabelRenderer extends JsonFormsBaseRenderer - implements OnDestroy, OnInit + implements OnInit { label: string; visible: boolean; - private subscription: Subscription; - constructor(private jsonFormsService: JsonFormsAngularService) { super(); } ngOnInit() { - this.subscription = this.jsonFormsService.$state.subscribe({ - next: (state: JsonFormsState) => { - const props = mapStateToLabelProps( - state, - this.getOwnProps() as OwnPropsOfLabel - ); - this.visible = props.visible; - this.label = props.text; - }, - }); - } - - ngOnDestroy() { - if (this.subscription) { - this.subscription.unsubscribe(); - } + this.addSubscription( + this.jsonFormsService.$state.subscribe({ + next: (state: JsonFormsState) => { + const props = mapStateToLabelProps( + state, + this.getOwnProps() as OwnPropsOfLabel + ); + this.visible = props.visible; + this.label = props.text; + }, + }) + ); } } diff --git a/packages/angular/src/library/abstract-control.ts b/packages/angular/src/library/abstract-control.ts index 54105e51a..af43608ce 100644 --- a/packages/angular/src/library/abstract-control.ts +++ b/packages/angular/src/library/abstract-control.ts @@ -39,7 +39,6 @@ import { ValidationErrors, ValidatorFn, } from '@angular/forms'; -import type { Subscription } from 'rxjs'; import { JsonFormsBaseRenderer } from './base.renderer'; import { JsonFormsAngularService } from './jsonforms.service'; @@ -58,7 +57,6 @@ export abstract class JsonFormsAbstractControl< @Input() visible: boolean; form: FormControl; - subscription: Subscription; data: any; label: string; description: string; @@ -99,41 +97,45 @@ export abstract class JsonFormsAbstractControl< } ngOnInit() { - this.jsonFormsService.$state.subscribe({ - next: (state: JsonFormsState) => { - const props = this.mapToProps(state); - const { - data, - enabled, - errors, - label, - required, - schema, - rootSchema, - visible, - path, - config, - } = props; - this.label = computeLabel( - label, - required, - config ? config.hideRequiredAsterisk : false - ); - this.data = data; - this.error = errors; - this.enabled = enabled; - this.isEnabled() ? this.form.enable() : this.form.disable(); - this.hidden = !visible; - this.scopedSchema = schema; - this.rootSchema = rootSchema; - this.description = - this.scopedSchema !== undefined ? this.scopedSchema.description : ''; - this.id = props.id; - this.form.setValue(data); - this.propsPath = path; - this.mapAdditionalProps(props); - }, - }); + this.addSubscription( + this.jsonFormsService.$state.subscribe({ + next: (state: JsonFormsState) => { + const props = this.mapToProps(state); + const { + data, + enabled, + errors, + label, + required, + schema, + rootSchema, + visible, + path, + config, + } = props; + this.label = computeLabel( + label, + required, + config ? config.hideRequiredAsterisk : false + ); + this.data = data; + this.error = errors; + this.enabled = enabled; + this.isEnabled() ? this.form.enable() : this.form.disable(); + this.hidden = !visible; + this.scopedSchema = schema; + this.rootSchema = rootSchema; + this.description = + this.scopedSchema !== undefined + ? this.scopedSchema.description + : ''; + this.id = props.id; + this.form.setValue(data); + this.propsPath = path; + this.mapAdditionalProps(props); + }, + }) + ); this.triggerValidation(); } @@ -146,9 +148,7 @@ export abstract class JsonFormsAbstractControl< } ngOnDestroy() { - if (this.subscription) { - this.subscription.unsubscribe(); - } + super.ngOnDestroy(); removeId(this.id); } diff --git a/packages/angular/src/library/base.renderer.ts b/packages/angular/src/library/base.renderer.ts index 44ff40172..5b283ed03 100644 --- a/packages/angular/src/library/base.renderer.ts +++ b/packages/angular/src/library/base.renderer.ts @@ -22,18 +22,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Directive, Input } from '@angular/core'; +import { Directive, Input, OnDestroy } from '@angular/core'; import { JsonSchema, OwnPropsOfRenderer, UISchemaElement, } from '@jsonforms/core'; +import { Subscription } from 'rxjs'; @Directive() -export class JsonFormsBaseRenderer { +export class JsonFormsBaseRenderer + implements OnDestroy +{ @Input() uischema: T; @Input() schema: JsonSchema; @Input() path: string; + protected subscriptions: Subscription = new Subscription(); + + protected addSubscription(subscription: Subscription | Subscription[]) { + if (Array.isArray(subscription)) { + subscription.forEach((sub) => this.subscriptions.add(sub)); + } else { + this.subscriptions.add(subscription); + } + } protected getOwnProps(): OwnPropsOfRenderer { return { @@ -42,4 +54,8 @@ export class JsonFormsBaseRenderer { path: this.path, }; } + + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); + } } diff --git a/packages/angular/src/library/jsonforms-root.component.ts b/packages/angular/src/library/jsonforms-root.component.ts index 89c2ed133..1150a7543 100644 --- a/packages/angular/src/library/jsonforms-root.component.ts +++ b/packages/angular/src/library/jsonforms-root.component.ts @@ -28,6 +28,7 @@ import { EventEmitter, Input, OnChanges, + OnDestroy, OnInit, Output, SimpleChanges, @@ -45,6 +46,7 @@ import { } from '@jsonforms/core'; import Ajv, { ErrorObject } from 'ajv'; import { JsonFormsAngularService, USE_STATE_VALUE } from './jsonforms.service'; +import { Subscription } from 'rxjs'; // TODO Can this be rewritten to not use DoCheck and OnChanges? /* eslint-disable @angular-eslint/no-conflicting-lifecycle */ @@ -53,7 +55,7 @@ import { JsonFormsAngularService, USE_STATE_VALUE } from './jsonforms.service'; template: '', providers: [JsonFormsAngularService], }) -export class JsonForms implements DoCheck, OnChanges, OnInit { +export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { @Input() uischema: UISchemaElement; @Input() schema: JsonSchema; @Input() data: any; @@ -71,6 +73,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit { private previousData: any; private previousErrors: ErrorObject[]; + subscription: Subscription; private initialized = false; oldI18N: JsonFormsI18nState; @@ -96,7 +99,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit { }, this.middleware ); - this.jsonformsService.$state.subscribe((state) => { + this.subscription = this.jsonformsService.$state.subscribe((state) => { const data = state?.jsonforms?.core?.data; const errors = state?.jsonforms?.core?.errors; if (this.previousData !== data) { @@ -199,4 +202,8 @@ export class JsonForms implements DoCheck, OnChanges, OnInit { ); } } + + ngOnDestroy(): void { + this.subscription.unsubscribe(); + } } diff --git a/packages/angular/src/library/jsonforms.component.ts b/packages/angular/src/library/jsonforms.component.ts index 2ac3014de..22a93264b 100644 --- a/packages/angular/src/library/jsonforms.component.ts +++ b/packages/angular/src/library/jsonforms.component.ts @@ -27,7 +27,6 @@ import { ComponentFactoryResolver, Directive, Input, - OnDestroy, OnInit, Type, ViewContainerRef, @@ -46,7 +45,6 @@ import { } from '@jsonforms/core'; import { UnknownRenderer } from './unknown.component'; import { JsonFormsBaseRenderer } from './base.renderer'; -import type { Subscription } from 'rxjs'; import { JsonFormsControl } from './control'; import { JsonFormsAngularService } from './jsonforms.service'; @@ -71,9 +69,8 @@ const areEqual = ( }) export class JsonFormsOutlet extends JsonFormsBaseRenderer - implements OnInit, OnDestroy + implements OnInit { - private subscription: Subscription; private previousProps: StatePropsOfJsonFormsRenderer; constructor( @@ -93,9 +90,11 @@ export class JsonFormsOutlet } ngOnInit(): void { - this.subscription = this.jsonformsService.$state.subscribe({ - next: (state: JsonFormsState) => this.update(state), - }); + this.addSubscription( + this.jsonformsService.$state.subscribe({ + next: (state: JsonFormsState) => this.update(state), + }) + ); } update(state: JsonFormsState) { @@ -151,10 +150,4 @@ export class JsonFormsOutlet } } } - - ngOnDestroy() { - if (this.subscription) { - this.subscription.unsubscribe(); - } - } }