mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
AAE-29973 Dynamic component properties (#10540)
* [AAE-29973] updated dynamic component inputs and outputs * [AAE-29973] added check to prevent errors * [AAE-29973] added unit tests for input and output * [AAE-29973] fix for dynamic component with absolute position
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<mat-card-content [ngStyle]="{'position':'relative'}">
|
||||
<div #container></div>
|
||||
</mat-card-content>
|
||||
<mat-card-actions align="end">
|
||||
|
@@ -17,30 +17,46 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ScreenRenderingService } from '../../../services/public-api';
|
||||
import { TaskScreenCloudComponent } from './screen-cloud.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-test-component',
|
||||
template: `<div class="adf-cloud-test-container">test component</div>`,
|
||||
template: `
|
||||
<div class="adf-cloud-test-container">
|
||||
test component
|
||||
<div class="adf-cloud-test-container-taskId">{{ taskId }}</div>
|
||||
<button class="adf-cloud-test-container-complete-btn" (click)="onComplete()">complete</button>
|
||||
</div>
|
||||
`,
|
||||
imports: [CommonModule],
|
||||
standalone: true
|
||||
})
|
||||
class TestComponent {}
|
||||
class TestComponent {
|
||||
@Input() taskId = '';
|
||||
@Output() taskCompleted = new EventEmitter();
|
||||
onComplete() {
|
||||
this.taskCompleted.emit();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-test-actions-component',
|
||||
template: `<adf-cloud-task-screen [taskId]="'1'" [appName]="'app-name-test'" [screenId]="'test'">
|
||||
template: `
|
||||
<adf-cloud-task-screen [taskId]="'1'" [appName]="'app-name-test'" [screenId]="'test'" (taskCompleted)="onTaskCompleted()">
|
||||
<div buttons class="adf-cloud-test-buttons">
|
||||
<button>Test</button>
|
||||
</div>
|
||||
</adf-cloud-task-screen> `,
|
||||
</adf-cloud-task-screen>
|
||||
`,
|
||||
imports: [CommonModule, TaskScreenCloudComponent],
|
||||
standalone: true
|
||||
})
|
||||
class TestWrapperComponent {}
|
||||
class TestWrapperComponent {
|
||||
onTaskCompleted() {}
|
||||
}
|
||||
|
||||
describe('TaskScreenCloudComponent', () => {
|
||||
let fixture: ComponentFixture<TestWrapperComponent>;
|
||||
@@ -66,4 +82,19 @@ describe('TaskScreenCloudComponent', () => {
|
||||
const projectedContent = fixture.debugElement.query(By.css('.adf-cloud-test-buttons'));
|
||||
expect(projectedContent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set input property for dynamic component', () => {
|
||||
const inputValueFromDynamicComponent = fixture.debugElement.query(By.css('.adf-cloud-test-container-taskId'));
|
||||
expect((inputValueFromDynamicComponent.nativeElement as HTMLElement).textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('should subscribe to the output of dynamic component', () => {
|
||||
const onTaskCompletedSpy = spyOn(fixture.componentInstance, 'onTaskCompleted');
|
||||
const btnComplete = fixture.debugElement.query(By.css('.adf-cloud-test-container-complete-btn'));
|
||||
|
||||
expect(btnComplete).toBeDefined();
|
||||
|
||||
(btnComplete.nativeElement as HTMLButtonElement).click();
|
||||
expect(onTaskCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@@ -16,9 +16,11 @@
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
|
||||
import { Component, ComponentRef, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewChild, ViewContainerRef } from '@angular/core';
|
||||
import { ScreenRenderingService } from '../../../services/public-api';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { UserTaskCustomUi } from './screen-cloud.interface';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-task-screen',
|
||||
@@ -35,29 +37,74 @@ export class TaskScreenCloudComponent implements OnInit {
|
||||
/** Screen id to fetch corresponding screen widget. */
|
||||
@Input()
|
||||
screenId: string = '';
|
||||
@Input()
|
||||
processInstanceId: string = '';
|
||||
@Input()
|
||||
taskName: string = '';
|
||||
/** Toggle readonly state of the task. */
|
||||
@Input()
|
||||
readOnly = false;
|
||||
|
||||
/** Emitted when the task is saved. */
|
||||
@Output()
|
||||
taskSaved = new EventEmitter();
|
||||
|
||||
/** Emitted when the task is completed. */
|
||||
@Output()
|
||||
taskCompleted = new EventEmitter();
|
||||
|
||||
/** Emitted when there is an error. */
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
|
||||
@ViewChild('container', { read: ViewContainerRef, static: true })
|
||||
container: ViewContainerRef;
|
||||
componentRef: ComponentRef<any>;
|
||||
|
||||
private destroyRef = inject(DestroyRef);
|
||||
componentRef: ComponentRef<UserTaskCustomUi>;
|
||||
|
||||
private readonly screenRenderingService = inject(ScreenRenderingService);
|
||||
|
||||
ngOnInit() {
|
||||
this.createDynamicComponent();
|
||||
}
|
||||
|
||||
createDynamicComponent() {
|
||||
if (this.screenId) {
|
||||
const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId });
|
||||
this.componentRef = this.container.createComponent(componentType);
|
||||
if (this.taskId) {
|
||||
this.setInputsForDynamicComponent();
|
||||
this.subscribeToOutputs();
|
||||
}
|
||||
}
|
||||
|
||||
setInputsForDynamicComponent(): void {
|
||||
if (this.taskId && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'taskId')) {
|
||||
this.componentRef.setInput('taskId', this.taskId);
|
||||
}
|
||||
if (this.appName) {
|
||||
if (this.appName && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'appName')) {
|
||||
this.componentRef.setInput('appName', this.appName);
|
||||
}
|
||||
if (this.screenId) {
|
||||
if (this.screenId && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'screenId')) {
|
||||
this.componentRef.setInput('screenId', this.screenId);
|
||||
}
|
||||
if (this.processInstanceId && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'processInstanceId')) {
|
||||
this.componentRef.setInput('processInstanceId', this.processInstanceId);
|
||||
}
|
||||
if (this.taskName && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'taskName')) {
|
||||
this.componentRef.setInput('taskName', this.taskName);
|
||||
}
|
||||
}
|
||||
|
||||
subscribeToOutputs() {
|
||||
if (this.componentRef.instance?.taskSaved) {
|
||||
this.componentRef.instance.taskSaved.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.taskSaved.emit());
|
||||
}
|
||||
if (this.componentRef.instance?.taskCompleted) {
|
||||
this.componentRef.instance.taskCompleted.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.taskCompleted.emit());
|
||||
}
|
||||
if (this.componentRef.instance?.error) {
|
||||
this.componentRef.instance.error.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => this.error.emit(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from '@angular/core';
|
||||
|
||||
export interface UserTaskCustomUi {
|
||||
processInstanceId: string;
|
||||
taskName: string;
|
||||
appName: string;
|
||||
taskId: string;
|
||||
screenId: string;
|
||||
taskCompleted: EventEmitter<string>;
|
||||
taskSaved: EventEmitter<string>;
|
||||
error: EventEmitter<any>;
|
||||
}
|
@@ -29,6 +29,11 @@
|
||||
[appName]="appName"
|
||||
[screenId]="screenId"
|
||||
[taskId]="taskId"
|
||||
[taskName]="taskDetails.name"
|
||||
[processInstanceId]="taskDetails.processInstanceId"
|
||||
(error)="onError($event)"
|
||||
(taskCompleted)="onCompleteTask()"
|
||||
(taskSaved)="onFormSaved()"
|
||||
>
|
||||
<ng-template [ngTemplateOutlet]="taskFormCloudButtons" buttons>
|
||||
</ng-template>
|
||||
|
Reference in New Issue
Block a user