101577: Fixed ClaimedTaskActionsLoaderComponent not updating its @rendersWorkflowTaskOption components ngOnChanges

This commit is contained in:
Alexandre Vryghem
2023-05-17 17:57:00 +01:00
parent 2ca91fd331
commit 82a8da6028

View File

@@ -3,18 +3,19 @@ import {
ComponentFactoryResolver, ComponentFactoryResolver,
EventEmitter, EventEmitter,
Input, Input,
OnDestroy,
OnInit, OnInit,
Output, Output,
ViewChild ViewChild,
OnChanges,
SimpleChanges,
ComponentRef,
} from '@angular/core'; } from '@angular/core';
import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator'; import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator';
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive'; import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive';
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; import { hasValue, isNotEmpty, hasNoValue } from '../../../empty.util';
import { hasValue } from '../../../empty.util';
import { Subscription } from 'rxjs';
import { MyDSpaceActionsResult } from '../../mydspace-actions'; import { MyDSpaceActionsResult } from '../../mydspace-actions';
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
@Component({ @Component({
selector: 'ds-claimed-task-actions-loader', selector: 'ds-claimed-task-actions-loader',
@@ -24,7 +25,7 @@ import { MyDSpaceActionsResult } from '../../mydspace-actions';
* Component for loading a ClaimedTaskAction component depending on the "option" input * Component for loading a ClaimedTaskAction component depending on the "option" input
* Passes on the ClaimedTask to the component and subscribes to the processCompleted output * Passes on the ClaimedTask to the component and subscribes to the processCompleted output
*/ */
export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges {
/** /**
* The ClaimedTask object * The ClaimedTask object
*/ */
@@ -47,10 +48,18 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
@ViewChild(ClaimedTaskActionsDirective, {static: true}) claimedTaskActionsDirective: ClaimedTaskActionsDirective; @ViewChild(ClaimedTaskActionsDirective, {static: true}) claimedTaskActionsDirective: ClaimedTaskActionsDirective;
/** /**
* Array to track all subscriptions and unsubscribe them onDestroy * The reference to the dynamic component
* @type {Array}
*/ */
protected subs: Subscription[] = []; protected compRef: ComponentRef<Component>;
/**
* The list of input and output names for the dynamic component
*/
protected inAndOutputNames: (keyof ClaimedTaskActionsAbstractComponent & keyof this)[] = [
'object',
'option',
'processCompleted',
];
constructor(private componentFactoryResolver: ComponentFactoryResolver) { constructor(private componentFactoryResolver: ComponentFactoryResolver) {
} }
@@ -59,7 +68,29 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
* Fetch, create and initialize the relevant component * Fetch, create and initialize the relevant component
*/ */
ngOnInit(): void { ngOnInit(): void {
this.instantiateComponent();
}
/**
* Whenever the inputs change, update the inputs of the dynamic component
*/
ngOnChanges(changes: SimpleChanges): void {
if (hasNoValue(this.compRef)) {
// sometimes the component has not been initialized yet, so it first needs to be initialized
// before being called again
this.instantiateComponent(changes);
} else {
// if an input or output has changed
if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) {
this.connectInputsAndOutputs();
if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) {
(this.compRef.instance as any).ngOnChanges(changes);
}
}
}
}
private instantiateComponent(changes?: SimpleChanges): void {
const comp = this.getComponentByWorkflowTaskOption(this.option); const comp = this.getComponentByWorkflowTaskOption(this.option);
if (hasValue(comp)) { if (hasValue(comp)) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp);
@@ -67,11 +98,12 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory); this.compRef = viewContainerRef.createComponent(componentFactory);
const componentInstance = (componentRef.instance as ClaimedTaskActionsAbstractComponent);
componentInstance.object = this.object; if (hasValue(changes)) {
if (hasValue(componentInstance.processCompleted)) { this.ngOnChanges(changes);
this.subs.push(componentInstance.processCompleted.subscribe((result) => this.processCompleted.emit(result))); } else {
this.connectInputsAndOutputs();
} }
} }
} }
@@ -81,11 +113,14 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy {
} }
/** /**
* Unsubscribe from open subscriptions * Connect the in and outputs of this component to the dynamic component,
* to ensure they're in sync
*/ */
ngOnDestroy(): void { protected connectInputsAndOutputs(): void {
this.subs if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) {
.filter((subscription) => hasValue(subscription)) this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => {
.forEach((subscription) => subscription.unsubscribe()); this.compRef.instance[name] = this[name];
});
}
} }
} }