From 892172150e0a918e740f7e398b4f137abc2ab5a9 Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Thu, 30 May 2019 20:13:41 +0300 Subject: [PATCH] [ADF-4586] - fix preselecting value for DropdownCloud (#4747) * [ADF-4586] - fix preselecting value for DropdownCloud * ADF-4586] - fix form complete for dropdown and tests * [ADF-4586] - add FormRenderingService as provider * [ADF-4586] - fix memory leaks * [ADF-4586] - add type information * [ADF-4586] - fix memory leak on dropdown * [ADF-4589] - PR changes * [ADF-4586] - fix unit tests * [ADF-4586] - lint --- .../dropdown-cloud/dropdown-cloud.widget.html | 1 + .../dropdown-cloud.widget.spec.ts | 48 +++++++++++++++++-- .../dropdown-cloud/dropdown-cloud.widget.ts | 33 +++++++++++-- .../components/form-cloud.component.spec.ts | 8 +++- .../form/components/form-cloud.component.ts | 37 +++++++++++--- .../src/lib/form/form-cloud.module.ts | 5 +- 6 files changed, 114 insertions(+), 18 deletions(-) diff --git a/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.html b/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.html index fa2d9eb593..1f896ae790 100644 --- a/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.html +++ b/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.html @@ -6,6 +6,7 @@ [id]="field.id" [(ngModel)]="field.value" [disabled]="field.readOnly" + [compareWith]="compareDropdownValues" (ngModelChange)="onFieldChanged(field)"> { }); })); - it('shoud map properties if restResponsePath is set', async(() => { + it('shoud map properties if restResponsePath is set', (done) => { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), { id: 'dropdown-id', name: 'date-name', @@ -175,7 +175,8 @@ describe('DropdownCloudWidgetComponent', () => { id: 1, path: { name: 'test1' - } + }, + name: '' }])); spyOn(widget, 'mapJsonData').and.returnValue([]); @@ -183,8 +184,49 @@ describe('DropdownCloudWidgetComponent', () => { fixture.detectChanges(); fixture.whenStable().then(() => { expect(widget.mapJsonData).toHaveBeenCalled(); + done(); }); - })); + }); + + it('shoud preselect dropdown widget value set as id ', (done) => { + widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), { + id: 'dropdown-id', + name: 'date-name', + type: 'dropdown-cloud', + readOnly: 'false', + restUrl: 'fake-rest-url', + optionType: 'rest', + value: { + id: 'opt1', + name: 'defaul_value' + } + }); + + const dropdownSpy = spyOn(formCloudService, 'getDropDownJsonData').and.returnValue(of( [ + { + id: 'opt1', + name: 'default1_value' + }, + { + id: 2, + name: 'default2_value' + } + ])); + + widget.ngOnInit(); + fixture.detectChanges(); + + const dropDownElement: any = element.querySelector('#dropdown-id'); + dropDownElement.click(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const optOne: any = fixture.debugElement.queryAll(By.css('[id="opt1"]')); + expect(dropdownSpy).toHaveBeenCalled(); + expect(optOne[0].nativeElement.className).toBe('mat-option ng-star-inserted mat-active'); + done(); + }); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.ts index 7058a13ee2..c7f26635d1 100644 --- a/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/dropdown-cloud/dropdown-cloud.widget.ts @@ -15,9 +15,11 @@ * limitations under the License. */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core'; import { baseHost, WidgetComponent, FormService, LogService, FormFieldOption } from '@alfresco/adf-core'; import { FormCloudService } from '../../services/form-cloud.service'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; /* tslint:disable:component-selector */ @@ -28,7 +30,9 @@ import { FormCloudService } from '../../services/form-cloud.service'; host: baseHost, encapsulation: ViewEncapsulation.None }) -export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit { +export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { + + protected onDestroy$ = new Subject(); constructor(public formService: FormService, private formCloudService: FormCloudService, @@ -44,11 +48,13 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI getValuesFromRestApi() { if (this.isValidRestType()) { - this.formCloudService.getDropDownJsonData(this.field.restUrl).subscribe( (result: FormFieldOption[]) => { + this.formCloudService.getDropDownJsonData(this.field.restUrl) + .pipe(takeUntil(this.onDestroy$)) + .subscribe( (result: FormFieldOption[]) => { if (this.field.restResponsePath) { this.field.options = this.mapJsonData(result); } else { - this.field.options = result; + this.setOptionValues(result); } }, (err) => this.handleError(err)); @@ -67,7 +73,20 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI }); } - getOptionValue(option: FormFieldOption, fieldValue: string): string { + private setOptionValues(result: FormFieldOption[] ) { + this.field.options = result.map( (value: FormFieldOption) => { + return { + id: value.id, + name: value.name + }; + }); + } + + compareDropdownValues(opt1: string, opt2: FormFieldOption): boolean { + return opt1 && opt2 && ( opt1 === opt2.id || opt1 === opt2.name); + } + + getOptionValue(option: FormFieldOption, fieldValue: string): string { let optionValue: string = ''; if (option.id === 'empty' || option.name !== fieldValue) { optionValue = option.id; @@ -89,4 +108,8 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI return this.field.type === 'readonly' ? true : false; } + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } } diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts index 5fd6e8079c..a7e5edb31a 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.spec.ts @@ -20,7 +20,7 @@ import { By } from '@angular/platform-browser'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Observable, of, throwError } from 'rxjs'; import { FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService, - setupTestBed, AppConfigService } from '@alfresco/adf-core'; + setupTestBed, AppConfigService, FormRenderingService } from '@alfresco/adf-core'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module'; import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudComponent } from './form-cloud.component'; @@ -34,14 +34,18 @@ describe('FormCloudComponent', () => { let formComponent: FormCloudComponent; let visibilityService: WidgetVisibilityService; let logService: LogService; + let formRenderingService: FormRenderingService; beforeEach(() => { logService = new LogService(null); + formRenderingService = TestBed.get(FormRenderingService); visibilityService = new WidgetVisibilityService(null, logService); spyOn(visibilityService, 'refreshVisibility').and.stub(); + spyOn(formRenderingService, 'setComponentTypeResolver').and.returnValue(true); formCloudService = new FormCloudService(null, new AppConfigService(null), logService); formService = new FormService(null, null, logService); - formComponent = new FormCloudComponent(formCloudService, formService, null, visibilityService); + formComponent = new FormCloudComponent(formCloudService, formService, null, formRenderingService, visibilityService); + }); it('should check form', () => { diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts index 69e61f9f30..7c0b07abc2 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts @@ -17,21 +17,30 @@ import { Component, EventEmitter, Input, OnChanges, - Output, SimpleChanges + Output, SimpleChanges, OnDestroy } from '@angular/core'; -import { Observable, of, forkJoin } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { Observable, of, forkJoin, Subject } from 'rxjs'; +import { switchMap, takeUntil } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -import { FormBaseComponent, FormFieldModel, FormOutcomeEvent, FormOutcomeModel, WidgetVisibilityService, FormService, NotificationService } from '@alfresco/adf-core'; +import { FormBaseComponent, + FormFieldModel, + FormOutcomeEvent, + FormOutcomeModel, + WidgetVisibilityService, + FormService, + NotificationService, + FormRenderingService } from '@alfresco/adf-core'; import { FormCloudService } from '../services/form-cloud.service'; import { FormCloud } from '../models/form-cloud.model'; import { TaskVariableCloud } from '../models/task-variable-cloud.model'; +import { DropdownCloudWidgetComponent } from './dropdown-cloud/dropdown-cloud.widget'; +import { UploadCloudWidgetComponent } from './upload-cloud.widget'; @Component({ selector: 'adf-cloud-form', templateUrl: './form-cloud.component.html' }) -export class FormCloudComponent extends FormBaseComponent implements OnChanges { +export class FormCloudComponent extends FormBaseComponent implements OnChanges, OnDestroy { /** App id to fetch corresponding form and values. */ @Input() @@ -75,15 +84,22 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges { protected subscriptions: Subscription[] = []; nodeId: string; + protected onDestroy$ = new Subject(); + constructor(protected formCloudService: FormCloudService, protected formService: FormService, private notificationService: NotificationService, + private formRenderingService: FormRenderingService, protected visibilityService: WidgetVisibilityService) { super(); - this.formService.formContentClicked.subscribe((content: any) => { + this.formService.formContentClicked + .pipe(takeUntil(this.onDestroy$)) + .subscribe((content: any) => { this.formContentClicked.emit(content); }); + this.formRenderingService.setComponentTypeResolver('upload', () => UploadCloudWidgetComponent, true); + this.formRenderingService.setComponentTypeResolver('dropdown', () => DropdownCloudWidgetComponent, true); } ngOnChanges(changes: SimpleChanges) { @@ -152,6 +168,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges { return new Promise((resolve, reject) => { forkJoin(this.formCloudService.getTaskForm(appName, taskId), this.formCloudService.getTaskVariables(appName, taskId)) + .pipe(takeUntil(this.onDestroy$)) .subscribe( (data) => { this.data = data[1]; @@ -174,6 +191,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges { getFormById(appName: string, formId: string) { this.formCloudService .getForm(appName, formId) + .pipe(takeUntil(this.onDestroy$)) .subscribe( (form) => { const parsedForm = this.parseForm(form); @@ -220,6 +238,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges { if (this.form && this.appName && this.taskId) { this.formCloudService .saveTaskForm(this.appName, this.taskId, this.form.id, this.form.values) + .pipe(takeUntil(this.onDestroy$)) .subscribe( () => { this.onTaskSaved(this.form); @@ -233,6 +252,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges { if (this.form && this.appName && this.taskId) { this.formCloudService .completeTaskForm(this.appName, this.taskId, this.form.id, this.form.values, outcome) + .pipe(takeUntil(this.onDestroy$)) .subscribe( () => { this.onTaskCompleted(this.form); @@ -319,4 +339,9 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges { protected storeFormAsMetadata() { } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } } diff --git a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts index 7d843b6485..a4031eb493 100644 --- a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts @@ -18,7 +18,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; -import { TemplateModule, FormBaseModule, PipeModule, CoreModule } from '@alfresco/adf-core'; +import { TemplateModule, FormBaseModule, PipeModule, CoreModule, FormRenderingService } from '@alfresco/adf-core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { UploadCloudWidgetComponent } from './components/upload-cloud.widget'; import { MaterialModule } from '../material.module'; @@ -42,7 +42,8 @@ import { DropdownCloudWidgetComponent } from './components/dropdown-cloud/dropdo ], declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent, FormCustomOutcomesComponent, DropdownCloudWidgetComponent], providers: [ - FormDefinitionSelectorCloudService + FormDefinitionSelectorCloudService, + FormRenderingService ], entryComponents: [ UploadCloudWidgetComponent,