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>
|
||||||
<mat-card-content>
|
<mat-card-content [ngStyle]="{'position':'relative'}">
|
||||||
<div #container></div>
|
<div #container></div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions align="end">
|
<mat-card-actions align="end">
|
||||||
|
@@ -17,30 +17,46 @@
|
|||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { CommonModule } from '@angular/common';
|
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 { By } from '@angular/platform-browser';
|
||||||
import { ScreenRenderingService } from '../../../services/public-api';
|
import { ScreenRenderingService } from '../../../services/public-api';
|
||||||
import { TaskScreenCloudComponent } from './screen-cloud.component';
|
import { TaskScreenCloudComponent } from './screen-cloud.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-cloud-test-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],
|
imports: [CommonModule],
|
||||||
standalone: true
|
standalone: true
|
||||||
})
|
})
|
||||||
class TestComponent {}
|
class TestComponent {
|
||||||
|
@Input() taskId = '';
|
||||||
|
@Output() taskCompleted = new EventEmitter();
|
||||||
|
onComplete() {
|
||||||
|
this.taskCompleted.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-cloud-test-actions-component',
|
selector: 'adf-cloud-test-actions-component',
|
||||||
template: `<adf-cloud-task-screen [taskId]="'1'" [appName]="'app-name-test'" [screenId]="'test'">
|
template: `
|
||||||
<div buttons class="adf-cloud-test-buttons">
|
<adf-cloud-task-screen [taskId]="'1'" [appName]="'app-name-test'" [screenId]="'test'" (taskCompleted)="onTaskCompleted()">
|
||||||
<button>Test</button>
|
<div buttons class="adf-cloud-test-buttons">
|
||||||
</div>
|
<button>Test</button>
|
||||||
</adf-cloud-task-screen> `,
|
</div>
|
||||||
|
</adf-cloud-task-screen>
|
||||||
|
`,
|
||||||
imports: [CommonModule, TaskScreenCloudComponent],
|
imports: [CommonModule, TaskScreenCloudComponent],
|
||||||
standalone: true
|
standalone: true
|
||||||
})
|
})
|
||||||
class TestWrapperComponent {}
|
class TestWrapperComponent {
|
||||||
|
onTaskCompleted() {}
|
||||||
|
}
|
||||||
|
|
||||||
describe('TaskScreenCloudComponent', () => {
|
describe('TaskScreenCloudComponent', () => {
|
||||||
let fixture: ComponentFixture<TestWrapperComponent>;
|
let fixture: ComponentFixture<TestWrapperComponent>;
|
||||||
@@ -66,4 +82,19 @@ describe('TaskScreenCloudComponent', () => {
|
|||||||
const projectedContent = fixture.debugElement.query(By.css('.adf-cloud-test-buttons'));
|
const projectedContent = fixture.debugElement.query(By.css('.adf-cloud-test-buttons'));
|
||||||
expect(projectedContent).toBeTruthy();
|
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 { 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 { ScreenRenderingService } from '../../../services/public-api';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { UserTaskCustomUi } from './screen-cloud.interface';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-cloud-task-screen',
|
selector: 'adf-cloud-task-screen',
|
||||||
@@ -35,29 +37,74 @@ export class TaskScreenCloudComponent implements OnInit {
|
|||||||
/** Screen id to fetch corresponding screen widget. */
|
/** Screen id to fetch corresponding screen widget. */
|
||||||
@Input()
|
@Input()
|
||||||
screenId: string = '';
|
screenId: string = '';
|
||||||
|
@Input()
|
||||||
|
processInstanceId: string = '';
|
||||||
|
@Input()
|
||||||
|
taskName: string = '';
|
||||||
/** Toggle readonly state of the task. */
|
/** Toggle readonly state of the task. */
|
||||||
@Input()
|
@Input()
|
||||||
readOnly = false;
|
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 })
|
@ViewChild('container', { read: ViewContainerRef, static: true })
|
||||||
container: ViewContainerRef;
|
container: ViewContainerRef;
|
||||||
componentRef: ComponentRef<any>;
|
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
componentRef: ComponentRef<UserTaskCustomUi>;
|
||||||
|
|
||||||
private readonly screenRenderingService = inject(ScreenRenderingService);
|
private readonly screenRenderingService = inject(ScreenRenderingService);
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.createDynamicComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
createDynamicComponent() {
|
||||||
if (this.screenId) {
|
if (this.screenId) {
|
||||||
const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId });
|
const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId });
|
||||||
this.componentRef = this.container.createComponent(componentType);
|
this.componentRef = this.container.createComponent(componentType);
|
||||||
if (this.taskId) {
|
this.setInputsForDynamicComponent();
|
||||||
this.componentRef.setInput('taskId', this.taskId);
|
this.subscribeToOutputs();
|
||||||
}
|
}
|
||||||
if (this.appName) {
|
}
|
||||||
this.componentRef.setInput('appName', this.appName);
|
|
||||||
}
|
setInputsForDynamicComponent(): void {
|
||||||
if (this.screenId) {
|
if (this.taskId && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'taskId')) {
|
||||||
this.componentRef.setInput('screenId', this.screenId);
|
this.componentRef.setInput('taskId', this.taskId);
|
||||||
}
|
}
|
||||||
|
if (this.appName && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'appName')) {
|
||||||
|
this.componentRef.setInput('appName', this.appName);
|
||||||
|
}
|
||||||
|
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"
|
[appName]="appName"
|
||||||
[screenId]="screenId"
|
[screenId]="screenId"
|
||||||
[taskId]="taskId"
|
[taskId]="taskId"
|
||||||
|
[taskName]="taskDetails.name"
|
||||||
|
[processInstanceId]="taskDetails.processInstanceId"
|
||||||
|
(error)="onError($event)"
|
||||||
|
(taskCompleted)="onCompleteTask()"
|
||||||
|
(taskSaved)="onFormSaved()"
|
||||||
>
|
>
|
||||||
<ng-template [ngTemplateOutlet]="taskFormCloudButtons" buttons>
|
<ng-template [ngTemplateOutlet]="taskFormCloudButtons" buttons>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
Reference in New Issue
Block a user